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