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