1# Copyright 2019 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Public Key Sign wrapper.""" 16 17from typing import Type 18from absl import logging 19 20from tink.proto import tink_pb2 21from tink import core 22from tink.signature import _public_key_sign 23from tink.signature import _public_key_verify 24 25 26class _WrappedPublicKeySign(_public_key_sign.PublicKeySign): 27 """Implements PublicKeySign for a set of PublicKeySign primitives.""" 28 29 def __init__(self, primitives_set: core.PrimitiveSet): 30 self._primitive_set = primitives_set 31 32 def sign(self, data: bytes) -> bytes: 33 """Computes the signature for data using the primary primitive. 34 35 Args: 36 data: The input data. 37 38 Returns: 39 The signature. 40 """ 41 primary = self._primitive_set.primary() 42 43 if not primary: 44 raise core.TinkError('primary primitive not set') 45 46 sign_data = data 47 if primary.output_prefix_type == tink_pb2.LEGACY: 48 sign_data = sign_data + b'\x00' 49 50 return primary.identifier + primary.primitive.sign(sign_data) 51 52 53class PublicKeySignWrapper( 54 core.PrimitiveWrapper[_public_key_sign.PublicKeySign, 55 _public_key_sign.PublicKeySign]): 56 """A PrimitiveWrapper for the PublicKeySign primitive. 57 58 The returned primitive works with a keyset (rather than a single key). To sign 59 a message, it uses the primary key in the keyset, and prepends to the 60 signature a certain prefix associated with the primary key. 61 """ 62 63 def wrap(self, primitives_set: core.PrimitiveSet 64 ) -> _WrappedPublicKeySign: 65 return _WrappedPublicKeySign(primitives_set) 66 67 def primitive_class(self) -> Type[_public_key_sign.PublicKeySign]: 68 return _public_key_sign.PublicKeySign 69 70 def input_primitive_class(self) -> Type[_public_key_sign.PublicKeySign]: 71 return _public_key_sign.PublicKeySign 72 73 74class _WrappedPublicKeyVerify(_public_key_verify.PublicKeyVerify): 75 """Implements PublicKeyVerify for a set of PublicKeyVerify primitives.""" 76 77 def __init__(self, primitives_set: core.PrimitiveSet): 78 self._primitive_set = primitives_set 79 80 def verify(self, signature: bytes, data: bytes): 81 """Verifies that signature is a digital signature for data. 82 83 Args: 84 signature: The signature bytes to be checked. 85 data: The data bytes to be checked. 86 87 Raises: 88 tink_error.TinkError if the verification fails. 89 """ 90 if len(signature) <= core.crypto_format.NON_RAW_PREFIX_SIZE: 91 # This also rejects raw signatures with size of 4 bytes or fewer. 92 # We're not aware of any schemes that output signatures that small. 93 raise core.TinkError('signature too short') 94 95 key_id = signature[:core.crypto_format.NON_RAW_PREFIX_SIZE] 96 raw_sig = signature[core.crypto_format.NON_RAW_PREFIX_SIZE:] 97 98 for entry in self._primitive_set.primitive_from_identifier(key_id): 99 try: 100 if entry.output_prefix_type == tink_pb2.LEGACY: 101 entry.primitive.verify(raw_sig, data + b'\x00') 102 else: 103 entry.primitive.verify(raw_sig, data) 104 # Signature is valid, we can return 105 return 106 except core.TinkError as err: 107 logging.info('signature prefix matches a key, but cannot verify: %s', 108 err) 109 110 # No matching key succeeded with verification, try all RAW keys 111 for entry in self._primitive_set.raw_primitives(): 112 try: 113 entry.primitive.verify(signature, data) 114 # Signature is valid, we can return 115 return 116 except core.TinkError: 117 pass 118 119 raise core.TinkError('invalid signature') 120 121 122class PublicKeyVerifyWrapper( 123 core.PrimitiveWrapper[_public_key_verify.PublicKeyVerify, 124 _public_key_verify.PublicKeyVerify]): 125 """WrappedPublicKeyVerify is the PrimitiveWrapper for PublicKeyVerify. 126 127 The returned primitive works with a keyset (rather than a single key). To sign 128 a message, it uses the primary key in the keyset, and prepends to the 129 signature a certain prefix associated with the primary key. 130 131 The returned primitive works with a keyset (rather than a single key). To 132 verify a signature, the primitive uses the prefix of the signature to 133 efficiently select the right key in the set. If there is no key associated 134 with the prefix or if the keys associated with the prefix do not work, the 135 primitive tries all keys with tink_pb2.OutputPrefixType = tink_pb2.RAW. 136 """ 137 138 def wrap(self, primitives_set: core.PrimitiveSet 139 ) -> _WrappedPublicKeyVerify: 140 return _WrappedPublicKeyVerify(primitives_set) 141 142 def primitive_class(self) -> Type[_public_key_verify.PublicKeyVerify]: 143 return _public_key_verify.PublicKeyVerify 144 145 def input_primitive_class(self) -> Type[_public_key_verify.PublicKeyVerify]: 146 return _public_key_verify.PublicKeyVerify 147