Merge tag 'libcrypto-tests-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux
Pull crypto library test updates from Eric Biggers: "Add KUnit test suites for the Poly1305, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512 library functions. These are the first KUnit tests for lib/crypto/. So in addition to being useful tests for these specific algorithms, they also establish some conventions for lib/crypto/ testing going forwards. The new tests are fairly comprehensive: more comprehensive than the generic crypto infrastructure's tests. They use a variety of techniques to check for the types of implementation bugs that tend to occur in the real world, rather than just naively checking some test vectors. (Interestingly, poly1305_kunit found a bug in QEMU) The core test logic is shared by all six algorithms, rather than being duplicated for each algorithm. Each algorithm's test suite also optionally includes a benchmark" * tag 'libcrypto-tests-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux: lib/crypto: tests: Annotate worker to be on stack lib/crypto: tests: Add KUnit tests for SHA-1 and HMAC-SHA1 lib/crypto: tests: Add KUnit tests for Poly1305 lib/crypto: tests: Add KUnit tests for SHA-384 and SHA-512 lib/crypto: tests: Add KUnit tests for SHA-224 and SHA-256 lib/crypto: tests: Add hash-test-template.h and gen-hash-testvecs.py
This commit is contained in:
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/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)
|
||||
|
||||
POLY1305_KEY_SIZE = 32
|
||||
|
||||
# A straightforward, unoptimized implementation of Poly1305.
|
||||
# Reference: https://cr.yp.to/mac/poly1305-20050329.pdf
|
||||
class Poly1305:
|
||||
def __init__(self, key):
|
||||
assert len(key) == POLY1305_KEY_SIZE
|
||||
self.h = 0
|
||||
rclamp = 0x0ffffffc0ffffffc0ffffffc0fffffff
|
||||
self.r = int.from_bytes(key[:16], byteorder='little') & rclamp
|
||||
self.s = int.from_bytes(key[16:], byteorder='little')
|
||||
|
||||
# Note: this supports partial blocks only at the end.
|
||||
def update(self, data):
|
||||
for i in range(0, len(data), 16):
|
||||
chunk = data[i:i+16]
|
||||
c = int.from_bytes(chunk, byteorder='little') + 2**(8 * len(chunk))
|
||||
self.h = ((self.h + c) * self.r) % (2**130 - 5)
|
||||
return self
|
||||
|
||||
# Note: gen_additional_poly1305_testvecs() relies on this being
|
||||
# nondestructive, i.e. not changing any field of self.
|
||||
def digest(self):
|
||||
m = (self.h + self.s) % 2**128
|
||||
return m.to_bytes(16, byteorder='little')
|
||||
|
||||
def hash_init(alg):
|
||||
if alg == 'poly1305':
|
||||
# Use a fixed random key here, to present Poly1305 as an unkeyed hash.
|
||||
# This allows all the test cases for unkeyed hashes to work on Poly1305.
|
||||
return Poly1305(rand_bytes(POLY1305_KEY_SIZE))
|
||||
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())
|
||||
|
||||
def gen_additional_poly1305_testvecs():
|
||||
key = b'\xff' * POLY1305_KEY_SIZE
|
||||
data = b''
|
||||
ctx = Poly1305(key)
|
||||
for _ in range(32):
|
||||
for j in range(0, 4097, 16):
|
||||
ctx.update(b'\xff' * j)
|
||||
data += ctx.digest()
|
||||
print_static_u8_array_definition(
|
||||
'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]',
|
||||
Poly1305(key).update(data).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, or poly1305.\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)
|
||||
if alg == 'poly1305':
|
||||
gen_additional_poly1305_testvecs()
|
||||
else:
|
||||
gen_hmac_testvecs(alg)
|
||||
Reference in New Issue
Block a user