#!/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) def hash_init(alg): 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()) 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.\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) gen_hmac_testvecs(alg)