xref: /aosp_15_r20/external/tink/python/tink/signature/_signature_wrapper.py (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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