103 lines
3.2 KiB
Python
Executable File
103 lines
3.2 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)
|
|
|
|
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)
|