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"""Tests for tink.python.tink.aead_wrapper.""" 16 17from absl.testing import absltest 18from absl.testing import parameterized 19import tink 20from tink import cleartext_keyset_handle 21from tink import core 22from tink import hybrid 23from tink.testing import keyset_builder 24 25 26TEMPLATE = hybrid.hybrid_key_templates.ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM 27RAW_TEMPLATE = keyset_builder.raw_template(TEMPLATE) 28 29PRIVATE_KEYSET_WITH_PRIMARY = """ 30 { 31 "primaryKeyId": 2160196527, 32 "key": [ 33 { 34 "keyData": { 35 "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey", 36 "value": "GiCP3xXdT0G7GQMQ+k7nsl+W5ElO3EN6ybwSPTGWdArHTBKKASIgrgyjrYI2mV+K8Csa703wGgtlOG+yxHajoM6quNA1IHEaIASUo/ubipc/Vn+WhN64qmnzp9mqzyvQTQ5jrNt6Xwq6EkQYARI6EjgYARICEBAKMHR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmNyeXB0by50aW5rLkFlc0djbUtleQoEEAMIAg==", 37 "keyMaterialType": "ASYMMETRIC_PRIVATE" 38 }, 39 "status": "ENABLED", 40 "keyId": 2160196527, 41 "outputPrefixType": "TINK" 42 } 43 ] 44 }""" 45# Same as PRIVATE_KEYSET_WITH_PRIMARY, but without primaryKeyId. 46PRIVATE_KEYSET_WITHOUT_PRIMARY = """ 47 { 48 "key": [ 49 { 50 "keyData": { 51 "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey", 52 "value": "GiCP3xXdT0G7GQMQ+k7nsl+W5ElO3EN6ybwSPTGWdArHTBKKASIgrgyjrYI2mV+K8Csa703wGgtlOG+yxHajoM6quNA1IHEaIASUo/ubipc/Vn+WhN64qmnzp9mqzyvQTQ5jrNt6Xwq6EkQYARI6EjgYARICEBAKMHR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmNyeXB0by50aW5rLkFlc0djbUtleQoEEAMIAg==", 53 "keyMaterialType": "ASYMMETRIC_PRIVATE" 54 }, 55 "status": "ENABLED", 56 "keyId": 2160196527, 57 "outputPrefixType": "TINK" 58 } 59 ] 60 }""" 61PUBLIC_KEYSET_WITH_PRIMARY = """ 62 { 63 "primaryKeyId": 2160196527, 64 "key": [ 65 { 66 "keyData": { 67 "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey", 68 "value": "IiCuDKOtgjaZX4rwKxrvTfAaC2U4b7LEdqOgzqq40DUgcRogBJSj+5uKlz9Wf5aE3riqafOn2arPK9BNDmOs23pfCroSRBgBEjoSOBgBEgIQEAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5CgQQAwgC", 69 "keyMaterialType": "ASYMMETRIC_PUBLIC" 70 }, 71 "status": "ENABLED", 72 "keyId": 2160196527, 73 "outputPrefixType": "TINK" 74 } 75 ] 76 }""" 77# Same as PUBLIC_KEYSET_WITH_PRIMARY, but without primaryKeyId. 78PUBLIC_KEYSET_WITHOUT_PRIMARY = """ 79 { 80 "key": [ 81 { 82 "keyData": { 83 "typeUrl": "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPublicKey", 84 "value": "IiCuDKOtgjaZX4rwKxrvTfAaC2U4b7LEdqOgzqq40DUgcRogBJSj+5uKlz9Wf5aE3riqafOn2arPK9BNDmOs23pfCroSRBgBEjoSOBgBEgIQEAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5CgQQAwgC", 85 "keyMaterialType": "ASYMMETRIC_PUBLIC" 86 }, 87 "status": "ENABLED", 88 "keyId": 2160196527, 89 "outputPrefixType": "TINK" 90 } 91 ] 92 }""" 93 94 95def setUpModule(): 96 hybrid.register() 97 98 99class HybridWrapperTest(parameterized.TestCase): 100 101 @parameterized.parameters([TEMPLATE, RAW_TEMPLATE]) 102 def test_encrypt_decrypt(self, template): 103 private_handle = tink.new_keyset_handle(template) 104 public_handle = private_handle.public_keyset_handle() 105 106 hybrid_enc = public_handle.primitive(hybrid.HybridEncrypt) 107 ciphertext = hybrid_enc.encrypt(b'plaintext', b'context') 108 hybrid_dec = private_handle.primitive(hybrid.HybridDecrypt) 109 self.assertEqual(hybrid_dec.decrypt(ciphertext, b'context'), 110 b'plaintext') 111 112 @parameterized.parameters([TEMPLATE, RAW_TEMPLATE]) 113 def test_decrypt_unknown_ciphertext_fails(self, template): 114 unknown_private_handle = tink.new_keyset_handle(template) 115 unknown_public_handle = unknown_private_handle.public_keyset_handle() 116 unknown_enc = unknown_public_handle.primitive(hybrid.HybridEncrypt) 117 unknown_ciphertext = unknown_enc.encrypt(b'plaintext', b'context') 118 119 private_handle = tink.new_keyset_handle(template) 120 hybrid_dec = private_handle.primitive(hybrid.HybridDecrypt) 121 with self.assertRaises(core.TinkError): 122 hybrid_dec.decrypt(unknown_ciphertext, b'context') 123 124 @parameterized.parameters([TEMPLATE, RAW_TEMPLATE]) 125 def test_decrypt_wrong_associated_data_fails(self, template): 126 private_handle = tink.new_keyset_handle(template) 127 public_handle = private_handle.public_keyset_handle() 128 129 hybrid_enc = public_handle.primitive(hybrid.HybridEncrypt) 130 ciphertext = hybrid_enc.encrypt(b'plaintext', b'context') 131 hybrid_dec = private_handle.primitive(hybrid.HybridDecrypt) 132 with self.assertRaises(core.TinkError): 133 hybrid_dec.decrypt(ciphertext, b'wrong_context') 134 135 @parameterized.parameters([(TEMPLATE, TEMPLATE), 136 (RAW_TEMPLATE, TEMPLATE), 137 (TEMPLATE, RAW_TEMPLATE), 138 (RAW_TEMPLATE, RAW_TEMPLATE)]) 139 def test_encrypt_decrypt_with_key_rotation(self, old_template, new_template): 140 builder = keyset_builder.new_keyset_builder() 141 older_key_id = builder.add_new_key(old_template) 142 builder.set_primary_key(older_key_id) 143 private_handle1 = builder.keyset_handle() 144 dec1 = private_handle1.primitive(hybrid.HybridDecrypt) 145 enc1 = private_handle1.public_keyset_handle().primitive( 146 hybrid.HybridEncrypt) 147 148 newer_key_id = builder.add_new_key(new_template) 149 private_handle2 = builder.keyset_handle() 150 dec2 = private_handle2.primitive(hybrid.HybridDecrypt) 151 enc2 = private_handle2.public_keyset_handle().primitive( 152 hybrid.HybridEncrypt) 153 154 builder.set_primary_key(newer_key_id) 155 private_handle3 = builder.keyset_handle() 156 dec3 = private_handle3.primitive(hybrid.HybridDecrypt) 157 enc3 = private_handle3.public_keyset_handle().primitive( 158 hybrid.HybridEncrypt) 159 160 builder.disable_key(older_key_id) 161 private_handle4 = builder.keyset_handle() 162 dec4 = private_handle4.primitive(hybrid.HybridDecrypt) 163 enc4 = private_handle4.public_keyset_handle().primitive( 164 hybrid.HybridEncrypt) 165 self.assertNotEqual(older_key_id, newer_key_id) 166 167 # p1 encrypts with the older key. So p1, p2 and p3 can decrypt it, 168 # but not p4. 169 ciphertext1 = enc1.encrypt(b'plaintext', b'context') 170 self.assertEqual(dec1.decrypt(ciphertext1, b'context'), b'plaintext') 171 self.assertEqual(dec2.decrypt(ciphertext1, b'context'), b'plaintext') 172 self.assertEqual(dec3.decrypt(ciphertext1, b'context'), b'plaintext') 173 with self.assertRaises(tink.TinkError): 174 _ = dec4.decrypt(ciphertext1, b'context') 175 176 # p2 encrypts with the older key. So p1, p2 and p3 can decrypt it, 177 # but not p4. 178 ciphertext2 = enc2.encrypt(b'plaintext', b'context') 179 self.assertEqual(dec1.decrypt(ciphertext2, b'context'), b'plaintext') 180 self.assertEqual(dec2.decrypt(ciphertext2, b'context'), b'plaintext') 181 self.assertEqual(dec3.decrypt(ciphertext2, b'context'), b'plaintext') 182 with self.assertRaises(tink.TinkError): 183 _ = dec4.decrypt(ciphertext2, b'context') 184 185 # p3 encrypts with the newer key. So p2, p3 and p4 can decrypt it, 186 # but not p1. 187 ciphertext3 = enc3.encrypt(b'plaintext', b'context') 188 with self.assertRaises(tink.TinkError): 189 _ = dec1.decrypt(ciphertext3, b'context') 190 self.assertEqual(dec2.decrypt(ciphertext3, b'context'), b'plaintext') 191 self.assertEqual(dec3.decrypt(ciphertext3, b'context'), b'plaintext') 192 self.assertEqual(dec4.decrypt(ciphertext3, b'context'), b'plaintext') 193 194 # p4 encrypts with the newer key. So p2, p3 and p4 can decrypt it, 195 # but not p1. 196 ciphertext4 = enc4.encrypt(b'plaintext', b'context') 197 with self.assertRaises(tink.TinkError): 198 _ = dec1.decrypt(ciphertext4, b'context') 199 self.assertEqual(dec2.decrypt(ciphertext4, b'context'), b'plaintext') 200 self.assertEqual(dec3.decrypt(ciphertext4, b'context'), b'plaintext') 201 self.assertEqual(dec4.decrypt(ciphertext4, b'context'), b'plaintext') 202 203 def test_encrypt_decrypt_with_primary_key_succeeds(self): 204 private_handle_with_primary = cleartext_keyset_handle.read( 205 tink.JsonKeysetReader(PRIVATE_KEYSET_WITH_PRIMARY)) 206 public_handle_with_primary = cleartext_keyset_handle.read( 207 tink.JsonKeysetReader(PUBLIC_KEYSET_WITH_PRIMARY)) 208 dec = private_handle_with_primary.primitive(hybrid.HybridDecrypt) 209 enc = public_handle_with_primary.primitive(hybrid.HybridEncrypt) 210 ciphertext = enc.encrypt(b'plaintext', b'context') 211 self.assertEqual(dec.decrypt(ciphertext, b'context'), b'plaintext') 212 213 def test_encrypt_decrypt_without_primary_key_fails(self): 214 with self.assertRaises(tink.TinkError): 215 cleartext_keyset_handle.read( 216 tink.JsonKeysetReader(PRIVATE_KEYSET_WITHOUT_PRIMARY)) 217 # Currently, the public keyset only fails when 'encrypt' is called. 218 # TODO(b/228140127): It would be preferable that it fails earlier. 219 public_handle_without_primary = cleartext_keyset_handle.read( 220 tink.JsonKeysetReader(PUBLIC_KEYSET_WITHOUT_PRIMARY)) 221 enc_without_primary = public_handle_without_primary.primitive( 222 hybrid.HybridEncrypt) 223 with self.assertRaises(tink.TinkError): 224 enc_without_primary.encrypt(b'plaintext', b'context') 225 226 227if __name__ == '__main__': 228 absltest.main() 229