| # 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 |