blob: c66b5f60773a0d14a3c526ba7fe7b3173fbf4b8b [file] [log] [blame]
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Public Key Sign wrapper."""
from typing import Type
from absl import logging
from tink.proto import tink_pb2
from tink import core
from tink.signature import _public_key_sign
from tink.signature import _public_key_verify
class _WrappedPublicKeySign(_public_key_sign.PublicKeySign):
"""Implements PublicKeySign for a set of PublicKeySign primitives."""
def __init__(self, primitives_set: core.PrimitiveSet):
self._primitive_set = primitives_set
def sign(self, data: bytes) -> bytes:
"""Computes the signature for data using the primary primitive.
Args:
data: The input data.
Returns:
The signature.
"""
primary = self._primitive_set.primary()
if not primary:
raise core.TinkError('primary primitive not set')
sign_data = data
if primary.output_prefix_type == tink_pb2.LEGACY:
sign_data = sign_data + b'\x00'
return primary.identifier + primary.primitive.sign(sign_data)
class PublicKeySignWrapper(
core.PrimitiveWrapper[_public_key_sign.PublicKeySign,
_public_key_sign.PublicKeySign]):
"""A PrimitiveWrapper for the PublicKeySign primitive.
The returned primitive works with a keyset (rather than a single key). To sign
a message, it uses the primary key in the keyset, and prepends to the
signature a certain prefix associated with the primary key.
"""
def wrap(self, primitives_set: core.PrimitiveSet
) -> _WrappedPublicKeySign:
return _WrappedPublicKeySign(primitives_set)
def primitive_class(self) -> Type[_public_key_sign.PublicKeySign]:
return _public_key_sign.PublicKeySign
def input_primitive_class(self) -> Type[_public_key_sign.PublicKeySign]:
return _public_key_sign.PublicKeySign
class _WrappedPublicKeyVerify(_public_key_verify.PublicKeyVerify):
"""Implements PublicKeyVerify for a set of PublicKeyVerify primitives."""
def __init__(self, primitives_set: core.PrimitiveSet):
self._primitive_set = primitives_set
def verify(self, signature: bytes, data: bytes):
"""Verifies that signature is a digital signature for data.
Args:
signature: The signature bytes to be checked.
data: The data bytes to be checked.
Raises:
tink_error.TinkError if the verification fails.
"""
if len(signature) <= core.crypto_format.NON_RAW_PREFIX_SIZE:
# This also rejects raw signatures with size of 4 bytes or fewer.
# We're not aware of any schemes that output signatures that small.
raise core.TinkError('signature too short')
key_id = signature[:core.crypto_format.NON_RAW_PREFIX_SIZE]
raw_sig = signature[core.crypto_format.NON_RAW_PREFIX_SIZE:]
for entry in self._primitive_set.primitive_from_identifier(key_id):
try:
if entry.output_prefix_type == tink_pb2.LEGACY:
entry.primitive.verify(raw_sig, data + b'\x00')
else:
entry.primitive.verify(raw_sig, data)
# Signature is valid, we can return
return
except core.TinkError as err:
logging.info('signature prefix matches a key, but cannot verify: %s',
err)
# No matching key succeeded with verification, try all RAW keys
for entry in self._primitive_set.raw_primitives():
try:
entry.primitive.verify(signature, data)
# Signature is valid, we can return
return
except core.TinkError:
pass
raise core.TinkError('invalid signature')
class PublicKeyVerifyWrapper(
core.PrimitiveWrapper[_public_key_verify.PublicKeyVerify,
_public_key_verify.PublicKeyVerify]):
"""WrappedPublicKeyVerify is the PrimitiveWrapper for PublicKeyVerify.
The returned primitive works with a keyset (rather than a single key). To sign
a message, it uses the primary key in the keyset, and prepends to the
signature a certain prefix associated with the primary key.
The returned primitive works with a keyset (rather than a single key). To
verify a signature, the primitive uses the prefix of the signature to
efficiently select the right key in the set. If there is no key associated
with the prefix or if the keys associated with the prefix do not work, the
primitive tries all keys with tink_pb2.OutputPrefixType = tink_pb2.RAW.
"""
def wrap(self, primitives_set: core.PrimitiveSet
) -> _WrappedPublicKeyVerify:
return _WrappedPublicKeyVerify(primitives_set)
def primitive_class(self) -> Type[_public_key_verify.PublicKeyVerify]:
return _public_key_verify.PublicKeyVerify
def input_primitive_class(self) -> Type[_public_key_verify.PublicKeyVerify]:
return _public_key_verify.PublicKeyVerify