authorgravatar for erik@erisc.seErik Schlyter <erik@erisc.se> 2025-08-25 17:59:42+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-08-25 18:50:36+02:00
loga51e88c3b108acfa1ea1823ed31d78870e662fc0
tree97dd05bdf197c76f495697f2cf6d553a5503d018
parent98585af51d2611584d329ab4defe444a44b8f35e
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Fix #24999: copy left-overs before we XOR into c. (#25001)

It is important we copy the left-overs in the message *before* we XOR it into the ciphertext, because if we're encrypting in-place (i.e., m == c), we will manipulate the message that will be used for tag generation. This will generate faulty tags when message length doesn't conform with 16 byte blocks.

1 files changed, 32 insertions(+), 3 deletions(-)

lib/std/crypto/aes_ocb.zig+32-3
......@@ -155,12 +155,12 @@ fn AesOcb(comptime Aes: anytype) type {
155155 xorWith(&offset, lx.star);
156156 var pad = offset;
157157 aes_enc_ctx.encrypt(&pad, &pad);
158 for (m[i * 16 ..], 0..) |x, j| {
159 c[i * 16 + j] = pad[j] ^ x;
160 }
161158 var e = [_]u8{0} ** 16;
162159 @memcpy(e[0..leftover], m[i * 16 ..][0..leftover]);
163160 e[leftover] = 0x80;
161 for (m[i * 16 ..], 0..) |x, j| {
162 c[i * 16 + j] = pad[j] ^ x;
163 }
164164 xorWith(&sum, e);
165165 }
166166 var e = xorBlocks(xorBlocks(sum, offset), lx.dol);
......@@ -354,3 +354,32 @@ test "AesOcb test vector 4" {
354354 try Aes128Ocb.decrypt(&m2, &c, tag, &ad, nonce, k);
355355 assert(mem.eql(u8, &m, &m2));
356356}
357
358test "AesOcb in-place encryption-decryption" {
359 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
360
361 var k: [Aes128Ocb.key_length]u8 = undefined;
362 var nonce: [Aes128Ocb.nonce_length]u8 = undefined;
363 var tag: [Aes128Ocb.tag_length]u8 = undefined;
364 var m: [40]u8 = undefined;
365 var original_m: [m.len]u8 = undefined;
366 _ = try hexToBytes(&k, "000102030405060708090A0B0C0D0E0F");
367 _ = try hexToBytes(&m, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627");
368 _ = try hexToBytes(&nonce, "BBAA9988776655443322110D");
369 const ad = m;
370
371 @memcpy(&original_m, &m);
372
373 Aes128Ocb.encrypt(&m, &tag, &m, &ad, nonce, k);
374
375 var expected_c: [m.len]u8 = undefined;
376 var expected_tag: [tag.len]u8 = undefined;
377 _ = try hexToBytes(&expected_tag, "ED07BA06A4A69483A7035490C5769E60");
378 _ = try hexToBytes(&expected_c, "D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7A");
379
380 try testing.expectEqualSlices(u8, &expected_tag, &tag);
381 try testing.expectEqualSlices(u8, &expected_c, &m);
382 try Aes128Ocb.decrypt(&m, &m, tag, &ad, nonce, k);
383
384 try testing.expectEqualSlices(u8, &original_m, &m);
385}