xref: /aosp_15_r20/external/tink/testing/cross_language/util/key_util_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 for tink.testing.cross_language.util.key_util."""
15
16from absl.testing import absltest
17from absl.testing import parameterized
18
19from google.protobuf import text_format
20from tink.proto import tink_pb2
21from util import key_util
22
23KEY_TEMPLATE_1 = r"""
24 type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey"
25 value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\n\002\010\020\020\020\030\001"
26 output_prefix_type: RAW
27"""
28
29# The same template as 1, but here AesEaxKeyFormat is encoded differently
30KEY_TEMPLATE_1_NOT_NORMALIZED = r"""
31 type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey"
32 value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\020\020\n\002\010\020\030\001"
33 output_prefix_type: RAW
34"""
35
36# The same template as 1, but the inner AesEaxKeyFormat has a different iv_size
37KEY_TEMPLATE_2 = r"""
38 type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey"
39 value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\n\002\010\020\020\022\030\001"
40 output_prefix_type: RAW
41"""
42
43KEY_TEMPLATE_1_COMMENTED_FORMAT = r"""
44type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey"
45# value: [type.googleapis.com/google.crypto.tink.EciesAeadHkdfKeyFormat] {
46#   params {
47#     kem_params {
48#       curve_type: UNKNOWN_CURVE
49#       hkdf_hash_type: UNKNOWN_HASH
50#       hkdf_salt: ""
51#     }
52#     dem_params {
53#       aead_dem {
54#         type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey"
55#         # value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] {
56#         #   params {
57#         #     iv_size: 16
58#         #   }
59#         #   key_size: 16
60#         # }
61#         value: "\n\002\010\020\020\020"
62#         output_prefix_type: TINK
63#       }
64#     }
65#     ec_point_format: UNKNOWN_FORMAT
66#   }
67# }
68value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\n\002\010\020\020\020\030\001"
69output_prefix_type: RAW
70""".strip()
71
72
73class KeyUtilTest(parameterized.TestCase):
74
75  def test_text_format_symmetric_key_template(self):
76    template = tink_pb2.KeyTemplate(
77        type_url='type.googleapis.com/google.crypto.tink.AesEaxKey',
78        value=b'\n\x02\x08\x10\x10\x10',
79        output_prefix_type=tink_pb2.TINK)
80    expected = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey"
81# value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] {
82#   params {
83#     iv_size: 16
84#   }
85#   key_size: 16
86# }
87value: "\n\002\010\020\020\020"
88output_prefix_type: TINK"""
89    output = key_util.text_format(template)
90    self.assertEqual(output, expected)
91    # the output should be in text format, and result in the original template.
92    self.assertEqual(
93        text_format.Parse(output, tink_pb2.KeyTemplate()), template)
94
95  def test_text_format_asymmetric_key_template(self):
96    template = tink_pb2.KeyTemplate(
97        type_url='type.googleapis.com/google.crypto.tink.EcdsaPrivateKey',
98        value=b'\022\006\010\004\020\003\030\002',
99        output_prefix_type=tink_pb2.TINK)
100    expected = r"""type_url: "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey"
101# value: [type.googleapis.com/google.crypto.tink.EcdsaKeyFormat] {
102#   params {
103#     hash_type: SHA512
104#     curve: NIST_P384
105#     encoding: DER
106#   }
107#   version: 0
108# }
109value: "\022\006\010\004\020\003\030\002"
110output_prefix_type: TINK"""
111    output = key_util.text_format(template)
112    self.assertEqual(output, expected)
113    # the output should be in text format, and result in the original template.
114    self.assertEqual(
115        text_format.Parse(output, tink_pb2.KeyTemplate()), template)
116
117  def test_text_format_recursive_template(self):
118    template = tink_pb2.KeyTemplate()
119    text_format.Parse(KEY_TEMPLATE_1, template)
120    output = key_util.text_format(template)
121    self.assertEqual(output, KEY_TEMPLATE_1_COMMENTED_FORMAT)
122
123  def test_text_format_normalizes_recursive_template(self):
124    template1a = tink_pb2.KeyTemplate()
125    text_format.Parse(KEY_TEMPLATE_1, template1a)
126
127    template = tink_pb2.KeyTemplate()
128    text_format.Parse(KEY_TEMPLATE_1_NOT_NORMALIZED, template)
129    # Before the call, the value is different (different serializations)
130    self.assertNotEqual(template1a.value, template.value)
131
132    normalized_template = key_util.text_format(template)
133    self.assertEqual(normalized_template, KEY_TEMPLATE_1_COMMENTED_FORMAT)
134
135    # We explicitly test that the value has not been changed (since this
136    # requirement needs an explicit copy in the code)
137    self.assertNotEqual(template1a.value, template.value)
138
139  def test_text_format_keyset(self):
140    key = tink_pb2.Keyset.Key(
141        key_data=tink_pb2.KeyData(
142            type_url='type.googleapis.com/google.crypto.tink.AesGcmKey',
143            value=b'\032\020Z\027\031\027\362\353\020\320\257p\271^\260\022\344\274',
144            key_material_type=tink_pb2.KeyData.SYMMETRIC),
145        status=tink_pb2.ENABLED,
146        key_id=3588418072,
147        output_prefix_type=tink_pb2.TINK)
148    keyset = tink_pb2.Keyset(primary_key_id=3588418072)
149    keyset.key.append(key)
150    expected = r"""primary_key_id: 3588418072
151key {
152  key_data {
153    type_url: "type.googleapis.com/google.crypto.tink.AesGcmKey"
154    # value: [type.googleapis.com/google.crypto.tink.AesGcmKey] {
155    #   version: 0
156    #   key_value: "Z\027\031\027\362\353\020\320\257p\271^\260\022\344\274"
157    # }
158    value: "\032\020Z\027\031\027\362\353\020\320\257p\271^\260\022\344\274"
159    key_material_type: SYMMETRIC
160  }
161  status: ENABLED
162  key_id: 3588418072
163  output_prefix_type: TINK
164}"""
165    output = key_util.text_format(keyset)
166    self.assertEqual(output, expected)
167    # the output should be in text format, and result in the original template.
168    self.assertEqual(
169        text_format.Parse(output, tink_pb2.Keyset()), keyset)
170
171  def test_compare_tink_messages(self):
172    """Tests that all testdata have the expected format, including comments."""
173    key_template_1 = text_format.Parse(KEY_TEMPLATE_1, tink_pb2.KeyTemplate())
174    key_template_1_not_normalized = text_format.Parse(
175        KEY_TEMPLATE_1_NOT_NORMALIZED, tink_pb2.KeyTemplate())
176    key_util.assert_tink_proto_equal(self, key_template_1,
177                                     key_template_1_not_normalized)
178    key_template_2 = text_format.Parse(KEY_TEMPLATE_2, tink_pb2.KeyTemplate())
179
180    with self.assertRaises(AssertionError):
181      key_util.assert_tink_proto_equal(self, key_template_1, key_template_2)
182
183  def test_parse_text_format_symmetric_key_template(self):
184    serialized = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey"
185# value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] {
186#   params {
187#     iv_size: 16
188#   }
189#   key_size: 16
190# }
191value: "\n\002\010\020\020\020"
192output_prefix_type: TINK"""
193    expected = tink_pb2.KeyTemplate(
194        type_url='type.googleapis.com/google.crypto.tink.AesEaxKey',
195        value=b'\n\x02\x08\x10\x10\x10',
196        output_prefix_type=tink_pb2.TINK)
197
198    parsed_template = tink_pb2.KeyTemplate()
199    key_util.parse_text_format(serialized, parsed_template)
200    self.assertEqual(parsed_template, expected)
201
202  def test_parse_text_format_wrong_comment(self):
203    serialized = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey"
204value: "\n\002\010\020\020\020"
205output_prefix_type: TINK"""
206
207    parsed_template = tink_pb2.KeyTemplate()
208    with self.assertRaises(AssertionError):
209      key_util.parse_text_format(serialized, parsed_template)
210
211  def test_parse_text_format_missing_comment(self):
212    serialized = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey"
213# value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] {
214#   params {
215#     iv_size: 16
216#   }
217#   key_size: 18
218# }
219value: "\n\002\010\020\020\020"
220output_prefix_type: TINK"""
221
222    parsed_template = tink_pb2.KeyTemplate()
223    with self.assertRaises(AssertionError):
224      key_util.parse_text_format(serialized, parsed_template)
225
226  def test_assert_tink_proto_equal_does_not_modify_messages(self):
227    """Tests that assert_tink_proto_equal does not modify the message."""
228    key_template_1 = text_format.Parse(KEY_TEMPLATE_1, tink_pb2.KeyTemplate())
229    key_template_1_original = text_format.Parse(
230        KEY_TEMPLATE_1_NOT_NORMALIZED, tink_pb2.KeyTemplate())
231    key_template_1_not_normalized = text_format.Parse(
232        KEY_TEMPLATE_1_NOT_NORMALIZED, tink_pb2.KeyTemplate())
233    key_util.assert_tink_proto_equal(self, key_template_1,
234                                     key_template_1_not_normalized)
235    self.assertEqual(key_template_1_original.value,
236                     key_template_1_not_normalized.value)
237    key_util.assert_tink_proto_equal(self, key_template_1_not_normalized,
238                                     key_template_1)
239    self.assertEqual(key_template_1_original.value,
240                     key_template_1_not_normalized.value)
241
242  def test_text_format_with_empty_value(self):
243    expected = r"""type_url: "type.googleapis.com/google.crypto.tink.ChaCha20Poly1305Key"
244# value: [type.googleapis.com/google.crypto.tink.ChaCha20Poly1305KeyFormat] {
245# }
246value: ""
247output_prefix_type: RAW"""
248
249    template = tink_pb2.KeyTemplate(
250        type_url='type.googleapis.com/google.crypto.tink.ChaCha20Poly1305Key',
251        output_prefix_type=tink_pb2.RAW)
252    formatted = key_util.text_format(template)
253    self.assertEqual(formatted, expected)
254
255
256if __name__ == '__main__':
257  absltest.main()
258