# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License") # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for tink.testing.cross_language.util.key_util.""" from absl.testing import absltest from absl.testing import parameterized from google.protobuf import text_format from tink.proto import tink_pb2 from util import key_util KEY_TEMPLATE_1 = r""" type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey" value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\n\002\010\020\020\020\030\001" output_prefix_type: RAW """ # The same template as 1, but here AesEaxKeyFormat is encoded differently KEY_TEMPLATE_1_NOT_NORMALIZED = r""" type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey" value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\020\020\n\002\010\020\030\001" output_prefix_type: RAW """ # The same template as 1, but the inner AesEaxKeyFormat has a different iv_size KEY_TEMPLATE_2 = r""" type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey" value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\n\002\010\020\020\022\030\001" output_prefix_type: RAW """ KEY_TEMPLATE_1_COMMENTED_FORMAT = r""" type_url: "type.googleapis.com/google.crypto.tink.EciesAeadHkdfPrivateKey" # value: [type.googleapis.com/google.crypto.tink.EciesAeadHkdfKeyFormat] { # params { # kem_params { # curve_type: UNKNOWN_CURVE # hkdf_hash_type: UNKNOWN_HASH # hkdf_salt: "" # } # dem_params { # aead_dem { # type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey" # # value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] { # # params { # # iv_size: 16 # # } # # key_size: 16 # # } # value: "\n\002\010\020\020\020" # output_prefix_type: TINK # } # } # ec_point_format: UNKNOWN_FORMAT # } # } value: "\n@\022>\022<\n0type.googleapis.com/google.crypto.tink.AesEaxKey\022\006\n\002\010\020\020\020\030\001" output_prefix_type: RAW """.strip() class KeyUtilTest(parameterized.TestCase): def test_text_format_symmetric_key_template(self): template = tink_pb2.KeyTemplate( type_url='type.googleapis.com/google.crypto.tink.AesEaxKey', value=b'\n\x02\x08\x10\x10\x10', output_prefix_type=tink_pb2.TINK) expected = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey" # value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] { # params { # iv_size: 16 # } # key_size: 16 # } value: "\n\002\010\020\020\020" output_prefix_type: TINK""" output = key_util.text_format(template) self.assertEqual(output, expected) # the output should be in text format, and result in the original template. self.assertEqual( text_format.Parse(output, tink_pb2.KeyTemplate()), template) def test_text_format_asymmetric_key_template(self): template = tink_pb2.KeyTemplate( type_url='type.googleapis.com/google.crypto.tink.EcdsaPrivateKey', value=b'\022\006\010\004\020\003\030\002', output_prefix_type=tink_pb2.TINK) expected = r"""type_url: "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey" # value: [type.googleapis.com/google.crypto.tink.EcdsaKeyFormat] { # params { # hash_type: SHA512 # curve: NIST_P384 # encoding: DER # } # version: 0 # } value: "\022\006\010\004\020\003\030\002" output_prefix_type: TINK""" output = key_util.text_format(template) self.assertEqual(output, expected) # the output should be in text format, and result in the original template. self.assertEqual( text_format.Parse(output, tink_pb2.KeyTemplate()), template) def test_text_format_recursive_template(self): template = tink_pb2.KeyTemplate() text_format.Parse(KEY_TEMPLATE_1, template) output = key_util.text_format(template) self.assertEqual(output, KEY_TEMPLATE_1_COMMENTED_FORMAT) def test_text_format_normalizes_recursive_template(self): template1a = tink_pb2.KeyTemplate() text_format.Parse(KEY_TEMPLATE_1, template1a) template = tink_pb2.KeyTemplate() text_format.Parse(KEY_TEMPLATE_1_NOT_NORMALIZED, template) # Before the call, the value is different (different serializations) self.assertNotEqual(template1a.value, template.value) normalized_template = key_util.text_format(template) self.assertEqual(normalized_template, KEY_TEMPLATE_1_COMMENTED_FORMAT) # We explicitly test that the value has not been changed (since this # requirement needs an explicit copy in the code) self.assertNotEqual(template1a.value, template.value) def test_text_format_keyset(self): key = tink_pb2.Keyset.Key( key_data=tink_pb2.KeyData( type_url='type.googleapis.com/google.crypto.tink.AesGcmKey', value=b'\032\020Z\027\031\027\362\353\020\320\257p\271^\260\022\344\274', key_material_type=tink_pb2.KeyData.SYMMETRIC), status=tink_pb2.ENABLED, key_id=3588418072, output_prefix_type=tink_pb2.TINK) keyset = tink_pb2.Keyset(primary_key_id=3588418072) keyset.key.append(key) expected = r"""primary_key_id: 3588418072 key { key_data { type_url: "type.googleapis.com/google.crypto.tink.AesGcmKey" # value: [type.googleapis.com/google.crypto.tink.AesGcmKey] { # version: 0 # key_value: "Z\027\031\027\362\353\020\320\257p\271^\260\022\344\274" # } value: "\032\020Z\027\031\027\362\353\020\320\257p\271^\260\022\344\274" key_material_type: SYMMETRIC } status: ENABLED key_id: 3588418072 output_prefix_type: TINK }""" output = key_util.text_format(keyset) self.assertEqual(output, expected) # the output should be in text format, and result in the original template. self.assertEqual( text_format.Parse(output, tink_pb2.Keyset()), keyset) def test_compare_tink_messages(self): """Tests that all testdata have the expected format, including comments.""" key_template_1 = text_format.Parse(KEY_TEMPLATE_1, tink_pb2.KeyTemplate()) key_template_1_not_normalized = text_format.Parse( KEY_TEMPLATE_1_NOT_NORMALIZED, tink_pb2.KeyTemplate()) key_util.assert_tink_proto_equal(self, key_template_1, key_template_1_not_normalized) key_template_2 = text_format.Parse(KEY_TEMPLATE_2, tink_pb2.KeyTemplate()) with self.assertRaises(AssertionError): key_util.assert_tink_proto_equal(self, key_template_1, key_template_2) def test_parse_text_format_symmetric_key_template(self): serialized = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey" # value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] { # params { # iv_size: 16 # } # key_size: 16 # } value: "\n\002\010\020\020\020" output_prefix_type: TINK""" expected = tink_pb2.KeyTemplate( type_url='type.googleapis.com/google.crypto.tink.AesEaxKey', value=b'\n\x02\x08\x10\x10\x10', output_prefix_type=tink_pb2.TINK) parsed_template = tink_pb2.KeyTemplate() key_util.parse_text_format(serialized, parsed_template) self.assertEqual(parsed_template, expected) def test_parse_text_format_wrong_comment(self): serialized = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey" value: "\n\002\010\020\020\020" output_prefix_type: TINK""" parsed_template = tink_pb2.KeyTemplate() with self.assertRaises(AssertionError): key_util.parse_text_format(serialized, parsed_template) def test_parse_text_format_missing_comment(self): serialized = r"""type_url: "type.googleapis.com/google.crypto.tink.AesEaxKey" # value: [type.googleapis.com/google.crypto.tink.AesEaxKeyFormat] { # params { # iv_size: 16 # } # key_size: 18 # } value: "\n\002\010\020\020\020" output_prefix_type: TINK""" parsed_template = tink_pb2.KeyTemplate() with self.assertRaises(AssertionError): key_util.parse_text_format(serialized, parsed_template) def test_assert_tink_proto_equal_does_not_modify_messages(self): """Tests that assert_tink_proto_equal does not modify the message.""" key_template_1 = text_format.Parse(KEY_TEMPLATE_1, tink_pb2.KeyTemplate()) key_template_1_original = text_format.Parse( KEY_TEMPLATE_1_NOT_NORMALIZED, tink_pb2.KeyTemplate()) key_template_1_not_normalized = text_format.Parse( KEY_TEMPLATE_1_NOT_NORMALIZED, tink_pb2.KeyTemplate()) key_util.assert_tink_proto_equal(self, key_template_1, key_template_1_not_normalized) self.assertEqual(key_template_1_original.value, key_template_1_not_normalized.value) key_util.assert_tink_proto_equal(self, key_template_1_not_normalized, key_template_1) self.assertEqual(key_template_1_original.value, key_template_1_not_normalized.value) def test_text_format_with_empty_value(self): expected = r"""type_url: "type.googleapis.com/google.crypto.tink.ChaCha20Poly1305Key" # value: [type.googleapis.com/google.crypto.tink.ChaCha20Poly1305KeyFormat] { # } value: "" output_prefix_type: RAW""" template = tink_pb2.KeyTemplate( type_url='type.googleapis.com/google.crypto.tink.ChaCha20Poly1305Key', output_prefix_type=tink_pb2.RAW) formatted = key_util.text_format(template) self.assertEqual(formatted, expected) if __name__ == '__main__': absltest.main()