authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-03-23 03:11:13+01:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-03-23 03:11:13+01:00
logb2c2b4d0630716fa6a61ec27df26c5253f2de5b0
treeda26fc596c27ddfdd4fdbbf3761641c278b8c162
parent1311880eea5e80b5a50eb6360d183b21ec66f0c7
parent8efd539305f9beea2da940d9e174a5a38f9c7edf

Merge pull request 'crypto: correct aes-siv s2v' (#31623) from sinon/zig:fix-aes-siv into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31623 Reviewed-by: Frank Denis <jedisct1@noreply.codeberg.org>

1 files changed, 53 insertions(+), 8 deletions(-)

lib/std/crypto/aes_siv.zig+53-8
...@@ -88,16 +88,19 @@ fn AesSiv(comptime Aes: anytype) type {...@@ -88,16 +88,19 @@ fn AesSiv(comptime Aes: anytype) type {
88 // Process the final string88 // Process the final string
89 const sn = strings[strings.len - 1];89 const sn = strings[strings.len - 1];
90 if (sn.len >= 16) {90 if (sn.len >= 16) {
91 // XOR d with the first 16 bytes of Sn91 // XOR d with the last 16 bytes of Sn,
92 var xored_msg_buf: [4096]u8 = undefined;92 // and give the entire Sn to CMAC incrementally.
93 const xored_len = @min(sn.len, xored_msg_buf.len);93 var cmac = CmacImpl.init(&key);
94 @memcpy(xored_msg_buf[0..xored_len], sn[0..xored_len]);94 const prefix = sn.len - 16;
9595 cmac.update(sn[0..prefix]);
96 for (d, 0..) |b, j| {96
97 xored_msg_buf[j] ^= b;97 var tail: [16]u8 = undefined;
98 for (&tail, sn[prefix..][0..16], d) |*out, s, db| {
99 out.* = s ^ db;
98 }100 }
101 cmac.update(&tail);
99102
100 CmacImpl.create(iv, xored_msg_buf[0..xored_len], &key);103 cmac.final(iv);
101 } else {104 } else {
102 // Pad and XOR105 // Pad and XOR
103 d = dbl(d);106 d = dbl(d);
...@@ -355,6 +358,48 @@ test "Aes128Siv - RFC 5297 Test Vector A.1" {...@@ -355,6 +358,48 @@ test "Aes128Siv - RFC 5297 Test Vector A.1" {
355 try testing.expectEqualSlices(u8, &plaintext, &decrypted);358 try testing.expectEqualSlices(u8, &plaintext, &decrypted);
356}359}
357360
361test "Aes128Siv - RFC 5297 Test Vector A.2" {
362 // Test vector from RFC 5297 Appendix A.2
363 const key: [32]u8 = .{
364 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
365 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
366 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
367 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
368 };
369 const ad1 = [_]u8{
370 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
371 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
372 0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
373 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
374 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
375 };
376 const ad2 = [_]u8{
377 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
378 0x90, 0xa0,
379 };
380 const nonce: [16]u8 = .{
381 0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
382 0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0,
383 };
384 const plaintext = [_]u8{
385 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
386 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
387 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
388 0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
389 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
390 0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53,
391 };
392
393 var ciphertext: [plaintext.len]u8 = undefined;
394 var tag: [16]u8 = undefined;
395
396 Aes128Siv.encryptWithAdVector(&ciphertext, &tag, &plaintext, &.{ &ad1, &ad2, &nonce }, key);
397
398 // Expected values from RFC 5297
399 try htest.assertEqual("7bdb6e3b432667eb06f4d14bff2fbd0f", &tag);
400 try htest.assertEqual("cb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829ea64ad544a272e9c485b62a3fd5c0d", &ciphertext);
401}
402
358test "Aes128Siv - empty plaintext" {403test "Aes128Siv - empty plaintext" {
359 const key: [32]u8 = @splat(0x42);404 const key: [32]u8 = @splat(0x42);
360 const plaintext = "";405 const plaintext = "";