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

lib/crypto: tests: Add additional SHAKE tests

Add the following test cases to cover gaps in the SHAKE testing:

- test_shake_all_lens_up_to_4096()
- test_shake_multiple_squeezes()
- test_shake_with_guarded_bufs()

Remove test_shake256_tiling() and test_shake256_tiling2() since they are
superseded by test_shake_multiple_squeezes(). It provides better test
coverage by using randomized testing. E.g., it's able to generate a
zero-length squeeze followed by a nonzero-length squeeze, which the
first 7 versions of the SHA-3 patchset handled incorrectly.

Tested-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20251026055032.1413733-7-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@kernel.org>

+172 -57
+19 -1
lib/crypto/tests/sha3-testvecs.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 - /* This file was generated by: ./scripts/crypto/gen-hash-testvecs.py sha3-256 */ 2 + /* This file was generated by: ./scripts/crypto/gen-hash-testvecs.py sha3 */ 3 + 4 + /* SHA3-256 test vectors */ 3 5 4 6 static const struct { 5 7 size_t data_len; ··· 230 228 0xdd, 0xbe, 0x72, 0x15, 0xbd, 0x6f, 0xfa, 0xe5, 231 229 0x5e, 0xab, 0x9f, 0xb1, 0xe4, 0x23, 0x7c, 0x2c, 232 230 0x80, 0xcf, 0x09, 0x75, 0xf8, 0xe2, 0xfa, 0x30, 231 + }; 232 + 233 + /* SHAKE test vectors */ 234 + 235 + static const u8 shake128_testvec_consolidated[SHA3_256_DIGEST_SIZE] = { 236 + 0x89, 0x88, 0x3a, 0x44, 0xec, 0xfe, 0x3c, 0xeb, 237 + 0x2f, 0x1c, 0x1d, 0xda, 0x9e, 0x36, 0x64, 0xf0, 238 + 0x85, 0x4c, 0x49, 0x12, 0x76, 0x5a, 0x4d, 0xe7, 239 + 0xa8, 0xfd, 0xcd, 0xbe, 0x45, 0xb4, 0x6f, 0xb0, 240 + }; 241 + 242 + static const u8 shake256_testvec_consolidated[SHA3_256_DIGEST_SIZE] = { 243 + 0x5a, 0xfd, 0x66, 0x62, 0x5c, 0x37, 0x2b, 0x41, 244 + 0x77, 0x1c, 0x01, 0x5d, 0x64, 0x7c, 0x63, 0x7a, 245 + 0x7c, 0x76, 0x9e, 0xa8, 0xd1, 0xb0, 0x8e, 0x02, 246 + 0x16, 0x9b, 0xfe, 0x0e, 0xb5, 0xd8, 0x6a, 0xb5, 233 247 };
+130 -52
lib/crypto/tests/sha3_kunit.c
··· 247 247 "SHAKE256 gives wrong output for NIST.1600"); 248 248 } 249 249 250 - /* 251 - * Output tiling test of SHAKE256; equal output tiles barring the last. A 252 - * series of squeezings of the same context should, if laid end-to-end, match a 253 - * single squeezing of the combined size. 254 - */ 255 - static void test_shake256_tiling(struct kunit *test) 250 + static void shake(int alg, const u8 *in, size_t in_len, u8 *out, size_t out_len) 256 251 { 257 - struct shake_ctx ctx; 258 - u8 out[8 + SHA3_512_DIGEST_SIZE + 8]; 252 + if (alg == 0) 253 + shake128(in, in_len, out, out_len); 254 + else 255 + shake256(in, in_len, out, out_len); 256 + } 259 257 260 - for (int tile_size = 1; tile_size < SHAKE256_DEFAULT_SIZE; tile_size++) { 261 - int left = SHAKE256_DEFAULT_SIZE; 262 - u8 *p = out + 8; 258 + static void shake_init(struct shake_ctx *ctx, int alg) 259 + { 260 + if (alg == 0) 261 + shake128_init(ctx); 262 + else 263 + shake256_init(ctx); 264 + } 263 265 264 - memset(out, 0, sizeof(out)); 265 - shake256_init(&ctx); 266 - shake_update(&ctx, test_sha3_sample, 267 - sizeof(test_sha3_sample) - 1); 268 - while (left > 0) { 269 - int part = umin(tile_size, left); 266 + /* 267 + * Test each of SHAKE128 and SHAKE256 with all input lengths 0 through 4096, for 268 + * both input and output. The input and output lengths cycle through the values 269 + * together, so we do 4096 tests total. To verify all the SHAKE outputs, 270 + * compute and verify the SHA3-256 digest of all of them concatenated together. 271 + */ 272 + static void test_shake_all_lens_up_to_4096(struct kunit *test) 273 + { 274 + struct sha3_ctx main_ctx; 275 + const size_t max_len = 4096; 276 + u8 *const in = test_buf; 277 + u8 *const out = &test_buf[TEST_BUF_LEN - max_len]; 278 + u8 main_hash[SHA3_256_DIGEST_SIZE]; 270 279 271 - shake_squeeze(&ctx, p, part); 272 - p += part; 273 - left -= part; 280 + KUNIT_ASSERT_LE(test, 2 * max_len, TEST_BUF_LEN); 281 + 282 + rand_bytes_seeded_from_len(in, max_len); 283 + for (int alg = 0; alg < 2; alg++) { 284 + sha3_256_init(&main_ctx); 285 + for (size_t in_len = 0; in_len <= max_len; in_len++) { 286 + size_t out_len = (in_len * 293) % (max_len + 1); 287 + 288 + shake(alg, in, in_len, out, out_len); 289 + sha3_update(&main_ctx, out, out_len); 274 290 } 275 - 276 - KUNIT_ASSERT_MEMEQ_MSG(test, out, test_shake256, sizeof(test_shake256), 277 - "SHAKE tile %u gives wrong output", tile_size); 291 + sha3_final(&main_ctx, main_hash); 292 + if (alg == 0) 293 + KUNIT_ASSERT_MEMEQ_MSG(test, main_hash, 294 + shake128_testvec_consolidated, 295 + sizeof(main_hash), 296 + "shake128() gives wrong output"); 297 + else 298 + KUNIT_ASSERT_MEMEQ_MSG(test, main_hash, 299 + shake256_testvec_consolidated, 300 + sizeof(main_hash), 301 + "shake256() gives wrong output"); 278 302 } 279 303 } 280 304 281 305 /* 282 - * Output tiling test of SHAKE256; output tiles getting gradually smaller and 283 - * then cycling round to medium sized ones. A series of squeezings of the same 284 - * context should, if laid end-to-end, match a single squeezing of the combined 285 - * size. 306 + * Test that a sequence of SHAKE squeezes gives the same output as a single 307 + * squeeze of the same total length. 286 308 */ 287 - static void test_shake256_tiling2(struct kunit *test) 309 + static void test_shake_multiple_squeezes(struct kunit *test) 288 310 { 289 - struct shake_ctx ctx; 290 - u8 out[8 + SHA3_512_DIGEST_SIZE + 8]; 311 + const size_t max_len = 512; 312 + u8 *ref_out; 291 313 292 - for (int first_tile_size = 3; 293 - first_tile_size < SHAKE256_DEFAULT_SIZE; 294 - first_tile_size++) { 295 - int tile_size = first_tile_size; 296 - int left = SHAKE256_DEFAULT_SIZE; 297 - u8 *p = out + 8; 314 + KUNIT_ASSERT_GE(test, TEST_BUF_LEN, 2 * max_len); 298 315 299 - memset(out, 0, sizeof(out)); 300 - shake256_init(&ctx); 301 - shake_update(&ctx, test_sha3_sample, 302 - sizeof(test_sha3_sample) - 1); 303 - while (left > 0) { 304 - int part = umin(tile_size, left); 316 + ref_out = kunit_kzalloc(test, max_len, GFP_KERNEL); 317 + KUNIT_ASSERT_NOT_NULL(test, ref_out); 305 318 306 - shake_squeeze(&ctx, p, part); 307 - p += part; 308 - left -= part; 309 - tile_size--; 310 - if (tile_size < 1) 311 - tile_size = 5; 319 + for (int i = 0; i < 2000; i++) { 320 + const int alg = rand32() % 2; 321 + const size_t in_len = rand_length(max_len); 322 + const size_t out_len = rand_length(max_len); 323 + const size_t in_offs = rand_offset(max_len - in_len); 324 + const size_t out_offs = rand_offset(max_len - out_len); 325 + u8 *const in = &test_buf[in_offs]; 326 + u8 *const out = &test_buf[out_offs]; 327 + struct shake_ctx ctx; 328 + size_t remaining_len, j, num_parts; 329 + 330 + rand_bytes(in, in_len); 331 + rand_bytes(out, out_len); 332 + 333 + /* Compute the output using the one-shot function. */ 334 + shake(alg, in, in_len, ref_out, out_len); 335 + 336 + /* Compute the output using a random sequence of squeezes. */ 337 + shake_init(&ctx, alg); 338 + shake_update(&ctx, in, in_len); 339 + remaining_len = out_len; 340 + j = 0; 341 + num_parts = 0; 342 + while (rand_bool()) { 343 + size_t part_len = rand_length(remaining_len); 344 + 345 + shake_squeeze(&ctx, &out[j], part_len); 346 + num_parts++; 347 + j += part_len; 348 + remaining_len -= part_len; 349 + } 350 + if (remaining_len != 0 || rand_bool()) { 351 + shake_squeeze(&ctx, &out[j], remaining_len); 352 + num_parts++; 312 353 } 313 354 314 - KUNIT_ASSERT_MEMEQ_MSG(test, out, test_shake256, sizeof(test_shake256), 315 - "SHAKE tile %u gives wrong output", tile_size); 355 + /* Verify that the outputs are the same. */ 356 + KUNIT_ASSERT_MEMEQ_MSG( 357 + test, out, ref_out, out_len, 358 + "Multi-squeeze test failed with in_len=%zu in_offs=%zu out_len=%zu out_offs=%zu num_parts=%zu alg=%d", 359 + in_len, in_offs, out_len, out_offs, num_parts, alg); 360 + } 361 + } 362 + 363 + /* 364 + * Test that SHAKE operations on buffers immediately followed by an unmapped 365 + * page work as expected. This catches out-of-bounds memory accesses even if 366 + * they occur in assembly code. 367 + */ 368 + static void test_shake_with_guarded_bufs(struct kunit *test) 369 + { 370 + const size_t max_len = 512; 371 + u8 *reg_buf; 372 + 373 + KUNIT_ASSERT_GE(test, TEST_BUF_LEN, max_len); 374 + 375 + reg_buf = kunit_kzalloc(test, max_len, GFP_KERNEL); 376 + KUNIT_ASSERT_NOT_NULL(test, reg_buf); 377 + 378 + for (int alg = 0; alg < 2; alg++) { 379 + for (size_t len = 0; len <= max_len; len++) { 380 + u8 *guarded_buf = &test_buf[TEST_BUF_LEN - len]; 381 + 382 + rand_bytes(reg_buf, len); 383 + memcpy(guarded_buf, reg_buf, len); 384 + 385 + shake(alg, reg_buf, len, reg_buf, len); 386 + shake(alg, guarded_buf, len, guarded_buf, len); 387 + 388 + KUNIT_ASSERT_MEMEQ_MSG( 389 + test, reg_buf, guarded_buf, len, 390 + "Guard page test failed with len=%zu alg=%d", 391 + len, alg); 392 + } 316 393 } 317 394 } 318 395 ··· 403 326 KUNIT_CASE(test_shake256_basic), 404 327 KUNIT_CASE(test_shake128_nist), 405 328 KUNIT_CASE(test_shake256_nist), 406 - KUNIT_CASE(test_shake256_tiling), 407 - KUNIT_CASE(test_shake256_tiling2), 329 + KUNIT_CASE(test_shake_all_lens_up_to_4096), 330 + KUNIT_CASE(test_shake_multiple_squeezes), 331 + KUNIT_CASE(test_shake_with_guarded_bufs), 408 332 KUNIT_CASE(benchmark_hash), 409 333 {}, 410 334 };
+23 -4
scripts/crypto/gen-hash-testvecs.py
··· 111 111 f'hash_testvec_consolidated[{alg_digest_size_const(alg)}]', 112 112 hash_final(ctx)) 113 113 114 + def gen_additional_sha3_testvecs(): 115 + max_len = 4096 116 + in_data = rand_bytes(max_len) 117 + for alg in ['shake128', 'shake256']: 118 + ctx = hashlib.new('sha3-256') 119 + for in_len in range(max_len + 1): 120 + out_len = (in_len * 293) % (max_len + 1) 121 + out = hashlib.new(alg, data=in_data[:in_len]).digest(out_len) 122 + ctx.update(out) 123 + print_static_u8_array_definition(f'{alg}_testvec_consolidated[SHA3_256_DIGEST_SIZE]', 124 + ctx.digest()) 125 + 114 126 def gen_hmac_testvecs(alg): 115 127 ctx = hmac.new(rand_bytes(32), digestmod=alg) 116 128 data = rand_bytes(4096) ··· 167 155 168 156 if len(sys.argv) != 2: 169 157 sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n') 170 - sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n') 158 + sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305 or sha3.\n') 171 159 sys.stderr.write('Example: gen-hash-testvecs.py sha512\n') 172 160 sys.exit(1) 173 161 174 162 alg = sys.argv[1] 175 163 print('/* SPDX-License-Identifier: GPL-2.0-or-later */') 176 164 print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') 177 - gen_unkeyed_testvecs(alg) 178 165 if alg.startswith('blake2'): 166 + gen_unkeyed_testvecs(alg) 179 167 gen_additional_blake2_testvecs(alg) 180 168 elif alg == 'poly1305': 169 + gen_unkeyed_testvecs(alg) 181 170 gen_additional_poly1305_testvecs() 182 - elif alg.startswith('sha3-'): 183 - pass # no HMAC 171 + elif alg == 'sha3': 172 + print() 173 + print('/* SHA3-256 test vectors */') 174 + gen_unkeyed_testvecs('sha3-256') 175 + print() 176 + print('/* SHAKE test vectors */') 177 + gen_additional_sha3_testvecs() 184 178 else: 179 + gen_unkeyed_testvecs(alg) 185 180 gen_hmac_testvecs(alg)