xref: /aosp_15_r20/external/tink/testing/cross_language/key_generation_consistency_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 that keys are consistently accepted or rejected in all languages."""
15
16import itertools
17from typing import Iterable, Tuple
18
19from absl import logging
20from absl.testing import absltest
21from absl.testing import parameterized
22
23import tink
24from tink import aead
25from tink import daead
26from tink import hybrid
27from tink import mac
28from tink import prf
29from tink import signature
30
31from tink.proto import common_pb2
32from tink.proto import ecdsa_pb2
33from tink.proto import jwt_hmac_pb2
34from tink.proto import tink_pb2
35import tink_config
36from util import testing_servers
37
38# Test cases that succeed in a language but should fail
39SUCCEEDS_BUT_SHOULD_FAIL = [
40    # TODO(b/160130470): In CC and Python Hybrid templates are not checked for
41    # valid AEAD params. (These params *are* checked when the key is used.)
42    ('EciesAeadHkdfPrivateKey(NIST_P256,UNCOMPRESSED,SHA256,AesEaxKey(15,11))',
43     'cc'),
44    ('EciesAeadHkdfPrivateKey(NIST_P256,UNCOMPRESSED,SHA256,AesEaxKey(15,11))',
45     'python'),
46]
47
48# Test cases that fail in a language but should succeed
49FAILS_BUT_SHOULD_SUCCEED = [
50    # TODO(b/160134058) Java and Go do not accept templates with CURVE25519.
51    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA1,AesGcmKey(16))',
52     'java'),
53    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA1,AesGcmKey(16))',
54     'go'),
55    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA224,AesGcmKey(16))',
56     'java'),
57    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA224,AesGcmKey(16))',
58     'go'),
59    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA256,AesGcmKey(16))',
60     'java'),
61    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA256,AesGcmKey(16))',
62     'go'),
63    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA384,AesGcmKey(16))',
64     'java'),
65    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA384,AesGcmKey(16))',
66     'go'),
67    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA512,AesGcmKey(16))',
68     'java'),
69    ('EciesAeadHkdfPrivateKey(CURVE25519,UNCOMPRESSED,SHA512,AesGcmKey(16))',
70     'go'),
71]
72
73HASH_TYPES = [
74    common_pb2.UNKNOWN_HASH, common_pb2.SHA1, common_pb2.SHA224,
75    common_pb2.SHA256, common_pb2.SHA384, common_pb2.SHA512
76]
77
78CURVE_TYPES = [
79    common_pb2.UNKNOWN_CURVE,
80    common_pb2.NIST_P256,
81    common_pb2.NIST_P384,
82    common_pb2.NIST_P521,
83    common_pb2.CURVE25519
84]
85
86EC_POINT_FORMATS = [
87    common_pb2.UNKNOWN_FORMAT,
88    common_pb2.UNCOMPRESSED,
89    common_pb2.COMPRESSED,
90    common_pb2.DO_NOT_USE_CRUNCHY_UNCOMPRESSED
91]
92
93SIGNATURE_ENCODINGS = [
94    ecdsa_pb2.UNKNOWN_ENCODING,
95    ecdsa_pb2.IEEE_P1363,
96    ecdsa_pb2.DER
97]
98
99
100TestCasesType = Iterable[Tuple[str, tink_pb2.KeyTemplate]]
101
102
103def aes_eax_test_cases() -> TestCasesType:
104  for key_size in [15, 16, 24, 32, 64, 96]:
105    for iv_size in [11, 12, 16, 17, 24, 32]:
106      yield ('AesEaxKey(%d,%d)' % (key_size, iv_size),
107             aead.aead_key_templates.create_aes_eax_key_template(
108                 key_size, iv_size))
109
110
111def aes_gcm_test_cases() -> TestCasesType:
112  for key_size in [15, 16, 24, 32, 64, 96]:
113    yield ('AesGcmKey(%d)' % key_size,
114           aead.aead_key_templates.create_aes_gcm_key_template(key_size))
115
116
117def aes_gcm_siv_test_cases() -> TestCasesType:
118  for key_size in [15, 16, 24, 32, 64, 96]:
119    yield ('AesGcmSivKey(%d)' % key_size,
120           aead.aead_key_templates.create_aes_gcm_siv_key_template(key_size))
121
122
123def aes_ctr_hmac_aead_test_cases() -> TestCasesType:
124  def _test_case(aes_key_size=16, iv_size=16, hmac_key_size=16,
125                 tag_size=16, hash_type=common_pb2.SHA256):
126    return ('AesCtrHmacAeadKey(%d,%d,%d,%d,%s)' %
127            (aes_key_size, iv_size, hmac_key_size, tag_size,
128             common_pb2.HashType.Name(hash_type)),
129            aead.aead_key_templates.create_aes_ctr_hmac_aead_key_template(
130                aes_key_size=aes_key_size,
131                iv_size=iv_size,
132                hmac_key_size=hmac_key_size,
133                tag_size=tag_size,
134                hash_type=hash_type))
135  for aes_key_size in [15, 16, 24, 32, 64, 96]:
136    for iv_size in [11, 12, 16, 17, 24, 32]:
137      yield _test_case(aes_key_size=aes_key_size, iv_size=iv_size)
138  for hmac_key_size in [15, 16, 24, 32, 64, 96]:
139    for tag_size in [9, 10, 16, 20, 21, 24, 32, 33, 64, 65]:
140      for hash_type in HASH_TYPES:
141        yield _test_case(hmac_key_size=hmac_key_size, tag_size=tag_size,
142                         hash_type=hash_type)
143
144
145def hmac_test_cases() -> TestCasesType:
146  def _test_case(key_size=32, tag_size=16, hash_type=common_pb2.SHA256):
147    return ('HmacKey(%d,%d,%s)' % (key_size, tag_size,
148                                   common_pb2.HashType.Name(hash_type)),
149            mac.mac_key_templates.create_hmac_key_template(
150                key_size, tag_size, hash_type))
151  for key_size in [15, 16, 24, 32, 64, 96]:
152    yield _test_case(key_size=key_size)
153  for tag_size in [9, 10, 16, 20, 21, 24, 32, 33, 64, 65]:
154    for hash_type in HASH_TYPES:
155      yield _test_case(tag_size=tag_size, hash_type=hash_type)
156
157
158def jwt_hmac_test_cases() -> TestCasesType:
159  def _test_case(
160      algorithm: jwt_hmac_pb2.JwtHmacAlgorithm, key_size: int,
161      output_prefix_type: tink_pb2.OutputPrefixType
162  ) -> Tuple[str, tink_pb2.KeyTemplate]:
163    key_format = jwt_hmac_pb2.JwtHmacKeyFormat(
164        algorithm=algorithm, key_size=key_size)
165    template = tink_pb2.KeyTemplate(
166        type_url='type.googleapis.com/google.crypto.tink.JwtHmacKey',
167        value=key_format.SerializeToString(),
168        output_prefix_type=output_prefix_type)
169    return ('JwtHmacKey(%d,%s,%s)' %
170            (key_size, jwt_hmac_pb2.JwtHmacAlgorithm.Name(algorithm),
171             tink_pb2.OutputPrefixType.Name(output_prefix_type)), template)
172  yield _test_case(jwt_hmac_pb2.HS256, 31, tink_pb2.RAW)
173  yield _test_case(jwt_hmac_pb2.HS256, 32, tink_pb2.RAW)
174  yield _test_case(jwt_hmac_pb2.HS256, 32, tink_pb2.TINK)
175  yield _test_case(jwt_hmac_pb2.HS256, 33, tink_pb2.RAW)
176
177  yield _test_case(jwt_hmac_pb2.HS384, 47, tink_pb2.RAW)
178  yield _test_case(jwt_hmac_pb2.HS384, 48, tink_pb2.RAW)
179  yield _test_case(jwt_hmac_pb2.HS384, 48, tink_pb2.TINK)
180  yield _test_case(jwt_hmac_pb2.HS384, 49, tink_pb2.RAW)
181
182  yield _test_case(jwt_hmac_pb2.HS512, 63, tink_pb2.RAW)
183  yield _test_case(jwt_hmac_pb2.HS512, 64, tink_pb2.RAW)
184  yield _test_case(jwt_hmac_pb2.HS512, 64, tink_pb2.TINK)
185  yield _test_case(jwt_hmac_pb2.HS512, 65, tink_pb2.RAW)
186
187
188def aes_cmac_test_cases() -> TestCasesType:
189  def _test_case(key_size=32, tag_size=16):
190    return ('AesCmacKey(%d,%d)' % (key_size, tag_size),
191            mac.mac_key_templates.create_aes_cmac_key_template(
192                key_size, tag_size))
193  for key_size in [15, 16, 24, 32, 64, 96]:
194    yield _test_case(key_size=key_size)
195  for tag_size in [9, 10, 16, 20, 21, 24, 32, 33, 64, 65]:
196    yield _test_case(tag_size=tag_size)
197
198
199def aes_cmac_prf_test_cases() -> TestCasesType:
200  for key_size in [15, 16, 24, 32, 64, 96]:
201    yield ('AesCmacPrfKey(%d)' % key_size,
202           prf.prf_key_templates._create_aes_cmac_key_template(key_size))
203
204
205def hmac_prf_test_cases() -> TestCasesType:
206  def _test_case(key_size=32, hash_type=common_pb2.SHA256):
207    return ('HmacPrfKey(%d,%s)' % (key_size,
208                                   common_pb2.HashType.Name(hash_type)),
209            prf.prf_key_templates._create_hmac_key_template(
210                key_size, hash_type))
211  for key_size in [15, 16, 24, 32, 64, 96]:
212    yield _test_case(key_size=key_size)
213  for hash_type in HASH_TYPES:
214    yield _test_case(hash_type=hash_type)
215
216
217def hkdf_prf_test_cases() -> TestCasesType:
218  def _test_case(key_size=32, hash_type=common_pb2.SHA256):
219    return ('HkdfPrfKey(%d,%s)' % (key_size,
220                                   common_pb2.HashType.Name(hash_type)),
221            prf.prf_key_templates._create_hkdf_key_template(
222                key_size, hash_type))
223  for key_size in [15, 16, 24, 32, 64, 96]:
224    yield _test_case(key_size=key_size)
225  for hash_type in HASH_TYPES:
226    yield _test_case(hash_type=hash_type)
227
228
229def aes_siv_test_cases() -> TestCasesType:
230  for key_size in [15, 16, 24, 32, 64, 96]:
231    yield ('AesSivKey(%d)' % key_size,
232           daead.deterministic_aead_key_templates.create_aes_siv_key_template(
233               key_size))
234
235
236def ecies_aead_hkdf_test_cases() -> TestCasesType:
237  for curve_type in CURVE_TYPES:
238    for hash_type in HASH_TYPES:
239      ec_point_format = common_pb2.UNCOMPRESSED
240      dem_key_template = aead.aead_key_templates.AES128_GCM
241      yield ('EciesAeadHkdfPrivateKey(%s,%s,%s,AesGcmKey(16))' %
242             (common_pb2.EllipticCurveType.Name(curve_type),
243              common_pb2.EcPointFormat.Name(ec_point_format),
244              common_pb2.HashType.Name(hash_type)),
245             hybrid.hybrid_key_templates.create_ecies_aead_hkdf_key_template(
246                 curve_type, ec_point_format, hash_type, dem_key_template))
247  for ec_point_format in EC_POINT_FORMATS:
248    curve_type = common_pb2.NIST_P256
249    hash_type = common_pb2.SHA256
250    dem_key_template = aead.aead_key_templates.AES128_GCM
251    yield ('EciesAeadHkdfPrivateKey(%s,%s,%s,AesGcmKey(16))' %
252           (common_pb2.EllipticCurveType.Name(curve_type),
253            common_pb2.EcPointFormat.Name(ec_point_format),
254            common_pb2.HashType.Name(hash_type)),
255           hybrid.hybrid_key_templates.create_ecies_aead_hkdf_key_template(
256               curve_type, ec_point_format, hash_type, dem_key_template))
257  curve_type = common_pb2.NIST_P256
258  ec_point_format = common_pb2.UNCOMPRESSED
259  hash_type = common_pb2.SHA256
260  # Use invalid AEAD key template as DEM
261  # TODO(juerg): Once b/160130470 is fixed, increase test coverage to all
262  # aead templates.
263  dem_key_template = aead.aead_key_templates.create_aes_eax_key_template(15, 11)
264  yield ('EciesAeadHkdfPrivateKey(%s,%s,%s,AesEaxKey(15,11))' %
265         (common_pb2.EllipticCurveType.Name(curve_type),
266          common_pb2.EcPointFormat.Name(ec_point_format),
267          common_pb2.HashType.Name(hash_type)),
268         hybrid.hybrid_key_templates.create_ecies_aead_hkdf_key_template(
269             curve_type, ec_point_format, hash_type, dem_key_template))
270
271
272def ecdsa_test_cases() -> TestCasesType:
273  for hash_type in HASH_TYPES:
274    for curve_type in CURVE_TYPES:
275      for signature_encoding in SIGNATURE_ENCODINGS:
276        yield ('EcdsaPrivateKey(%s,%s,%s)' %
277               (common_pb2.HashType.Name(hash_type),
278                common_pb2.EllipticCurveType.Name(curve_type),
279                ecdsa_pb2.EcdsaSignatureEncoding.Name(signature_encoding)),
280               signature.signature_key_templates.create_ecdsa_key_template(
281                   hash_type, curve_type, signature_encoding))
282
283
284def rsa_ssa_pkcs1_test_cases() -> TestCasesType:
285  gen = signature.signature_key_templates.create_rsa_ssa_pkcs1_key_template
286  for hash_type in HASH_TYPES:
287    modulus_size = 2048
288    public_exponent = 65537
289    yield ('RsaSsaPkcs1PrivateKey(%s,%d,%d)' %
290           (common_pb2.HashType.Name(hash_type), modulus_size,
291            public_exponent),
292           gen(hash_type, modulus_size, public_exponent))
293  for modulus_size in [0, 2000, 3072, 4096]:
294    hash_type = common_pb2.SHA256
295    public_exponent = 65537
296    yield ('RsaSsaPkcs1PrivateKey(%s,%d,%d)' %
297           (common_pb2.HashType.Name(hash_type), modulus_size,
298            public_exponent),
299           gen(hash_type, modulus_size, public_exponent))
300  for public_exponent in [0, 1, 2, 3, 65536, 65537, 65538]:
301    hash_type = common_pb2.SHA256
302    modulus_size = 2048
303    yield ('RsaSsaPkcs1PrivateKey(%s,%d,%d)' %
304           (common_pb2.HashType.Name(hash_type), modulus_size,
305            public_exponent),
306           gen(hash_type, modulus_size, public_exponent))
307
308
309def rsa_ssa_pss_test_cases() -> TestCasesType:
310  gen = signature.signature_key_templates.create_rsa_ssa_pss_key_template
311  for hash_type in HASH_TYPES:
312    salt_length = 32
313    modulus_size = 2048
314    public_exponent = 65537
315    yield ('RsaSsaPssPrivateKey(%s,%s,%d,%d,%d)' %
316           (common_pb2.HashType.Name(hash_type),
317            common_pb2.HashType.Name(hash_type), salt_length, modulus_size,
318            public_exponent),
319           gen(hash_type, hash_type, salt_length, modulus_size,
320               public_exponent))
321  for salt_length in [-3, 0, 1, 16, 64]:
322    hash_type = common_pb2.SHA256
323    modulus_size = 2048
324    public_exponent = 65537
325    yield ('RsaSsaPssPrivateKey(%s,%s,%d,%d,%d)' %
326           (common_pb2.HashType.Name(hash_type),
327            common_pb2.HashType.Name(hash_type), salt_length, modulus_size,
328            public_exponent),
329           gen(hash_type, hash_type, salt_length, modulus_size,
330               public_exponent))
331  for modulus_size in [0, 2000, 3072, 4096]:
332    hash_type = common_pb2.SHA256
333    salt_length = 32
334    public_exponent = 65537
335    yield ('RsaSsaPssPrivateKey(%s,%s,%d,%d,%d)' %
336           (common_pb2.HashType.Name(hash_type),
337            common_pb2.HashType.Name(hash_type), salt_length, modulus_size,
338            public_exponent),
339           gen(hash_type, hash_type, salt_length, modulus_size,
340               public_exponent))
341  hash_type1 = common_pb2.SHA256
342  hash_type2 = common_pb2.SHA512
343  salt_length = 32
344  modulus_size = 2048
345  public_exponent = 65537
346  yield ('RsaSsaPssPrivateKey(%s,%s,%d,%d,%d)' %
347         (common_pb2.HashType.Name(hash_type1),
348          common_pb2.HashType.Name(hash_type2), salt_length, modulus_size,
349          public_exponent),
350         gen(hash_type1, hash_type2, salt_length, modulus_size,
351             public_exponent))
352
353  for public_exponent in [0, 1, 2, 3, 65536, 65537, 65538]:
354    hash_type = common_pb2.SHA256
355    salt_length = 32
356    modulus_size = 2048
357    yield ('RsaSsaPssPrivateKey(%s,%s,%d,%d,%d)' %
358           (common_pb2.HashType.Name(hash_type),
359            common_pb2.HashType.Name(hash_type), salt_length, modulus_size,
360            public_exponent),
361           gen(hash_type, hash_type, salt_length, modulus_size,
362               public_exponent))
363
364
365def setUpModule():
366  aead.register()
367  daead.register()
368  mac.register()
369  hybrid.register()
370  signature.register()
371  testing_servers.start('key_generation_consistency')
372
373
374def tearDownModule():
375  testing_servers.stop()
376
377
378class KeyGenerationConsistencyTest(parameterized.TestCase):
379
380  @parameterized.parameters(
381      itertools.chain(aes_eax_test_cases(),
382                      aes_gcm_test_cases(),
383                      aes_gcm_siv_test_cases(),
384                      aes_ctr_hmac_aead_test_cases(),
385                      hmac_test_cases(),
386                      jwt_hmac_test_cases(),
387                      aes_cmac_test_cases(),
388                      aes_cmac_prf_test_cases(),
389                      hmac_prf_test_cases(),
390                      hkdf_prf_test_cases(),
391                      aes_siv_test_cases(),
392                      ecies_aead_hkdf_test_cases(),
393                      ecdsa_test_cases(),
394                      rsa_ssa_pkcs1_test_cases(),
395                      rsa_ssa_pss_test_cases()))
396  def test_key_generation_consistency(self, name, template):
397    supported_langs = tink_config.supported_languages_for_key_type(
398        tink_config.key_type_from_type_url(template.type_url))
399    failures = 0
400    results = {}
401    for lang in supported_langs:
402      try:
403        _ = testing_servers.new_keyset(lang, template)
404        if (name, lang) in SUCCEEDS_BUT_SHOULD_FAIL:
405          failures += 1
406        if (name, lang) in FAILS_BUT_SHOULD_SUCCEED:
407          self.fail('(%s, %s) succeeded, but is in FAILS_BUT_SHOULD_SUCCEED' %
408                    (name, lang))
409        results[lang] = 'success'
410      except tink.TinkError as e:
411        if (name, lang) not in FAILS_BUT_SHOULD_SUCCEED:
412          failures += 1
413        if (name, lang) in SUCCEEDS_BUT_SHOULD_FAIL:
414          self.fail(
415              '(%s, %s) is in SUCCEEDS_BUT_SHOULD_FAIL, but failed with %s' %
416              (name, lang, e))
417        results[lang] = e
418    # Test that either all supported langs accept the template, or all reject.
419    if failures not in [0, len(supported_langs)]:
420      self.fail('key generation for template %s is inconsistent: %s' %
421                (name, results))
422    logging.info('Key generation status: %s',
423                 'Success' if failures == 0 else 'Fail')
424
425if __name__ == '__main__':
426  absltest.main()
427