• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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