diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-14 08:23:37 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-14 08:23:37 -0700 |
| commit | 0fcb05b72344f40806cbe810de8872f1698bf053 (patch) | |
| tree | 4c8d2f41787972a683a7bcceac460f4c4ff4d3a7 /lib/crypto | |
| parent | 4056b3c9d3c92ea45955641dbafeb9b960e146ef (diff) | |
| parent | 7b817748bf083ad715a8d70de63f2184178cc372 (diff) | |
Merge tag 'libcrypto-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linuxHEADmaster
Pull crypto library fixes from Eric Biggers:
"Fix two Poly1305 bugs including a lost carry bit, and add a regression
test"
* tag 'libcrypto-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux:
lib/crypto: tests: Add Poly1305 split-update carry regression test
lib/crypto: arm64: Fix lost Poly1305 carry when resuming NEON state
lib/crypto: poly1305: Use memzero_explicit() in poly1305_final()
Diffstat (limited to 'lib/crypto')
| -rw-r--r-- | lib/crypto/arm64/poly1305-armv8.pl | 2 | ||||
| -rw-r--r-- | lib/crypto/poly1305.c | 2 | ||||
| -rw-r--r-- | lib/crypto/tests/poly1305_kunit.c | 97 |
3 files changed, 90 insertions, 11 deletions
diff --git a/lib/crypto/arm64/poly1305-armv8.pl b/lib/crypto/arm64/poly1305-armv8.pl index f1930c6b55ce..234398ff6b60 100644 --- a/lib/crypto/arm64/poly1305-armv8.pl +++ b/lib/crypto/arm64/poly1305-armv8.pl @@ -375,7 +375,7 @@ poly1305_blocks_neon: adc $h1,$h1,xzr lsr $h2,x14,#24 adds $h1,$h1,x14,lsl#40 - adc $d2,$h2,xzr // can be partially reduced... + adc $h2,$h2,xzr // preserve carry into top limb ldp $d0,$d1,[$inp],#16 // load input sub $len,$len,#16 diff --git a/lib/crypto/poly1305.c b/lib/crypto/poly1305.c index f313ccc4b4dd..048c7afe8613 100644 --- a/lib/crypto/poly1305.c +++ b/lib/crypto/poly1305.c @@ -78,7 +78,7 @@ void poly1305_final(struct poly1305_desc_ctx *desc, u8 *dst) } poly1305_emit(&desc->state.h, dst, desc->s); - *desc = (struct poly1305_desc_ctx){}; + memzero_explicit(desc, sizeof(*desc)); } EXPORT_SYMBOL(poly1305_final); diff --git a/lib/crypto/tests/poly1305_kunit.c b/lib/crypto/tests/poly1305_kunit.c index f3cb6245bc29..d4490ee27c3d 100644 --- a/lib/crypto/tests/poly1305_kunit.c +++ b/lib/crypto/tests/poly1305_kunit.c @@ -55,16 +55,17 @@ static int poly1305_suite_init(struct kunit_suite *suite) * - Using an all-one-bits r_key tests the key clamping. * - Using an all-one-bits s_key tests carries in implementations of the * addition mod 2**128 during finalization. - * - Using all-one-bits message, and to a lesser extent r_key, tends to maximize - * any intermediate accumulator values. This increases the chance of - * detecting bugs that occur only in rare cases where the accumulator values - * get very large, for example the bug fixed by commit 678cce4019d746da - * ("crypto: x86/poly1305 - fix overflow during partial reduction"). + * - Using all-one-bits message and r_key results in large values for the + * internal products of r_key by (accumulator + message block). This + * increases the chance of detecting bugs that occur only in rare cases where + * the sum of these internal products is very large, for example the bug fixed + * by commit 678cce4019d7 ("crypto: x86/poly1305 - fix overflow during partial + * reduction"). * - * Accumulator overflow bugs may be specific to particular update lengths (in - * blocks) and/or particular values of the previous acculumator. Note that the - * accumulator starts at 0 which gives the lowest chance of an overflow. Thus, - * a single all-one-bits test vector may be insufficient. + * Bugs with the handling of large internal product sums may be specific to + * particular update lengths (in blocks) and/or require that the accumulator + * also have a large value. Note that the accumulator starts at 0. Thus, a + * single all-one-bits test vector may be insufficient. * * Considering that, do the following test: continuously update a single * Poly1305 context with all-one-bits data of varying lengths (0, 16, 32, ..., @@ -141,10 +142,88 @@ static void test_poly1305_reduction_edge_cases(struct kunit *test) } } +/* + * Test that Poly1305 MACs are computed correctly when r_key=1, s_key=0, and the + * message consists of all-ones blocks. + * + * With r_key=1 and s_key=0, Poly1305 degrades to the sum of the padded blocks + * mod 2**130 - 5, then reduced mod 2**128. A padded all-ones block maps to + * 2**128 + (2**128 - 1) = 2**129 - 1. Two such blocks sum to 2**130 - 2, which + * is congruent to 3 mod 2**130 - 5. Thus, the expected MAC is just 3 * + * floor(nblocks / 2), minus 1 if nblocks is odd, then reduced mod 2**128. If + * nblocks == 1 this is 2**128 - 1, otherwise it's just a small integer. + * + * Inside the Poly1305 implementation in this case, the multiplication does + * nothing and each block just adds 2**129 - 1 to the accumulator. Without the + * multiplication to mix things up, this results in some interesting values + * being reached where the limbs are at or near their maximum values, even after + * (lazy) reduction. This can reproduce bugs that happen only in such cases. + * + * For example, this test case reproduces the bug fixed by commit 24f07e8c07a0 + * ("lib/crypto: arm64: Fix lost Poly1305 carry when resuming NEON state"). + * + * However, reaching that bug required two consecutive updates: one with >= 8 + * blocks to cause NEON code to be used and leave the accumulator in base 2**26 + * with its limbs in a certain pattern, then one with an odd number of blocks >= + * 9 to cause NEON to be used again, but first processing one block using scalar + * code as a special case, triggering a conversion to base 2**64 where the carry + * into the 2**128 bit was lost. To cover this and other similar edge cases + * that may exist in other Poly1305 implementations, just test all possible + * block-aligned splits between three poly1305_update() calls. + */ +static void test_poly1305_split_update_carry(struct kunit *test) +{ + static const u8 key[POLY1305_KEY_SIZE] = { 1 }; /* r_key=1, s_key=0 */ + /* + * Use max_nblocks=66 so that it's a bit more than twice the AVX-512 + * threshold of 32 blocks. + */ + const int max_nblocks = 66; + u8 *data = alloc_buf(test, max_nblocks * POLY1305_BLOCK_SIZE); + u8 expected_mac[POLY1305_DIGEST_SIZE]; + u8 actual_mac[POLY1305_DIGEST_SIZE]; + struct poly1305_desc_ctx ctx; + + KUNIT_ASSERT_LE(test, 3 * (max_nblocks / 2), U8_MAX); + + memset(data, 0xff, max_nblocks * POLY1305_BLOCK_SIZE); + + for (int nblocks = 0; nblocks <= max_nblocks; nblocks++) { + size_t len = nblocks * POLY1305_BLOCK_SIZE; + + expected_mac[0] = 3 * (nblocks / 2) - (nblocks % 2); + memset(&expected_mac[1], nblocks == 1 ? 0xff : 0, + POLY1305_DIGEST_SIZE - 1); + for (size_t part1_len = 0; part1_len <= len; + part1_len += POLY1305_BLOCK_SIZE) { + for (size_t part2_len = 0; part2_len <= len - part1_len; + part2_len += POLY1305_BLOCK_SIZE) { + size_t part3_len = len - part1_len - part2_len; + + poly1305_init(&ctx, key); + poly1305_update(&ctx, data, part1_len); + poly1305_update(&ctx, &data[part1_len], + part2_len); + poly1305_update(&ctx, + &data[part1_len + part2_len], + part3_len); + poly1305_final(&ctx, actual_mac); + KUNIT_ASSERT_MEMEQ_MSG( + test, actual_mac, expected_mac, + POLY1305_DIGEST_SIZE, + "Failed with nblocks=%d, part1_len=%zu, part2_len=%zu, part3_len=%zu", + nblocks, part1_len, part2_len, + part3_len); + } + } + } +} + static struct kunit_case poly1305_test_cases[] = { HASH_KUNIT_CASES, KUNIT_CASE(test_poly1305_allones_keys_and_message), KUNIT_CASE(test_poly1305_reduction_edge_cases), + KUNIT_CASE(test_poly1305_split_update_carry), KUNIT_CASE(benchmark_hash), {}, }; |
