1*e7b1675dSTing-Kang Chang# Copyright 2020 Google LLC 2*e7b1675dSTing-Kang Chang# 3*e7b1675dSTing-Kang Chang# Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang# you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang# You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang# 7*e7b1675dSTing-Kang Chang# http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang# 9*e7b1675dSTing-Kang Chang# Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang# distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang# See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang# limitations under the License. 14*e7b1675dSTing-Kang Chang"""Cross-language tests for the MAC primitive.""" 15*e7b1675dSTing-Kang Chang 16*e7b1675dSTing-Kang Changfrom typing import Iterable, Tuple 17*e7b1675dSTing-Kang Chang 18*e7b1675dSTing-Kang Changfrom absl.testing import absltest 19*e7b1675dSTing-Kang Changfrom absl.testing import parameterized 20*e7b1675dSTing-Kang Chang 21*e7b1675dSTing-Kang Changimport tink 22*e7b1675dSTing-Kang Changfrom tink import mac 23*e7b1675dSTing-Kang Chang 24*e7b1675dSTing-Kang Changfrom tink.proto import tink_pb2 25*e7b1675dSTing-Kang Changfrom tink.testing import keyset_builder 26*e7b1675dSTing-Kang Changimport tink_config 27*e7b1675dSTing-Kang Changfrom util import testing_servers 28*e7b1675dSTing-Kang Changfrom util import utilities 29*e7b1675dSTing-Kang Chang 30*e7b1675dSTing-Kang ChangSUPPORTED_LANGUAGES = testing_servers.SUPPORTED_LANGUAGES_BY_PRIMITIVE['mac'] 31*e7b1675dSTing-Kang Chang 32*e7b1675dSTing-Kang Chang 33*e7b1675dSTing-Kang Changdef setUpModule(): 34*e7b1675dSTing-Kang Chang mac.register() 35*e7b1675dSTing-Kang Chang testing_servers.start('mac') 36*e7b1675dSTing-Kang Chang 37*e7b1675dSTing-Kang Chang 38*e7b1675dSTing-Kang Changdef tearDownModule(): 39*e7b1675dSTing-Kang Chang testing_servers.stop() 40*e7b1675dSTing-Kang Chang 41*e7b1675dSTing-Kang Chang 42*e7b1675dSTing-Kang Chang# maps from key_template_name to (key_template, key_type) 43*e7b1675dSTing-Kang Chang_ADDITIONAL_KEY_TEMPLATES = { 44*e7b1675dSTing-Kang Chang # We additionally test a RAW and a LEGACY template, because LEGACY keys 45*e7b1675dSTing-Kang Chang # require tags with a special format, and RAW don't. 46*e7b1675dSTing-Kang Chang # We test one key template for each, because this is handled by the 47*e7b1675dSTing-Kang Chang # primitive wrapper which is independent of the particular key template 48*e7b1675dSTing-Kang Chang # used. 49*e7b1675dSTing-Kang Chang 'HMAC_SHA256_128BITTAG_RAW': (keyset_builder.raw_template( 50*e7b1675dSTing-Kang Chang mac.mac_key_templates.HMAC_SHA256_128BITTAG), 'HmacKey'), 51*e7b1675dSTing-Kang Chang 'HMAC_SHA256_128BITTAG_LEGACY': (keyset_builder.legacy_template( 52*e7b1675dSTing-Kang Chang mac.mac_key_templates.HMAC_SHA256_128BITTAG), 'HmacKey') 53*e7b1675dSTing-Kang Chang} 54*e7b1675dSTing-Kang Chang 55*e7b1675dSTing-Kang Chang 56*e7b1675dSTing-Kang Changclass MacTest(parameterized.TestCase): 57*e7b1675dSTing-Kang Chang 58*e7b1675dSTing-Kang Chang @parameterized.parameters([ 59*e7b1675dSTing-Kang Chang *utilities.tinkey_template_names_for(mac.Mac), 60*e7b1675dSTing-Kang Chang *_ADDITIONAL_KEY_TEMPLATES.keys() 61*e7b1675dSTing-Kang Chang ]) 62*e7b1675dSTing-Kang Chang def test_compute_verify_mac(self, key_template_name): 63*e7b1675dSTing-Kang Chang if key_template_name in _ADDITIONAL_KEY_TEMPLATES: 64*e7b1675dSTing-Kang Chang key_template, key_type = _ADDITIONAL_KEY_TEMPLATES[ 65*e7b1675dSTing-Kang Chang key_template_name] 66*e7b1675dSTing-Kang Chang supported_langs = tink_config.supported_languages_for_key_type(key_type) 67*e7b1675dSTing-Kang Chang else: 68*e7b1675dSTing-Kang Chang key_template = utilities.KEY_TEMPLATE[key_template_name] 69*e7b1675dSTing-Kang Chang supported_langs = ( 70*e7b1675dSTing-Kang Chang utilities.SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[key_template_name]) 71*e7b1675dSTing-Kang Chang self.assertNotEmpty(supported_langs) 72*e7b1675dSTing-Kang Chang # Take the first supported language to generate the keyset. 73*e7b1675dSTing-Kang Chang keyset = testing_servers.new_keyset(supported_langs[0], key_template) 74*e7b1675dSTing-Kang Chang supported_macs = {} 75*e7b1675dSTing-Kang Chang for lang in supported_langs: 76*e7b1675dSTing-Kang Chang supported_macs[lang] = testing_servers.remote_primitive( 77*e7b1675dSTing-Kang Chang lang, keyset, mac.Mac) 78*e7b1675dSTing-Kang Chang for lang, p in supported_macs.items(): 79*e7b1675dSTing-Kang Chang data = ( 80*e7b1675dSTing-Kang Chang b'This is some data to be authenticated using key_template ' 81*e7b1675dSTing-Kang Chang b'%s in %s.' % (key_template_name.encode('utf8'), 82*e7b1675dSTing-Kang Chang lang.encode('utf8'))) 83*e7b1675dSTing-Kang Chang mac_value = p.compute_mac(data) 84*e7b1675dSTing-Kang Chang for _, p2 in supported_macs.items(): 85*e7b1675dSTing-Kang Chang self.assertIsNone(p2.verify_mac(mac_value, data)) 86*e7b1675dSTing-Kang Chang 87*e7b1675dSTing-Kang Chang 88*e7b1675dSTing-Kang Chang# If the implementations work fine for keysets with single keys, then key 89*e7b1675dSTing-Kang Chang# rotation should work if the primitive wrapper is implemented correctly. 90*e7b1675dSTing-Kang Chang# These wrappers do not depend on the key type, so it should be fine to always 91*e7b1675dSTing-Kang Chang# test with the same key type. The wrapper needs to treat keys with output 92*e7b1675dSTing-Kang Chang# prefix RAW and LEGACY differently, so we also test templates with these 93*e7b1675dSTing-Kang Chang# prefixes. 94*e7b1675dSTing-Kang ChangKEY_ROTATION_TEMPLATES = [ 95*e7b1675dSTing-Kang Chang mac.mac_key_templates.HMAC_SHA512_512BITTAG, 96*e7b1675dSTing-Kang Chang keyset_builder.raw_template(mac.mac_key_templates.HMAC_SHA512_512BITTAG), 97*e7b1675dSTing-Kang Chang keyset_builder.legacy_template(mac.mac_key_templates.HMAC_SHA512_512BITTAG) 98*e7b1675dSTing-Kang Chang] 99*e7b1675dSTing-Kang Chang 100*e7b1675dSTing-Kang Chang 101*e7b1675dSTing-Kang Changdef key_rotation_test_cases( 102*e7b1675dSTing-Kang Chang) -> Iterable[Tuple[str, str, tink_pb2.KeyTemplate, tink_pb2.KeyTemplate]]: 103*e7b1675dSTing-Kang Chang for compute_lang in SUPPORTED_LANGUAGES: 104*e7b1675dSTing-Kang Chang for verify_lang in SUPPORTED_LANGUAGES: 105*e7b1675dSTing-Kang Chang for old_key_tmpl in KEY_ROTATION_TEMPLATES: 106*e7b1675dSTing-Kang Chang for new_key_tmpl in KEY_ROTATION_TEMPLATES: 107*e7b1675dSTing-Kang Chang yield (compute_lang, verify_lang, old_key_tmpl, new_key_tmpl) 108*e7b1675dSTing-Kang Chang 109*e7b1675dSTing-Kang Chang 110*e7b1675dSTing-Kang Changclass MacKeyRotationTest(parameterized.TestCase): 111*e7b1675dSTing-Kang Chang 112*e7b1675dSTing-Kang Chang @parameterized.parameters(key_rotation_test_cases()) 113*e7b1675dSTing-Kang Chang def test_key_rotation( 114*e7b1675dSTing-Kang Chang self, compute_lang, verify_lang, old_key_tmpl, new_key_tmpl): 115*e7b1675dSTing-Kang Chang # Do a key rotation from an old key generated from old_key_tmpl to a new 116*e7b1675dSTing-Kang Chang # key generated from new_key_tmpl. MAC computation and verification are done 117*e7b1675dSTing-Kang Chang # in languages compute_lang and verify_lang. 118*e7b1675dSTing-Kang Chang builder = keyset_builder.new_keyset_builder() 119*e7b1675dSTing-Kang Chang older_key_id = builder.add_new_key(old_key_tmpl) 120*e7b1675dSTing-Kang Chang builder.set_primary_key(older_key_id) 121*e7b1675dSTing-Kang Chang compute_mac1 = testing_servers.remote_primitive(compute_lang, 122*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 123*e7b1675dSTing-Kang Chang verify_mac1 = testing_servers.remote_primitive(verify_lang, 124*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 125*e7b1675dSTing-Kang Chang newer_key_id = builder.add_new_key(new_key_tmpl) 126*e7b1675dSTing-Kang Chang compute_mac2 = testing_servers.remote_primitive(compute_lang, 127*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 128*e7b1675dSTing-Kang Chang verify_mac2 = testing_servers.remote_primitive(verify_lang, 129*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 130*e7b1675dSTing-Kang Chang 131*e7b1675dSTing-Kang Chang builder.set_primary_key(newer_key_id) 132*e7b1675dSTing-Kang Chang compute_mac3 = testing_servers.remote_primitive(compute_lang, 133*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 134*e7b1675dSTing-Kang Chang verify_mac3 = testing_servers.remote_primitive(verify_lang, 135*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 136*e7b1675dSTing-Kang Chang 137*e7b1675dSTing-Kang Chang builder.disable_key(older_key_id) 138*e7b1675dSTing-Kang Chang compute_mac4 = testing_servers.remote_primitive(compute_lang, 139*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 140*e7b1675dSTing-Kang Chang verify_mac4 = testing_servers.remote_primitive(verify_lang, 141*e7b1675dSTing-Kang Chang builder.keyset(), mac.Mac) 142*e7b1675dSTing-Kang Chang 143*e7b1675dSTing-Kang Chang self.assertNotEqual(older_key_id, newer_key_id) 144*e7b1675dSTing-Kang Chang # 1 uses the older key. So 1, 2 and 3 can verify the mac, but not 4. 145*e7b1675dSTing-Kang Chang mac_value1 = compute_mac1.compute_mac(b'plaintext') 146*e7b1675dSTing-Kang Chang verify_mac1.verify_mac(mac_value1, b'plaintext') 147*e7b1675dSTing-Kang Chang verify_mac2.verify_mac(mac_value1, b'plaintext') 148*e7b1675dSTing-Kang Chang verify_mac3.verify_mac(mac_value1, b'plaintext') 149*e7b1675dSTing-Kang Chang with self.assertRaises(tink.TinkError): 150*e7b1675dSTing-Kang Chang verify_mac4.verify_mac(mac_value1, b'plaintext') 151*e7b1675dSTing-Kang Chang 152*e7b1675dSTing-Kang Chang # 2 uses the older key. So 1, 2 and 3 can verify the mac, but not 4. 153*e7b1675dSTing-Kang Chang mac_value2 = compute_mac2.compute_mac(b'plaintext') 154*e7b1675dSTing-Kang Chang verify_mac1.verify_mac(mac_value2, b'plaintext') 155*e7b1675dSTing-Kang Chang verify_mac2.verify_mac(mac_value2, b'plaintext') 156*e7b1675dSTing-Kang Chang verify_mac3.verify_mac(mac_value2, b'plaintext') 157*e7b1675dSTing-Kang Chang with self.assertRaises(tink.TinkError): 158*e7b1675dSTing-Kang Chang verify_mac4.verify_mac(mac_value2, b'plaintext') 159*e7b1675dSTing-Kang Chang 160*e7b1675dSTing-Kang Chang # 3 uses the newer key. So 2, 3 and 4 can verify the mac, but not 1. 161*e7b1675dSTing-Kang Chang mac_value3 = compute_mac3.compute_mac(b'plaintext') 162*e7b1675dSTing-Kang Chang with self.assertRaises(tink.TinkError): 163*e7b1675dSTing-Kang Chang verify_mac1.verify_mac(mac_value3, b'plaintext') 164*e7b1675dSTing-Kang Chang verify_mac2.verify_mac(mac_value3, b'plaintext') 165*e7b1675dSTing-Kang Chang verify_mac3.verify_mac(mac_value3, b'plaintext') 166*e7b1675dSTing-Kang Chang verify_mac4.verify_mac(mac_value3, b'plaintext') 167*e7b1675dSTing-Kang Chang 168*e7b1675dSTing-Kang Chang # 4 uses the newer key. So 2, 3 and 4 can verify the mac, but not 1. 169*e7b1675dSTing-Kang Chang mac_value4 = compute_mac4.compute_mac(b'plaintext') 170*e7b1675dSTing-Kang Chang with self.assertRaises(tink.TinkError): 171*e7b1675dSTing-Kang Chang verify_mac1.verify_mac(mac_value4, b'plaintext') 172*e7b1675dSTing-Kang Chang verify_mac2.verify_mac(mac_value4, b'plaintext') 173*e7b1675dSTing-Kang Chang verify_mac3.verify_mac(mac_value4, b'plaintext') 174*e7b1675dSTing-Kang Chang verify_mac4.verify_mac(mac_value4, b'plaintext') 175*e7b1675dSTing-Kang Chang 176*e7b1675dSTing-Kang Changif __name__ == '__main__': 177*e7b1675dSTing-Kang Chang absltest.main() 178