Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
27POLY1305_KEY_SIZE = 32
28
29# A straightforward, unoptimized implementation of Poly1305.
30# Reference: https://cr.yp.to/mac/poly1305-20050329.pdf
31class Poly1305:
32 def __init__(self, key):
33 assert len(key) == POLY1305_KEY_SIZE
34 self.h = 0
35 rclamp = 0x0ffffffc0ffffffc0ffffffc0fffffff
36 self.r = int.from_bytes(key[:16], byteorder='little') & rclamp
37 self.s = int.from_bytes(key[16:], byteorder='little')
38
39 # Note: this supports partial blocks only at the end.
40 def update(self, data):
41 for i in range(0, len(data), 16):
42 chunk = data[i:i+16]
43 c = int.from_bytes(chunk, byteorder='little') + 2**(8 * len(chunk))
44 self.h = ((self.h + c) * self.r) % (2**130 - 5)
45 return self
46
47 # Note: gen_additional_poly1305_testvecs() relies on this being
48 # nondestructive, i.e. not changing any field of self.
49 def digest(self):
50 m = (self.h + self.s) % 2**128
51 return m.to_bytes(16, byteorder='little')
52
53def hash_init(alg):
54 if alg == 'poly1305':
55 # Use a fixed random key here, to present Poly1305 as an unkeyed hash.
56 # This allows all the test cases for unkeyed hashes to work on Poly1305.
57 return Poly1305(rand_bytes(POLY1305_KEY_SIZE))
58 return hashlib.new(alg)
59
60def hash_update(ctx, data):
61 ctx.update(data)
62
63def hash_final(ctx):
64 return ctx.digest()
65
66def compute_hash(alg, data):
67 ctx = hash_init(alg)
68 hash_update(ctx, data)
69 return hash_final(ctx)
70
71def print_bytes(prefix, value, bytes_per_line):
72 for i in range(0, len(value), bytes_per_line):
73 line = prefix + ''.join(f'0x{b:02x}, ' for b in value[i:i+bytes_per_line])
74 print(f'{line.rstrip()}')
75
76def print_static_u8_array_definition(name, value):
77 print('')
78 print(f'static const u8 {name} = {{')
79 print_bytes('\t', value, 8)
80 print('};')
81
82def print_c_struct_u8_array_field(name, value):
83 print(f'\t\t.{name} = {{')
84 print_bytes('\t\t\t', value, 8)
85 print('\t\t},')
86
87def gen_unkeyed_testvecs(alg):
88 print('')
89 print('static const struct {')
90 print('\tsize_t data_len;')
91 print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];')
92 print('} hash_testvecs[] = {')
93 for data_len in DATA_LENS:
94 data = rand_bytes(data_len)
95 print('\t{')
96 print(f'\t\t.data_len = {data_len},')
97 print_c_struct_u8_array_field('digest', compute_hash(alg, data))
98 print('\t},')
99 print('};')
100
101 data = rand_bytes(4096)
102 ctx = hash_init(alg)
103 for data_len in range(len(data) + 1):
104 hash_update(ctx, compute_hash(alg, data[:data_len]))
105 print_static_u8_array_definition(
106 f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
107 hash_final(ctx))
108
109def gen_hmac_testvecs(alg):
110 ctx = hmac.new(rand_bytes(32), digestmod=alg)
111 data = rand_bytes(4096)
112 for data_len in range(len(data) + 1):
113 ctx.update(data[:data_len])
114 key_len = data_len % 293
115 key = rand_bytes(key_len)
116 mac = hmac.digest(key, data[:data_len], alg)
117 ctx.update(mac)
118 print_static_u8_array_definition(
119 f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
120 ctx.digest())
121
122def gen_additional_poly1305_testvecs():
123 key = b'\xff' * POLY1305_KEY_SIZE
124 data = b''
125 ctx = Poly1305(key)
126 for _ in range(32):
127 for j in range(0, 4097, 16):
128 ctx.update(b'\xff' * j)
129 data += ctx.digest()
130 print_static_u8_array_definition(
131 'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]',
132 Poly1305(key).update(data).digest())
133
134if len(sys.argv) != 2:
135 sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n')
136 sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n')
137 sys.stderr.write('Example: gen-hash-testvecs.py sha512\n')
138 sys.exit(1)
139
140alg = sys.argv[1]
141print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
142print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */')
143gen_unkeyed_testvecs(alg)
144if alg == 'poly1305':
145 gen_additional_poly1305_testvecs()
146else:
147 gen_hmac_testvecs(alg)