mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	 6dd4d9f791
			
		
	
	
		6dd4d9f791
		
	
	
	
	
		
			
			Add a KUnit test suite for the Poly1305 functions. Most of its test cases are instantiated from hash-test-template.h, which is also used by the SHA-2 tests. A couple additional test cases are also included to test edge cases specific to Poly1305. Acked-by: Ard Biesheuvel <ardb@kernel.org> Link: https://lore.kernel.org/r/20250709200112.258500-5-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org>
		
			
				
	
	
		
			147 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| # SPDX-License-Identifier: GPL-2.0-or-later
 | |
| #
 | |
| # Script that generates test vectors for the given cryptographic hash function.
 | |
| #
 | |
| # Copyright 2025 Google LLC
 | |
| 
 | |
| import hashlib
 | |
| import hmac
 | |
| import sys
 | |
| 
 | |
| DATA_LENS = [0, 1, 2, 3, 16, 32, 48, 49, 63, 64, 65, 127, 128, 129, 256, 511,
 | |
|              513, 1000, 3333, 4096, 4128, 4160, 4224, 16384]
 | |
| 
 | |
| # Generate the given number of random bytes, using the length itself as the seed
 | |
| # for a simple linear congruential generator (LCG).  The C test code uses the
 | |
| # same LCG with the same seeding strategy to reconstruct the data, ensuring
 | |
| # reproducibility without explicitly storing the data in the test vectors.
 | |
| def rand_bytes(length):
 | |
|     seed = length
 | |
|     out = []
 | |
|     for _ in range(length):
 | |
|         seed = (seed * 25214903917 + 11) % 2**48
 | |
|         out.append((seed >> 16) % 256)
 | |
|     return bytes(out)
 | |
| 
 | |
| POLY1305_KEY_SIZE = 32
 | |
| 
 | |
| # A straightforward, unoptimized implementation of Poly1305.
 | |
| # Reference: https://cr.yp.to/mac/poly1305-20050329.pdf
 | |
| class Poly1305:
 | |
|     def __init__(self, key):
 | |
|         assert len(key) == POLY1305_KEY_SIZE
 | |
|         self.h = 0
 | |
|         rclamp = 0x0ffffffc0ffffffc0ffffffc0fffffff
 | |
|         self.r = int.from_bytes(key[:16], byteorder='little') & rclamp
 | |
|         self.s = int.from_bytes(key[16:], byteorder='little')
 | |
| 
 | |
|     # Note: this supports partial blocks only at the end.
 | |
|     def update(self, data):
 | |
|         for i in range(0, len(data), 16):
 | |
|             chunk = data[i:i+16]
 | |
|             c = int.from_bytes(chunk, byteorder='little') + 2**(8 * len(chunk))
 | |
|             self.h = ((self.h + c) * self.r) % (2**130 - 5)
 | |
|         return self
 | |
| 
 | |
|     # Note: gen_additional_poly1305_testvecs() relies on this being
 | |
|     # nondestructive, i.e. not changing any field of self.
 | |
|     def digest(self):
 | |
|         m = (self.h + self.s) % 2**128
 | |
|         return m.to_bytes(16, byteorder='little')
 | |
| 
 | |
| def hash_init(alg):
 | |
|     if alg == 'poly1305':
 | |
|         # Use a fixed random key here, to present Poly1305 as an unkeyed hash.
 | |
|         # This allows all the test cases for unkeyed hashes to work on Poly1305.
 | |
|         return Poly1305(rand_bytes(POLY1305_KEY_SIZE))
 | |
|     return hashlib.new(alg)
 | |
| 
 | |
| def hash_update(ctx, data):
 | |
|     ctx.update(data)
 | |
| 
 | |
| def hash_final(ctx):
 | |
|     return ctx.digest()
 | |
| 
 | |
| def compute_hash(alg, data):
 | |
|     ctx = hash_init(alg)
 | |
|     hash_update(ctx, data)
 | |
|     return hash_final(ctx)
 | |
| 
 | |
| def print_bytes(prefix, value, bytes_per_line):
 | |
|     for i in range(0, len(value), bytes_per_line):
 | |
|         line = prefix + ''.join(f'0x{b:02x}, ' for b in value[i:i+bytes_per_line])
 | |
|         print(f'{line.rstrip()}')
 | |
| 
 | |
| def print_static_u8_array_definition(name, value):
 | |
|     print('')
 | |
|     print(f'static const u8 {name} = {{')
 | |
|     print_bytes('\t', value, 8)
 | |
|     print('};')
 | |
| 
 | |
| def print_c_struct_u8_array_field(name, value):
 | |
|     print(f'\t\t.{name} = {{')
 | |
|     print_bytes('\t\t\t', value, 8)
 | |
|     print('\t\t},')
 | |
| 
 | |
| def gen_unkeyed_testvecs(alg):
 | |
|     print('')
 | |
|     print('static const struct {')
 | |
|     print('\tsize_t data_len;')
 | |
|     print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];')
 | |
|     print('} hash_testvecs[] = {')
 | |
|     for data_len in DATA_LENS:
 | |
|         data = rand_bytes(data_len)
 | |
|         print('\t{')
 | |
|         print(f'\t\t.data_len = {data_len},')
 | |
|         print_c_struct_u8_array_field('digest', compute_hash(alg, data))
 | |
|         print('\t},')
 | |
|     print('};')
 | |
| 
 | |
|     data = rand_bytes(4096)
 | |
|     ctx = hash_init(alg)
 | |
|     for data_len in range(len(data) + 1):
 | |
|         hash_update(ctx, compute_hash(alg, data[:data_len]))
 | |
|     print_static_u8_array_definition(
 | |
|             f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
 | |
|             hash_final(ctx))
 | |
| 
 | |
| def gen_hmac_testvecs(alg):
 | |
|     ctx = hmac.new(rand_bytes(32), digestmod=alg)
 | |
|     data = rand_bytes(4096)
 | |
|     for data_len in range(len(data) + 1):
 | |
|         ctx.update(data[:data_len])
 | |
|         key_len = data_len % 293
 | |
|         key = rand_bytes(key_len)
 | |
|         mac = hmac.digest(key, data[:data_len], alg)
 | |
|         ctx.update(mac)
 | |
|     print_static_u8_array_definition(
 | |
|             f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
 | |
|             ctx.digest())
 | |
| 
 | |
| def gen_additional_poly1305_testvecs():
 | |
|     key = b'\xff' * POLY1305_KEY_SIZE
 | |
|     data = b''
 | |
|     ctx = Poly1305(key)
 | |
|     for _ in range(32):
 | |
|         for j in range(0, 4097, 16):
 | |
|             ctx.update(b'\xff' * j)
 | |
|             data += ctx.digest()
 | |
|     print_static_u8_array_definition(
 | |
|             'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]',
 | |
|             Poly1305(key).update(data).digest())
 | |
| 
 | |
| if len(sys.argv) != 2:
 | |
|     sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n')
 | |
|     sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n')
 | |
|     sys.stderr.write('Example: gen-hash-testvecs.py sha512\n')
 | |
|     sys.exit(1)
 | |
| 
 | |
| alg = sys.argv[1]
 | |
| print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
 | |
| print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */')
 | |
| gen_unkeyed_testvecs(alg)
 | |
| if alg == 'poly1305':
 | |
|     gen_additional_poly1305_testvecs()
 | |
| else:
 | |
|     gen_hmac_testvecs(alg)
 |