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