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"""Cross-language consistency tests for AEAD. 15 16These tests generate different kind of AEAD keysets, some are valid, some are 17invalid. The test succeeds if all implementation treat the keyset consistently, 18so either encryption/decryption works as expected, or the keyset is rejected. 19""" 20 21import itertools 22from typing import List 23from typing import Tuple 24 25from absl.testing import absltest 26from absl.testing import parameterized 27import tink 28 29from tink.proto import aes_ctr_hmac_aead_pb2 30from tink.proto import aes_eax_pb2 31from tink.proto import aes_gcm_pb2 32from tink.proto import common_pb2 33from tink.proto import tink_pb2 34import tink_config 35from util import testing_servers 36from util import utilities 37 38HASH_TYPES = [ 39 common_pb2.UNKNOWN_HASH, common_pb2.SHA1, common_pb2.SHA224, 40 common_pb2.SHA256, common_pb2.SHA384, common_pb2.SHA512 41] 42 43 44def setUpModule(): 45 tink.aead.register() 46 testing_servers.start('aead_consistency') 47 48 49def tearDownModule(): 50 testing_servers.stop() 51 52 53def _gen_keyset( 54 type_url: str, value: bytes, 55 key_material_type: tink_pb2.KeyData.KeyMaterialType) -> tink_pb2.Keyset: 56 """Generates a new Keyset.""" 57 keyset = tink_pb2.Keyset() 58 key = keyset.key.add() 59 key.key_data.type_url = type_url 60 key.key_data.value = value 61 key.key_data.key_material_type = key_material_type 62 key.status = tink_pb2.ENABLED 63 key.key_id = 42 64 key.output_prefix_type = tink_pb2.TINK 65 keyset.primary_key_id = 42 66 return keyset 67 68 69def _gen_key_value(size: int) -> bytes: 70 """Returns a fixed key_value of a given size.""" 71 return bytes(i for i in range(size)) 72 73 74def aes_eax_key_test_cases(): 75 def _test_case(key_size=16, iv_size=16, key_version=0): 76 key = aes_eax_pb2.AesEaxKey() 77 key.version = key_version 78 key.key_value = _gen_key_value(key_size) 79 key.params.iv_size = iv_size 80 keyset = _gen_keyset( 81 'type.googleapis.com/google.crypto.tink.AesEaxKey', 82 key.SerializeToString(), 83 tink_pb2.KeyData.SYMMETRIC) 84 return ('AesEaxKey(%d,%d,%d)' % (key_size, iv_size, key_version), keyset) 85 for key_size in [15, 16, 24, 32, 64, 96]: 86 for iv_size in [11, 12, 16, 17, 24, 32]: 87 yield _test_case(key_size=key_size, iv_size=iv_size) 88 yield _test_case(key_version=1) 89 90 91def aes_gcm_key_test_cases(): 92 def _test_case(key_size=16, key_version=0): 93 key = aes_gcm_pb2.AesGcmKey() 94 key.version = key_version 95 key.key_value = _gen_key_value(key_size) 96 keyset = _gen_keyset( 97 'type.googleapis.com/google.crypto.tink.AesGcmKey', 98 key.SerializeToString(), 99 tink_pb2.KeyData.SYMMETRIC) 100 return ('AesGcmKey(%d,%d)' % (key_size, key_version), keyset) 101 for key_size in [15, 16, 24, 32, 64, 96]: 102 yield _test_case(key_size=key_size) 103 yield _test_case(key_version=1) 104 105 106def aes_ctr_hmac_aead_key_test_cases(): 107 def _test_case(aes_key_size=16, 108 iv_size=16, 109 hmac_key_size=16, 110 hmac_tag_size=16, 111 hash_type=common_pb2.SHA256, 112 key_version=0, 113 aes_ctr_version=0, 114 hmac_version=0): 115 key = aes_ctr_hmac_aead_pb2.AesCtrHmacAeadKey() 116 key.version = key_version 117 key.aes_ctr_key.version = aes_ctr_version 118 key.aes_ctr_key.params.iv_size = iv_size 119 key.aes_ctr_key.key_value = _gen_key_value(aes_key_size) 120 key.hmac_key.version = hmac_version 121 key.hmac_key.params.tag_size = hmac_tag_size 122 key.hmac_key.params.hash = hash_type 123 key.hmac_key.key_value = _gen_key_value(hmac_key_size) 124 keyset = _gen_keyset( 125 'type.googleapis.com/google.crypto.tink.AesCtrHmacAeadKey', 126 key.SerializeToString(), tink_pb2.KeyData.SYMMETRIC) 127 return ('AesCtrHmacAeadKey(%d,%d,%d,%d,%s,%d,%d,%d)' % 128 (aes_key_size, iv_size, hmac_key_size, hmac_tag_size, 129 common_pb2.HashType.Name(hash_type), key_version, aes_ctr_version, 130 hmac_version), keyset) 131 132 yield _test_case() 133 for aes_key_size in [15, 16, 24, 32, 64, 96]: 134 for iv_size in [11, 12, 16, 17, 24, 32]: 135 yield _test_case(aes_key_size=aes_key_size, iv_size=iv_size) 136 for hmac_key_size in [15, 16, 24, 32, 64, 96]: 137 for hmac_tag_size in [9, 10, 16, 20, 21, 24, 32, 33, 64, 65]: 138 yield _test_case(hmac_key_size=hmac_key_size, hmac_tag_size=hmac_tag_size) 139 for hash_type in HASH_TYPES: 140 yield _test_case(hash_type=hash_type) 141 yield _test_case(key_version=1) 142 yield _test_case(aes_ctr_version=1) 143 yield _test_case(hmac_version=1) 144 145 146class AeadKeyConsistencyTest(parameterized.TestCase): 147 """Tests that all implementation treat all generated keyset in the same way. 148 149 We only consider keyset with single keys. This should be fine, since most 150 inconsistencies between languages will occur in the key validation, and 151 that is done for each key independently. 152 """ 153 154 def _create_aeads_ignore_errors( 155 self, keyset: tink_pb2.Keyset) -> List[Tuple[tink.aead.Aead, str]]: 156 """Creates AEADs for the given keyset in each language. 157 158 Args: 159 keyset: A keyset as 'keyset' proto. 160 161 Returns: 162 A list of pairs (aead, language) 163 """ 164 165 result = [] 166 for lang in utilities.ALL_LANGUAGES: 167 try: 168 aead = testing_servers.remote_primitive(lang, 169 keyset.SerializeToString(), 170 tink.aead.Aead) 171 result.append((aead, lang)) 172 except tink.TinkError: 173 pass 174 return result 175 176 @parameterized.parameters( 177 itertools.chain(aes_eax_key_test_cases(), aes_gcm_key_test_cases(), 178 aes_ctr_hmac_aead_key_test_cases())) 179 def test_aead_creation_supported_languages_consistent(self, name, keyset): 180 """Tests that AEAD creation is consistent in all supporeted languages.""" 181 supported_langs = tink_config.supported_languages_for_key_type( 182 tink_config.key_type_from_type_url(keyset.key[0].key_data.type_url)) 183 184 langs = [lang for _, lang in self._create_aeads_ignore_errors(keyset)] 185 186 if langs: 187 with self.subTest('When creating AEAD objects for %s' % name): 188 self.assertEqual(langs, supported_langs) 189 190 @parameterized.parameters( 191 itertools.chain(aes_eax_key_test_cases(), aes_gcm_key_test_cases(), 192 aes_ctr_hmac_aead_key_test_cases())) 193 def test_aead_creation_non_supported_languages_fail(self, name, keyset): 194 """Tests that AEAD creation fails in all unsupported languages.""" 195 supported_langs = tink_config.supported_languages_for_key_type( 196 tink_config.key_type_from_type_url(keyset.key[0].key_data.type_url)) 197 198 langs = [lang for _, lang in self._create_aeads_ignore_errors(keyset)] 199 200 for lang in utilities.ALL_LANGUAGES: 201 if lang not in supported_langs: 202 self.assertNotIn( 203 lang, langs, 204 'AEAD-Creation should fail in language %s for %s' % (lang, name)) 205 206 @parameterized.parameters( 207 itertools.chain(aes_eax_key_test_cases(), aes_gcm_key_test_cases(), 208 aes_ctr_hmac_aead_key_test_cases())) 209 def test_aead_pairwise_consistency(self, name, keyset): 210 """Tests that created AEADS behave consistently.""" 211 212 aead_and_lang = self._create_aeads_ignore_errors(keyset) 213 plaintext = b'plaintext' 214 associated_data = b'associated_data' 215 216 for aead, lang in aead_and_lang: 217 aead0 = aead_and_lang[0][0] 218 lang0 = aead_and_lang[0][1] 219 220 with self.subTest('Comparing %s-aead to %s-aead for %s ' % 221 (lang0, lang, name)): 222 ciphertext = aead.encrypt(plaintext, associated_data) 223 self.assertEqual(aead0.decrypt(ciphertext, associated_data), plaintext) 224 225 ciphertext = aead0.encrypt(plaintext, associated_data) 226 self.assertEqual(aead.decrypt(ciphertext, associated_data), plaintext) 227 228 229if __name__ == '__main__': 230 absltest.main() 231