Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib/crypto: tests: Add KUnit tests for Poly1305

Add a KUnit test suite for the Poly1305 functions. Most of its test
cases are instantiated from hash-test-template.h, which is also used by
the SHA-2 tests. A couple additional test cases are also included to
test edge cases specific to Poly1305.

Acked-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20250709200112.258500-5-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>

+408 -2
+9
lib/crypto/tests/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-or-later 2 2 3 + config CRYPTO_LIB_POLY1305_KUNIT_TEST 4 + tristate "KUnit tests for Poly1305" if !KUNIT_ALL_TESTS 5 + depends on KUNIT 6 + default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS 7 + select CRYPTO_LIB_BENCHMARK_VISIBLE 8 + select CRYPTO_LIB_POLY1305 9 + help 10 + KUnit tests for the Poly1305 library functions. 11 + 3 12 # Option is named *_SHA256_KUNIT_TEST, though both SHA-224 and SHA-256 tests are 4 13 # included, for consistency with the naming used elsewhere (e.g. CRYPTO_SHA256). 5 14 config CRYPTO_LIB_SHA256_KUNIT_TEST
+1
lib/crypto/tests/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-or-later 2 2 3 + obj-$(CONFIG_CRYPTO_LIB_POLY1305_KUNIT_TEST) += poly1305_kunit.o 3 4 obj-$(CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST) += sha224_kunit.o sha256_kunit.o 4 5 obj-$(CONFIG_CRYPTO_LIB_SHA512_KUNIT_TEST) += sha384_kunit.o sha512_kunit.o
+186
lib/crypto/tests/poly1305-testvecs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* This file was generated by: ./scripts/crypto/gen-hash-testvecs.py poly1305 */ 3 + 4 + static const struct { 5 + size_t data_len; 6 + u8 digest[POLY1305_DIGEST_SIZE]; 7 + } hash_testvecs[] = { 8 + { 9 + .data_len = 0, 10 + .digest = { 11 + 0xe8, 0x2d, 0x67, 0x2c, 0x01, 0x48, 0xf9, 0xb7, 12 + 0x87, 0x85, 0x3f, 0xcf, 0x18, 0x66, 0x8c, 0xd3, 13 + }, 14 + }, 15 + { 16 + .data_len = 1, 17 + .digest = { 18 + 0xb8, 0xad, 0xca, 0x6b, 0x32, 0xba, 0x34, 0x42, 19 + 0x54, 0x10, 0x28, 0xf5, 0x0f, 0x7e, 0x8e, 0xe3, 20 + }, 21 + }, 22 + { 23 + .data_len = 2, 24 + .digest = { 25 + 0xb8, 0xf7, 0xf4, 0xc2, 0x85, 0x33, 0x80, 0x63, 26 + 0xd1, 0x45, 0xda, 0xf8, 0x7c, 0x79, 0x42, 0xd1, 27 + }, 28 + }, 29 + { 30 + .data_len = 3, 31 + .digest = { 32 + 0xf3, 0x73, 0x7b, 0x60, 0x24, 0xcc, 0x5d, 0x3e, 33 + 0xd1, 0x95, 0x86, 0xce, 0x89, 0x0a, 0x33, 0xba, 34 + }, 35 + }, 36 + { 37 + .data_len = 16, 38 + .digest = { 39 + 0x0a, 0x1a, 0x2d, 0x39, 0xea, 0x49, 0x8f, 0xb7, 40 + 0x90, 0xb6, 0x74, 0x3b, 0x41, 0x3b, 0x96, 0x11, 41 + }, 42 + }, 43 + { 44 + .data_len = 32, 45 + .digest = { 46 + 0x99, 0x05, 0xe3, 0xa7, 0x9e, 0x2a, 0xd2, 0x42, 47 + 0xb9, 0x45, 0x0c, 0x08, 0xe7, 0x10, 0xe4, 0xe1, 48 + }, 49 + }, 50 + { 51 + .data_len = 48, 52 + .digest = { 53 + 0xe1, 0xb2, 0x15, 0xee, 0xa2, 0xf3, 0x04, 0xac, 54 + 0xdd, 0x27, 0x57, 0x95, 0x2f, 0x45, 0xa8, 0xd3, 55 + }, 56 + }, 57 + { 58 + .data_len = 49, 59 + .digest = { 60 + 0x1c, 0xf3, 0xab, 0x39, 0xc0, 0x69, 0x49, 0x69, 61 + 0x89, 0x6f, 0x1f, 0x03, 0x16, 0xe7, 0xc0, 0xf0, 62 + }, 63 + }, 64 + { 65 + .data_len = 63, 66 + .digest = { 67 + 0x30, 0xb0, 0x32, 0x87, 0x51, 0x55, 0x9c, 0x39, 68 + 0x38, 0x42, 0x06, 0xe9, 0x2a, 0x3e, 0x2c, 0x92, 69 + }, 70 + }, 71 + { 72 + .data_len = 64, 73 + .digest = { 74 + 0x2c, 0x04, 0x16, 0x36, 0x55, 0x25, 0x2d, 0xc6, 75 + 0x3d, 0x70, 0x5b, 0x88, 0x46, 0xb6, 0x71, 0x77, 76 + }, 77 + }, 78 + { 79 + .data_len = 65, 80 + .digest = { 81 + 0x03, 0x87, 0xdd, 0xbe, 0xe8, 0x30, 0xf2, 0x15, 82 + 0x40, 0x44, 0x29, 0x7b, 0xb1, 0xe9, 0x9d, 0xe7, 83 + }, 84 + }, 85 + { 86 + .data_len = 127, 87 + .digest = { 88 + 0x29, 0x83, 0x4f, 0xcb, 0x5a, 0x93, 0x25, 0xad, 89 + 0x05, 0xa4, 0xb3, 0x24, 0x77, 0x62, 0x2d, 0x3d, 90 + }, 91 + }, 92 + { 93 + .data_len = 128, 94 + .digest = { 95 + 0x20, 0x0e, 0x2c, 0x05, 0xe2, 0x0b, 0x85, 0xa0, 96 + 0x24, 0x73, 0x7f, 0x65, 0x70, 0x6c, 0x3e, 0xb0, 97 + }, 98 + }, 99 + { 100 + .data_len = 129, 101 + .digest = { 102 + 0xef, 0x2f, 0x98, 0x42, 0xc2, 0x90, 0x55, 0xea, 103 + 0xba, 0x28, 0x76, 0xfd, 0x9e, 0x3e, 0x4d, 0x53, 104 + }, 105 + }, 106 + { 107 + .data_len = 256, 108 + .digest = { 109 + 0x9e, 0x75, 0x4b, 0xc7, 0x69, 0x68, 0x51, 0x90, 110 + 0xdc, 0x29, 0xc8, 0xfa, 0x86, 0xf1, 0xc9, 0xb3, 111 + }, 112 + }, 113 + { 114 + .data_len = 511, 115 + .digest = { 116 + 0x9d, 0x13, 0xf5, 0x54, 0xe6, 0xe3, 0x45, 0x38, 117 + 0x8b, 0x6d, 0x5c, 0xc4, 0x50, 0xeb, 0x90, 0xcb, 118 + }, 119 + }, 120 + { 121 + .data_len = 513, 122 + .digest = { 123 + 0xaa, 0xb2, 0x3e, 0x3c, 0x2a, 0xfc, 0x62, 0x0e, 124 + 0xd4, 0xe6, 0xe5, 0x5c, 0x6b, 0x9f, 0x3d, 0xc7, 125 + }, 126 + }, 127 + { 128 + .data_len = 1000, 129 + .digest = { 130 + 0xd6, 0x8c, 0xea, 0x8a, 0x0f, 0x68, 0xa9, 0xa8, 131 + 0x67, 0x86, 0xf9, 0xc1, 0x4c, 0x26, 0x10, 0x6d, 132 + }, 133 + }, 134 + { 135 + .data_len = 3333, 136 + .digest = { 137 + 0xdc, 0xc1, 0x54, 0xe7, 0x38, 0xc6, 0xdb, 0x24, 138 + 0xa7, 0x0b, 0xff, 0xd3, 0x1b, 0x93, 0x01, 0xa6, 139 + }, 140 + }, 141 + { 142 + .data_len = 4096, 143 + .digest = { 144 + 0x8f, 0x88, 0x3e, 0x9c, 0x7b, 0x2e, 0x82, 0x5a, 145 + 0x1d, 0x31, 0x82, 0xcc, 0x69, 0xb4, 0x16, 0x26, 146 + }, 147 + }, 148 + { 149 + .data_len = 4128, 150 + .digest = { 151 + 0x23, 0x45, 0x94, 0xa8, 0x11, 0x54, 0x9d, 0xf2, 152 + 0xa1, 0x9a, 0xca, 0xf9, 0x3e, 0x65, 0x52, 0xfd, 153 + }, 154 + }, 155 + { 156 + .data_len = 4160, 157 + .digest = { 158 + 0x7b, 0xfc, 0xa9, 0x1e, 0x03, 0xad, 0xef, 0x03, 159 + 0xe2, 0x20, 0x92, 0xc7, 0x54, 0x83, 0xfa, 0x37, 160 + }, 161 + }, 162 + { 163 + .data_len = 4224, 164 + .digest = { 165 + 0x46, 0xab, 0x8c, 0x75, 0xb3, 0x10, 0xa6, 0x3f, 166 + 0x74, 0x55, 0x42, 0x6d, 0x69, 0x35, 0xd2, 0xf5, 167 + }, 168 + }, 169 + { 170 + .data_len = 16384, 171 + .digest = { 172 + 0xd0, 0xfe, 0x26, 0xc2, 0xca, 0x94, 0x2d, 0x52, 173 + 0x2d, 0xe1, 0x11, 0xdd, 0x42, 0x28, 0x83, 0xa6, 174 + }, 175 + }, 176 + }; 177 + 178 + static const u8 hash_testvec_consolidated[POLY1305_DIGEST_SIZE] = { 179 + 0x9d, 0x07, 0x5d, 0xc9, 0x6c, 0xeb, 0x62, 0x5d, 180 + 0x02, 0x5f, 0xe1, 0xe3, 0xd1, 0x71, 0x69, 0x34, 181 + }; 182 + 183 + static const u8 poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE] = { 184 + 0x0c, 0x26, 0x6b, 0x45, 0x87, 0x06, 0xcf, 0xc4, 185 + 0x3f, 0x70, 0x7d, 0xb3, 0x50, 0xdd, 0x81, 0x25, 186 + };
+165
lib/crypto/tests/poly1305_kunit.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright 2025 Google LLC 4 + */ 5 + #include <crypto/poly1305.h> 6 + #include "poly1305-testvecs.h" 7 + 8 + /* 9 + * A fixed key used when presenting Poly1305 as an unkeyed hash function in 10 + * order to reuse hash-test-template.h. At the beginning of the test suite, 11 + * this is initialized to bytes generated from a fixed seed. 12 + */ 13 + static u8 test_key[POLY1305_KEY_SIZE]; 14 + 15 + /* This probably should be in the actual API, but just define it here for now */ 16 + static void poly1305(const u8 key[POLY1305_KEY_SIZE], const u8 *data, 17 + size_t len, u8 out[POLY1305_DIGEST_SIZE]) 18 + { 19 + struct poly1305_desc_ctx ctx; 20 + 21 + poly1305_init(&ctx, key); 22 + poly1305_update(&ctx, data, len); 23 + poly1305_final(&ctx, out); 24 + } 25 + 26 + static void poly1305_init_withtestkey(struct poly1305_desc_ctx *ctx) 27 + { 28 + poly1305_init(ctx, test_key); 29 + } 30 + 31 + static void poly1305_withtestkey(const u8 *data, size_t len, 32 + u8 out[POLY1305_DIGEST_SIZE]) 33 + { 34 + poly1305(test_key, data, len, out); 35 + } 36 + 37 + /* Generate the HASH_KUNIT_CASES using hash-test-template.h. */ 38 + #define HASH poly1305_withtestkey 39 + #define HASH_CTX poly1305_desc_ctx 40 + #define HASH_SIZE POLY1305_DIGEST_SIZE 41 + #define HASH_INIT poly1305_init_withtestkey 42 + #define HASH_UPDATE poly1305_update 43 + #define HASH_FINAL poly1305_final 44 + #include "hash-test-template.h" 45 + 46 + static int poly1305_suite_init(struct kunit_suite *suite) 47 + { 48 + rand_bytes_seeded_from_len(test_key, POLY1305_KEY_SIZE); 49 + return hash_suite_init(suite); 50 + } 51 + 52 + static void poly1305_suite_exit(struct kunit_suite *suite) 53 + { 54 + hash_suite_exit(suite); 55 + } 56 + 57 + /* 58 + * Poly1305 test case which uses a key and message consisting only of one bits: 59 + * 60 + * - Using an all-one-bits r_key tests the key clamping. 61 + * - Using an all-one-bits s_key tests carries in implementations of the 62 + * addition mod 2**128 during finalization. 63 + * - Using all-one-bits message, and to a lesser extent r_key, tends to maximize 64 + * any intermediate accumulator values. This increases the chance of 65 + * detecting bugs that occur only in rare cases where the accumulator values 66 + * get very large, for example the bug fixed by commit 678cce4019d746da 67 + * ("crypto: x86/poly1305 - fix overflow during partial reduction"). 68 + * 69 + * Accumulator overflow bugs may be specific to particular update lengths (in 70 + * blocks) and/or particular values of the previous acculumator. Note that the 71 + * accumulator starts at 0 which gives the lowest chance of an overflow. Thus, 72 + * a single all-one-bits test vector may be insufficient. 73 + * 74 + * Considering that, do the following test: continuously update a single 75 + * Poly1305 context with all-one-bits data of varying lengths (0, 16, 32, ..., 76 + * 4096 bytes). After each update, generate the MAC from the current context, 77 + * and feed that MAC into a separate Poly1305 context. Repeat that entire 78 + * sequence of updates 32 times without re-initializing either context, 79 + * resulting in a total of 8224 MAC computations from a long-running, cumulative 80 + * context. Finally, generate and verify the MAC of all the MACs. 81 + */ 82 + static void test_poly1305_allones_keys_and_message(struct kunit *test) 83 + { 84 + struct poly1305_desc_ctx mac_ctx, macofmacs_ctx; 85 + u8 mac[POLY1305_DIGEST_SIZE]; 86 + 87 + static_assert(TEST_BUF_LEN >= 4096); 88 + memset(test_buf, 0xff, 4096); 89 + 90 + poly1305_init(&mac_ctx, test_buf); 91 + poly1305_init(&macofmacs_ctx, test_buf); 92 + for (int i = 0; i < 32; i++) { 93 + for (size_t len = 0; len <= 4096; len += 16) { 94 + struct poly1305_desc_ctx tmp_ctx; 95 + 96 + poly1305_update(&mac_ctx, test_buf, len); 97 + tmp_ctx = mac_ctx; 98 + poly1305_final(&tmp_ctx, mac); 99 + poly1305_update(&macofmacs_ctx, mac, 100 + POLY1305_DIGEST_SIZE); 101 + } 102 + } 103 + poly1305_final(&macofmacs_ctx, mac); 104 + KUNIT_ASSERT_MEMEQ(test, mac, poly1305_allones_macofmacs, 105 + POLY1305_DIGEST_SIZE); 106 + } 107 + 108 + /* 109 + * Poly1305 test case which uses r_key=1, s_key=0, and a 48-byte message 110 + * consisting of three blocks with integer values [2**128 - i, 0, 0]. In this 111 + * case, the result of the polynomial evaluation is 2**130 - i. For small 112 + * values of i, this is very close to the modulus 2**130 - 5, which helps catch 113 + * edge case bugs in the modular reduction logic. 114 + */ 115 + static void test_poly1305_reduction_edge_cases(struct kunit *test) 116 + { 117 + static const u8 key[POLY1305_KEY_SIZE] = { 1 }; /* r_key=1, s_key=0 */ 118 + u8 data[3 * POLY1305_BLOCK_SIZE] = {}; 119 + u8 expected_mac[POLY1305_DIGEST_SIZE]; 120 + u8 actual_mac[POLY1305_DIGEST_SIZE]; 121 + 122 + for (int i = 1; i <= 10; i++) { 123 + /* Set the first data block to 2**128 - i. */ 124 + data[0] = -i; 125 + memset(&data[1], 0xff, POLY1305_BLOCK_SIZE - 1); 126 + 127 + /* 128 + * Assuming s_key=0, the expected MAC as an integer is 129 + * (2**130 - i mod 2**130 - 5) + 0 mod 2**128. If 1 <= i <= 5, 130 + * that's 5 - i. If 6 <= i <= 10, that's 2**128 - i. 131 + */ 132 + if (i <= 5) { 133 + expected_mac[0] = 5 - i; 134 + memset(&expected_mac[1], 0, POLY1305_DIGEST_SIZE - 1); 135 + } else { 136 + expected_mac[0] = -i; 137 + memset(&expected_mac[1], 0xff, 138 + POLY1305_DIGEST_SIZE - 1); 139 + } 140 + 141 + /* Compute and verify the MAC. */ 142 + poly1305(key, data, sizeof(data), actual_mac); 143 + KUNIT_ASSERT_MEMEQ(test, actual_mac, expected_mac, 144 + POLY1305_DIGEST_SIZE); 145 + } 146 + } 147 + 148 + static struct kunit_case poly1305_test_cases[] = { 149 + HASH_KUNIT_CASES, 150 + KUNIT_CASE(test_poly1305_allones_keys_and_message), 151 + KUNIT_CASE(test_poly1305_reduction_edge_cases), 152 + KUNIT_CASE(benchmark_hash), 153 + {}, 154 + }; 155 + 156 + static struct kunit_suite poly1305_test_suite = { 157 + .name = "poly1305", 158 + .test_cases = poly1305_test_cases, 159 + .suite_init = poly1305_suite_init, 160 + .suite_exit = poly1305_suite_exit, 161 + }; 162 + kunit_test_suite(poly1305_test_suite); 163 + 164 + MODULE_DESCRIPTION("KUnit tests and benchmark for Poly1305"); 165 + MODULE_LICENSE("GPL");
+47 -2
scripts/crypto/gen-hash-testvecs.py
··· 24 24 out.append((seed >> 16) % 256) 25 25 return bytes(out) 26 26 27 + POLY1305_KEY_SIZE = 32 28 + 29 + # A straightforward, unoptimized implementation of Poly1305. 30 + # Reference: https://cr.yp.to/mac/poly1305-20050329.pdf 31 + class 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 + 27 53 def 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)) 28 58 return hashlib.new(alg) 29 59 30 60 def hash_update(ctx, data): ··· 119 89 f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', 120 90 ctx.digest()) 121 91 92 + def gen_additional_poly1305_testvecs(): 93 + key = b'\xff' * POLY1305_KEY_SIZE 94 + data = b'' 95 + ctx = Poly1305(key) 96 + for _ in range(32): 97 + for j in range(0, 4097, 16): 98 + ctx.update(b'\xff' * j) 99 + data += ctx.digest() 100 + print_static_u8_array_definition( 101 + 'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]', 102 + Poly1305(key).update(data).digest()) 103 + 122 104 if len(sys.argv) != 2: 123 105 sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n') 124 - sys.stderr.write('ALGORITHM may be any supported by Python hashlib.\n') 106 + sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n') 125 107 sys.stderr.write('Example: gen-hash-testvecs.py sha512\n') 126 108 sys.exit(1) 127 109 ··· 141 99 print('/* SPDX-License-Identifier: GPL-2.0-or-later */') 142 100 print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') 143 101 gen_unkeyed_testvecs(alg) 144 - gen_hmac_testvecs(alg) 102 + if alg == 'poly1305': 103 + gen_additional_poly1305_testvecs() 104 + else: 105 + gen_hmac_testvecs(alg)