1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0-or-later 3# 4# Script that generates test vectors for the given cryptographic hash function. 5# 6# Copyright 2025 Google LLC 7 8import hashlib 9import hmac 10import sys 11 12DATA_LENS = [0, 1, 2, 3, 16, 32, 48, 49, 63, 64, 65, 127, 128, 129, 256, 511, 13 513, 1000, 3333, 4096, 4128, 4160, 4224, 16384] 14 15# Generate the given number of random bytes, using the length itself as the seed 16# for a simple linear congruential generator (LCG). The C test code uses the 17# same LCG with the same seeding strategy to reconstruct the data, ensuring 18# reproducibility without explicitly storing the data in the test vectors. 19def rand_bytes(length): 20 seed = length 21 out = [] 22 for _ in range(length): 23 seed = (seed * 25214903917 + 11) % 2**48 24 out.append((seed >> 16) % 256) 25 return bytes(out) 26 27def hash_init(alg): 28 return hashlib.new(alg) 29 30def hash_update(ctx, data): 31 ctx.update(data) 32 33def hash_final(ctx): 34 return ctx.digest() 35 36def compute_hash(alg, data): 37 ctx = hash_init(alg) 38 hash_update(ctx, data) 39 return hash_final(ctx) 40 41def print_bytes(prefix, value, bytes_per_line): 42 for i in range(0, len(value), bytes_per_line): 43 line = prefix + ''.join(f'0x{b:02x}, ' for b in value[i:i+bytes_per_line]) 44 print(f'{line.rstrip()}') 45 46def print_static_u8_array_definition(name, value): 47 print('') 48 print(f'static const u8 {name} = {{') 49 print_bytes('\t', value, 8) 50 print('};') 51 52def print_c_struct_u8_array_field(name, value): 53 print(f'\t\t.{name} = {{') 54 print_bytes('\t\t\t', value, 8) 55 print('\t\t},') 56 57def gen_unkeyed_testvecs(alg): 58 print('') 59 print('static const struct {') 60 print('\tsize_t data_len;') 61 print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];') 62 print('} hash_testvecs[] = {') 63 for data_len in DATA_LENS: 64 data = rand_bytes(data_len) 65 print('\t{') 66 print(f'\t\t.data_len = {data_len},') 67 print_c_struct_u8_array_field('digest', compute_hash(alg, data)) 68 print('\t},') 69 print('};') 70 71 data = rand_bytes(4096) 72 ctx = hash_init(alg) 73 for data_len in range(len(data) + 1): 74 hash_update(ctx, compute_hash(alg, data[:data_len])) 75 print_static_u8_array_definition( 76 f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', 77 hash_final(ctx)) 78 79def gen_hmac_testvecs(alg): 80 ctx = hmac.new(rand_bytes(32), digestmod=alg) 81 data = rand_bytes(4096) 82 for data_len in range(len(data) + 1): 83 ctx.update(data[:data_len]) 84 key_len = data_len % 293 85 key = rand_bytes(key_len) 86 mac = hmac.digest(key, data[:data_len], alg) 87 ctx.update(mac) 88 print_static_u8_array_definition( 89 f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', 90 ctx.digest()) 91 92if len(sys.argv) != 2: 93 sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n') 94 sys.stderr.write('ALGORITHM may be any supported by Python hashlib.\n') 95 sys.stderr.write('Example: gen-hash-testvecs.py sha512\n') 96 sys.exit(1) 97 98alg = sys.argv[1] 99print('/* SPDX-License-Identifier: GPL-2.0-or-later */') 100print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') 101gen_unkeyed_testvecs(alg) 102gen_hmac_testvecs(alg) 103