xref: /aosp_15_r20/external/tink/testing/cross_language/prf_set_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 PrfSet primitive."""
15
16from absl.testing import absltest
17from absl.testing import parameterized
18
19import tink
20from tink import prf
21
22from tink.testing import keyset_builder
23from util import testing_servers
24from util import utilities
25
26SUPPORTED_LANGUAGES = testing_servers.SUPPORTED_LANGUAGES_BY_PRIMITIVE['prf']
27
28OUTPUT_LENGTHS = [
29    1, 2, 5, 10, 16, 17, 20, 32, 33, 48, 64, 65, 100, 256, 512, 1024
30]
31
32
33def all_prf_key_template_names_with_some_output_length():
34  """Yields (prf_key_template_name, output_length) tuples."""
35  for key_template_name in utilities.tinkey_template_names_for(prf.PrfSet):
36    for output_length in OUTPUT_LENGTHS:
37      yield (key_template_name, output_length)
38
39
40def gen_keyset(key_template_name: str) -> bytes:
41  builder = keyset_builder.new_keyset_builder()
42  primary_key_id = builder.add_new_key(
43      utilities.KEY_TEMPLATE[key_template_name])
44  builder.set_primary_key(primary_key_id)
45  return builder.keyset()
46
47
48def gen_keyset_with_2_prfs() -> bytes:
49  builder = keyset_builder.new_keyset_builder()
50  builder.add_new_key(prf.prf_key_templates.HMAC_SHA256)
51  primary_key_id = builder.add_new_key(prf.prf_key_templates.HKDF_SHA256)
52  builder.set_primary_key(primary_key_id)
53  return builder.keyset()
54
55
56def setUpModule():
57  prf.register()
58  testing_servers.start('prf_set')
59
60
61def tearDownModule():
62  testing_servers.stop()
63
64
65class PrfSetPythonTest(parameterized.TestCase):
66
67  @parameterized.parameters(utilities.tinkey_template_names_for(prf.PrfSet))
68  def test_supported(self, key_template_name):
69    supported_langs = utilities.SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[
70        key_template_name]
71    self.assertNotEmpty(supported_langs)
72    keyset = gen_keyset(key_template_name)
73    input_data = b'This is some input data.'
74    outputs = []
75    for lang in supported_langs:
76      p = testing_servers.remote_primitive(lang, keyset, prf.PrfSet)
77      outputs.append(p.primary().compute(input_data, 16))
78    self.assertLen(outputs, len(supported_langs))
79    self.assertLen(outputs[0], 16)
80    self.assertLen(set(outputs), 1)
81
82  @parameterized.parameters(
83      all_prf_key_template_names_with_some_output_length())
84  def test_compute_consistent_for_output_length(self, key_template_name,
85                                                output_length):
86    supported_langs = utilities.SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[
87        key_template_name]
88    # This test checks that for a given output_length, either all
89    # implementations fail or all produce the same value.
90    self.assertNotEmpty(supported_langs)
91    keyset = gen_keyset(key_template_name)
92    input_data = b'This is some input data.'
93    errors = {}
94    outputs = {}
95    for lang in supported_langs:
96      try:
97        p = testing_servers.remote_primitive(lang, keyset, prf.PrfSet)
98        outputs[lang] = p.primary().compute(input_data, output_length)
99      except tink.TinkError as e:
100        errors[lang] = e
101    inconsistent_errors = bool(errors) and bool(outputs)
102    inconsistent_output_values = len(set(outputs.values())) > 1
103    if inconsistent_errors or inconsistent_output_values:
104      self.fail('The PRF for template %s and output_length=%d is inconsistent: '
105                'outputs = %s, errors = %s.' %
106                (key_template_name, output_length, outputs, errors))
107
108  @parameterized.parameters(SUPPORTED_LANGUAGES)
109  def test_multiple_prfs(self, lang):
110    keyset = gen_keyset_with_2_prfs()
111    input_data = b'This is some input data.'
112    output_length = 15
113    p = testing_servers.remote_primitive(lang, keyset, prf.PrfSet)
114    primary_output = p.primary().compute(input_data, output_length)
115    primary_id = p.primary_id()
116    all_outputs = {
117        key_id: f.compute(input_data, output_length)
118        for key_id, f in p.all().items()
119    }
120    self.assertLen(all_outputs, 2)
121    self.assertEqual(all_outputs[primary_id], primary_output)
122
123
124if __name__ == '__main__':
125  absltest.main()
126