xref: /aosp_15_r20/external/tink/testing/cross_language/keyset_validation_test.py (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1# Copyright 2020 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"""Tests keyset validation.
15
16Currently only tests if keysets without a primary key are properly handled.
17"""
18
19import datetime
20from typing import Any
21from typing import Iterable
22from typing import Tuple
23
24from absl.testing import absltest
25from absl.testing import parameterized
26import tink
27from tink import aead
28from tink import daead
29from tink import hybrid
30from tink import jwt
31from tink import mac
32from tink import prf
33from tink import signature
34
35from tink.proto import tink_pb2
36import tink_config
37from util import testing_servers
38from util import utilities
39
40
41def unset_primary(keyset: bytes) -> bytes:
42  """Parses keyset and generates modified keyset without primary key."""
43  keyset_proto = tink_pb2.Keyset.FromString(keyset)
44  keyset_proto.ClearField('primary_key_id')
45  return keyset_proto.SerializeToString()
46
47
48def test_cases(primitive: Any) -> Iterable[Tuple[str, str]]:
49  for key_type in tink_config.key_types_for_primitive(primitive):
50    for key_template_name in utilities.KEY_TEMPLATE_NAMES[key_type]:
51      for lang in tink_config.supported_languages_for_key_type(key_type):
52        yield (key_template_name, lang)
53
54
55def setUpModule():
56  testing_servers.start('key_version')
57
58
59def tearDownModule():
60  testing_servers.stop()
61
62
63class KeysetValidationTest(parameterized.TestCase):
64  """These tests verify keysets are properly validated."""
65
66  @parameterized.parameters(test_cases(aead.Aead))
67  def test_aead_without_primary(self, key_template_name, lang):
68    """Unsets the primary key and tries to use an AEAD primitive."""
69    template = utilities.KEY_TEMPLATE[key_template_name]
70    keyset = testing_servers.new_keyset(lang, template)
71
72    with self.assertRaises(tink.TinkError):
73      _ = testing_servers.remote_primitive(lang, unset_primary(keyset),
74                                           aead.Aead)
75
76  @parameterized.parameters(test_cases(daead.DeterministicAead))
77  def test_daead_without_primary(self, key_template_name, lang):
78    """Unsets the primary key and tries to use a DAEAD primitive."""
79    template = utilities.KEY_TEMPLATE[key_template_name]
80    keyset = testing_servers.new_keyset(lang, template)
81    with self.assertRaises(tink.TinkError):
82      _ = testing_servers.remote_primitive(lang, unset_primary(keyset),
83                                           daead.DeterministicAead)
84
85  @parameterized.parameters(test_cases(mac.Mac))
86  def test_mac_without_primary(self, key_template_name, lang):
87    """Unsets the primary key and tries to use a MAC primitive."""
88    template = utilities.KEY_TEMPLATE[key_template_name]
89    keyset = testing_servers.new_keyset(lang, template)
90    with self.assertRaises(tink.TinkError):
91      testing_servers.remote_primitive(lang, unset_primary(keyset), mac.Mac)
92
93  @parameterized.parameters(test_cases(prf.PrfSet))
94  def test_prf_without_primary(self, key_template_name, lang):
95    """Unsets the primary key and tries to use a PRF set primitive."""
96    template = utilities.KEY_TEMPLATE[key_template_name]
97    keyset = testing_servers.new_keyset(lang, template)
98    with self.assertRaises(tink.TinkError):
99      _ = testing_servers.remote_primitive(lang, unset_primary(keyset),
100                                           prf.PrfSet)
101
102  @parameterized.parameters(test_cases(signature.PublicKeySign))
103  def test_signature_without_primary(self, key_template_name, lang):
104    """Unsets the primary key and tries to sign and verify signatures."""
105    template = utilities.KEY_TEMPLATE[key_template_name]
106    private_keyset = testing_servers.new_keyset(lang, template)
107    public_keyset = testing_servers.public_keyset(lang, private_keyset)
108    signer = testing_servers.remote_primitive(lang, private_keyset,
109                                              signature.PublicKeySign)
110    sig = signer.sign(b'foo')
111    verifier = testing_servers.remote_primitive(lang, public_keyset,
112                                                signature.PublicKeyVerify)
113    verifier.verify(sig, b'foo')
114    private_keyset_without_primary = unset_primary(private_keyset)
115    public_keyset_without_primary = unset_primary(public_keyset)
116    with self.assertRaises(tink.TinkError):
117      _ = testing_servers.remote_primitive(
118          lang, private_keyset_without_primary, signature.PublicKeySign)
119    if lang not in ['python']:
120      with self.assertRaises(tink.TinkError):
121        _ = testing_servers.remote_primitive(
122            lang, public_keyset_without_primary, signature.PublicKeyVerify)
123    if lang in ['python']:
124      # TODO(b/252792776) This should fail.
125      verifier_without_primary = testing_servers.remote_primitive(
126          lang, public_keyset_without_primary, signature.PublicKeyVerify)
127      verifier_without_primary.verify(sig, b'foo')
128
129  @parameterized.parameters(test_cases(hybrid.HybridDecrypt))
130  def test_hybrid_without_primary(self, key_template_name, lang):
131    """Unsets the primary key and tries to use hybrid encryption."""
132    template = utilities.KEY_TEMPLATE[key_template_name]
133    private_keyset = testing_servers.new_keyset(lang, template)
134    public_keyset = testing_servers.public_keyset(lang, private_keyset)
135
136    private_keyset_without_primary = unset_primary(private_keyset)
137    with self.assertRaises(tink.TinkError):
138      testing_servers.remote_primitive(
139          lang, unset_primary(private_keyset_without_primary),
140          hybrid.HybridDecrypt)
141
142    public_keyset_without_primary = unset_primary(public_keyset)
143    with self.assertRaises(tink.TinkError):
144      enc_without_primary = testing_servers.remote_primitive(
145          lang, public_keyset_without_primary, hybrid.HybridEncrypt)
146      # TODO(b/228140127) This should fail above already.
147      enc_without_primary.encrypt(b'foo', b'context_info')
148
149  @parameterized.parameters(test_cases(jwt.JwtPublicKeySign))
150  def test_jwt_signature_without_primary(self, key_template_name, lang):
151    """Unsets the primary key and tries to sign and verify JWT signatures."""
152    template = utilities.KEY_TEMPLATE[key_template_name]
153    private_keyset = testing_servers.new_keyset(lang, template)
154    public_keyset = testing_servers.public_keyset(lang, private_keyset)
155    signer = testing_servers.remote_primitive(lang, private_keyset,
156                                              jwt.JwtPublicKeySign)
157
158    now = datetime.datetime.now(tz=datetime.timezone.utc)
159    raw_jwt = jwt.new_raw_jwt(
160        issuer='issuer',
161        expiration=now + datetime.timedelta(seconds=100))
162    token = signer.sign_and_encode(raw_jwt)
163
164    private_keyset_without_primary = unset_primary(private_keyset)
165    public_keyset_without_primary = unset_primary(public_keyset)
166
167    with self.assertRaises(tink.TinkError):
168      _ = testing_servers.remote_primitive(
169          lang, private_keyset_without_primary, jwt.JwtPublicKeySign)
170
171    if lang not in ['cc', 'python']:
172      with self.assertRaises(tink.TinkError):
173        _ = testing_servers.remote_primitive(lang,
174                                             public_keyset_without_primary,
175                                             jwt.JwtPublicKeyVerify)
176    if lang in ['cc', 'python']:
177      # TODO(b/252792776) This should fail.
178      verifier_without_primary = testing_servers.remote_primitive(
179          lang, public_keyset_without_primary, jwt.JwtPublicKeyVerify)
180      validator = jwt.new_validator(expected_issuer='issuer', fixed_now=now)
181      verifier_without_primary.verify_and_decode(token, validator)
182
183  @parameterized.parameters(test_cases(jwt.JwtMac))
184  def test_jwt_mac_without_primary(self, key_template_name, lang):
185    """Unsets the primary key and tries to create and verify JWT MACs."""
186    template = utilities.KEY_TEMPLATE[key_template_name]
187    keyset = testing_servers.new_keyset(lang, template)
188
189    with self.assertRaises(tink.TinkError):
190      _ = testing_servers.remote_primitive(lang, unset_primary(keyset),
191                                           jwt.JwtMac)
192
193
194if __name__ == '__main__':
195  absltest.main()
196