From e3395669223afa70a5cae1dd9a12b3a5f0656209 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Fri, 29 May 2026 11:18:39 +0200 Subject: [PATCH] crypto.mode.ctr: make the counter wrap, even in parallel updates Counter mode encrypts/decrypts using a counter to create the key stream. The counter is allowed to wrap. This is especially necessary in SIV modes where it can start from any possible value. And it was in the sequential path, but not in the parallel one. --- lib/std/crypto/modes.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/std/crypto/modes.zig b/lib/std/crypto/modes.zig index ca8ecb90a6bb5b4b7a42f5da8e2b401d616c7fe5..eeba224d2738706774da66af30c4700aa9d0831f 100644 --- a/lib/std/crypto/modes.zig +++ b/lib/std/crypto/modes.zig @@ -55,7 +55,7 @@ pub fn ctrSlice( inline while (j < parallel_count) : (j += 1) { mem.writeInt(CounterInt, counters[j * block_length + counter_offset ..][0..counter_size], cnt_val +% j, endian); } - cnt_val += parallel_count; + cnt_val +%= parallel_count; block_cipher.xorWide(parallel_count, dst[i .. i + wide_block_length][0..wide_block_length], src[i .. i + wide_block_length][0..wide_block_length], counters); } mem.writeInt(CounterInt, counterBlock[counter_offset..][0..counter_size], cnt_val, endian); @@ -224,4 +224,11 @@ test "ctr mode" { const expected = [_]u8{ 0x7e, 0x48, 0x15, 0xa8, 0x16, 0x66, 0xf0, 0xea, 0xad, 0x3c, 0x07, 0x97, 0x2f, 0xe8, 0x25, 0xc1 }; try testing.expectEqualSlices(u8, expected[0..], out[0..]); } + + // Make the counter wrap + { + const iv_top: [16]u8 = @splat(0xff); + var buf: [256]u8 = @splat(0); + ctr(aes.AesEncryptCtx(aes.Aes128), ctx, buf[0..], buf[0..], iv_top, .little); + } } -- 2.54.0