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