xref: /linux/scripts/crypto/gen-hash-testvecs.py (revision 950a81224e8bda92813c5ecf851f488c94f06aba)
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