| ... | ... | @@ -161,7 +161,7 @@ fn State128X(comptime degree: u7) type { |
| 161 | 161 | state.update(msg0, msg1); |
| 162 | 162 | } |
| 163 | 163 | |
| 164 | | fn mac(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { |
| 164 | fn finalize(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { |
| 165 | 165 | const blocks = &state.blocks; |
| 166 | 166 | var sizes: [aes_block_length]u8 = undefined; |
| 167 | 167 | mem.writeInt(u64, sizes[0..8], @as(u64, adlen) * 8, .little); |
| ... | ... | @@ -200,6 +200,59 @@ fn State128X(comptime degree: u7) type { |
| 200 | 200 | else => unreachable, |
| 201 | 201 | } |
| 202 | 202 | } |
| 203 | |
| 204 | fn finalizeMac(state: *State, comptime tag_bits: u9, datalen: usize) [tag_bits / 8]u8 { |
| 205 | const blocks = &state.blocks; |
| 206 | var sizes: [aes_block_length]u8 = undefined; |
| 207 | mem.writeInt(u64, sizes[0..8], @as(u64, datalen) * 8, .little); |
| 208 | mem.writeInt(u64, sizes[8..16], tag_bits, .little); |
| 209 | for (1..degree) |i| { |
| 210 | @memcpy(sizes[i * 16 ..][0..16], sizes[0..16]); |
| 211 | } |
| 212 | var t = blocks[2].xorBlocks(AesBlockVec.fromBytes(&sizes)); |
| 213 | for (0..7) |_| { |
| 214 | state.update(t, t); |
| 215 | } |
| 216 | if (degree > 1) { |
| 217 | var v = [_]u8{0} ** rate; |
| 218 | switch (tag_bits) { |
| 219 | 128 => { |
| 220 | const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(); |
| 221 | for (0..degree / 2) |d| { |
| 222 | v[0..32].* = tags[d * 32 ..][0..32].*; |
| 223 | state.absorb(&v); |
| 224 | } |
| 225 | }, |
| 226 | 256 => { |
| 227 | const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes(); |
| 228 | const tags_1 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes(); |
| 229 | for (1..degree) |d| { |
| 230 | v[0..32].* = tags_0[d * 16 ..][0..16].* ++ tags_1[d * 16 ..][0..16].*; |
| 231 | state.absorb(&v); |
| 232 | } |
| 233 | }, |
| 234 | else => unreachable, |
| 235 | } |
| 236 | mem.writeInt(u64, sizes[0..8], degree, .little); |
| 237 | mem.writeInt(u64, sizes[8..16], tag_bits, .little); |
| 238 | t = blocks[2].xorBlocks(AesBlockVec.fromBytes(&sizes)); |
| 239 | for (0..7) |_| { |
| 240 | state.update(t, t); |
| 241 | } |
| 242 | } |
| 243 | switch (tag_bits) { |
| 244 | 128 => { |
| 245 | const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(); |
| 246 | return tags[0..16].*; |
| 247 | }, |
| 248 | 256 => { |
| 249 | const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes(); |
| 250 | const tags_1 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes(); |
| 251 | return tags_0[0..16].* ++ tags_1[0..16].*; |
| 252 | }, |
| 253 | else => unreachable, |
| 254 | } |
| 255 | } |
| 203 | 256 | }; |
| 204 | 257 | } |
| 205 | 258 | |
| ... | ... | @@ -252,7 +305,7 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type { |
| 252 | 305 | state.enc(&dst, &src); |
| 253 | 306 | @memcpy(c[i..][0 .. m.len % block_length], dst[0 .. m.len % block_length]); |
| 254 | 307 | } |
| 255 | | tag.* = state.mac(tag_bits, ad.len, m.len); |
| 308 | tag.* = state.finalize(tag_bits, ad.len, m.len); |
| 256 | 309 | } |
| 257 | 310 | |
| 258 | 311 | /// `m`: Message |
| ... | ... | @@ -284,7 +337,7 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type { |
| 284 | 337 | if (m.len % block_length != 0) { |
| 285 | 338 | state.decLast(m[i..], c[i..]); |
| 286 | 339 | } |
| 287 | | var computed_tag = state.mac(tag_bits, ad.len, m.len); |
| 340 | var computed_tag = state.finalize(tag_bits, ad.len, m.len); |
| 288 | 341 | const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); |
| 289 | 342 | if (!verify) { |
| 290 | 343 | crypto.secureZero(u8, &computed_tag); |
| ... | ... | @@ -401,7 +454,7 @@ fn State256X(comptime degree: u7) type { |
| 401 | 454 | state.update(msg); |
| 402 | 455 | } |
| 403 | 456 | |
| 404 | | fn mac(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { |
| 457 | fn finalize(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { |
| 405 | 458 | const blocks = &state.blocks; |
| 406 | 459 | var sizes: [aes_block_length]u8 = undefined; |
| 407 | 460 | mem.writeInt(u64, sizes[0..8], @as(u64, adlen) * 8, .little); |
| ... | ... | @@ -440,6 +493,61 @@ fn State256X(comptime degree: u7) type { |
| 440 | 493 | else => unreachable, |
| 441 | 494 | } |
| 442 | 495 | } |
| 496 | |
| 497 | fn finalizeMac(state: *State, comptime tag_bits: u9, datalen: usize) [tag_bits / 8]u8 { |
| 498 | const blocks = &state.blocks; |
| 499 | var sizes: [aes_block_length]u8 = undefined; |
| 500 | mem.writeInt(u64, sizes[0..8], @as(u64, datalen) * 8, .little); |
| 501 | mem.writeInt(u64, sizes[8..16], tag_bits, .little); |
| 502 | for (1..degree) |i| { |
| 503 | @memcpy(sizes[i * 16 ..][0..16], sizes[0..16]); |
| 504 | } |
| 505 | var t = blocks[3].xorBlocks(AesBlockVec.fromBytes(&sizes)); |
| 506 | for (0..7) |_| { |
| 507 | state.update(t); |
| 508 | } |
| 509 | if (degree > 1) { |
| 510 | var v = [_]u8{0} ** rate; |
| 511 | switch (tag_bits) { |
| 512 | 128 => { |
| 513 | const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(); |
| 514 | for (1..degree) |d| { |
| 515 | v[0..16].* = tags[d * 16 ..][0..16].*; |
| 516 | state.absorb(&v); |
| 517 | } |
| 518 | }, |
| 519 | 256 => { |
| 520 | const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).toBytes(); |
| 521 | const tags_1 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(); |
| 522 | for (1..degree) |d| { |
| 523 | v[0..16].* = tags_0[d * 16 ..][0..16].*; |
| 524 | state.absorb(&v); |
| 525 | v[0..16].* = tags_1[d * 16 ..][0..16].*; |
| 526 | state.absorb(&v); |
| 527 | } |
| 528 | }, |
| 529 | else => unreachable, |
| 530 | } |
| 531 | mem.writeInt(u64, sizes[0..8], degree, .little); |
| 532 | mem.writeInt(u64, sizes[8..16], tag_bits, .little); |
| 533 | t = blocks[3].xorBlocks(AesBlockVec.fromBytes(&sizes)); |
| 534 | for (0..7) |_| { |
| 535 | state.update(t); |
| 536 | } |
| 537 | } |
| 538 | switch (tag_bits) { |
| 539 | 128 => { |
| 540 | const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(); |
| 541 | return tags[0..16].*; |
| 542 | }, |
| 543 | 256 => { |
| 544 | const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).toBytes(); |
| 545 | const tags_1 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(); |
| 546 | return tags_0[0..16].* ++ tags_1[0..16].*; |
| 547 | }, |
| 548 | else => unreachable, |
| 549 | } |
| 550 | } |
| 443 | 551 | }; |
| 444 | 552 | } |
| 445 | 553 | |
| ... | ... | @@ -492,7 +600,7 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type { |
| 492 | 600 | state.enc(&dst, &src); |
| 493 | 601 | @memcpy(c[i..][0 .. m.len % block_length], dst[0 .. m.len % block_length]); |
| 494 | 602 | } |
| 495 | | tag.* = state.mac(tag_bits, ad.len, m.len); |
| 603 | tag.* = state.finalize(tag_bits, ad.len, m.len); |
| 496 | 604 | } |
| 497 | 605 | |
| 498 | 606 | /// `m`: Message |
| ... | ... | @@ -524,7 +632,7 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type { |
| 524 | 632 | if (m.len % block_length != 0) { |
| 525 | 633 | state.decLast(m[i..], c[i..]); |
| 526 | 634 | } |
| 527 | | var computed_tag = state.mac(tag_bits, ad.len, m.len); |
| 635 | var computed_tag = state.finalize(tag_bits, ad.len, m.len); |
| 528 | 636 | const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); |
| 529 | 637 | if (!verify) { |
| 530 | 638 | crypto.secureZero(u8, &computed_tag); |
| ... | ... | @@ -562,9 +670,31 @@ pub const Aegis128X2Mac = AegisMac(Aegis128X2_256); |
| 562 | 670 | /// - It has a large security margin against internal collisions. |
| 563 | 671 | pub const Aegis128LMac = AegisMac(Aegis128L_256); |
| 564 | 672 | |
| 673 | /// The `Aegis256X4Mac` message authentication function has a 256-bit key size, |
| 674 | /// and outputs 256 bit tags. |
| 675 | /// The key size is the main practical difference with `Aegis128X4Mac`. |
| 676 | /// AEGIS' large state, non-linearity and non-invertibility provides the |
| 677 | /// following properties: |
| 678 | /// - 256 bit security against forgery. |
| 679 | /// - Recovering the secret key from the state would require ~2^256 attempts, |
| 680 | /// which is infeasible for any practical adversary. |
| 681 | /// - It has a large security margin against internal collisions. |
| 682 | pub const Aegis256X4Mac = AegisMac(Aegis256X4_256); |
| 683 | |
| 684 | /// The `Aegis256X2Mac` message authentication function has a 256-bit key size, |
| 685 | /// and outputs 256 bit tags. |
| 686 | /// The key size is the main practical difference with `Aegis128X2Mac`. |
| 687 | /// AEGIS' large state, non-linearity and non-invertibility provides the |
| 688 | /// following properties: |
| 689 | /// - 256 bit security against forgery. |
| 690 | /// - Recovering the secret key from the state would require ~2^256 attempts, |
| 691 | /// which is infeasible for any practical adversary. |
| 692 | /// - It has a large security margin against internal collisions. |
| 693 | pub const Aegis256X2Mac = AegisMac(Aegis256X2_256); |
| 694 | |
| 565 | 695 | /// The `Aegis256Mac` message authentication function has a 256-bit key size, |
| 566 | | /// and outputs 256 bit tags. Unless theoretical multi-target attacks are a |
| 567 | | /// concern, the AEGIS-128L variant should be preferred. |
| 696 | /// and outputs 256 bit tags. |
| 697 | /// The key size is the main practical difference with `Aegis128LMac`. |
| 568 | 698 | /// AEGIS' large state, non-linearity and non-invertibility provides the |
| 569 | 699 | /// following properties: |
| 570 | 700 | /// - 256 bit security against forgery. |
| ... | ... | @@ -573,9 +703,21 @@ pub const Aegis128LMac = AegisMac(Aegis128L_256); |
| 573 | 703 | /// - It has a large security margin against internal collisions. |
| 574 | 704 | pub const Aegis256Mac = AegisMac(Aegis256_256); |
| 575 | 705 | |
| 706 | /// AEGIS-128X4 MAC with 128-bit tags |
| 707 | pub const Aegis128X4Mac_128 = AegisMac(Aegis128X4); |
| 708 | |
| 709 | /// AEGIS-128X2 MAC with 128-bit tags |
| 710 | pub const Aegis128X2Mac_128 = AegisMac(Aegis128X2); |
| 711 | |
| 576 | 712 | /// AEGIS-128L MAC with 128-bit tags |
| 577 | 713 | pub const Aegis128LMac_128 = AegisMac(Aegis128L); |
| 578 | 714 | |
| 715 | /// AEGIS-256X4 MAC with 128-bit tags |
| 716 | pub const Aegis256X4Mac_128 = AegisMac(Aegis256X4); |
| 717 | |
| 718 | /// AEGIS-256X2 MAC with 128-bit tags |
| 719 | pub const Aegis256X2Mac_128 = AegisMac(Aegis256X2); |
| 720 | |
| 579 | 721 | /// AEGIS-256 MAC with 128-bit tags |
| 580 | 722 | pub const Aegis256Mac_128 = AegisMac(Aegis256); |
| 581 | 723 | |
| ... | ... | @@ -585,6 +727,7 @@ fn AegisMac(comptime T: type) type { |
| 585 | 727 | |
| 586 | 728 | pub const mac_length = T.tag_length; |
| 587 | 729 | pub const key_length = T.key_length; |
| 730 | pub const nonce_length = T.nonce_length; |
| 588 | 731 | pub const block_length = T.block_length; |
| 589 | 732 | |
| 590 | 733 | state: T.State, |
| ... | ... | @@ -592,11 +735,17 @@ fn AegisMac(comptime T: type) type { |
| 592 | 735 | off: usize = 0, |
| 593 | 736 | msg_len: usize = 0, |
| 594 | 737 | |
| 595 | | /// Initialize a state for the MAC function |
| 738 | /// Initialize a state for the MAC function, with a key and a nonce |
| 739 | pub fn initWithNonce(key: *const [key_length]u8, nonce: *const [nonce_length]u8) Mac { |
| 740 | return Mac{ |
| 741 | .state = T.State.init(key.*, nonce.*), |
| 742 | }; |
| 743 | } |
| 744 | |
| 745 | /// Initialize a state for the MAC function, with a default nonce |
| 596 | 746 | pub fn init(key: *const [key_length]u8) Mac { |
| 597 | | const nonce = [_]u8{0} ** T.nonce_length; |
| 598 | 747 | return Mac{ |
| 599 | | .state = T.State.init(key.*, nonce), |
| 748 | .state = T.State.init(key.*, [_]u8{0} ** nonce_length), |
| 600 | 749 | }; |
| 601 | 750 | } |
| 602 | 751 | |
| ... | ... | @@ -634,7 +783,14 @@ fn AegisMac(comptime T: type) type { |
| 634 | 783 | @memcpy(pad[0..self.off], self.buf[0..self.off]); |
| 635 | 784 | self.state.absorb(&pad); |
| 636 | 785 | } |
| 637 | | out.* = self.state.mac(T.tag_length * 8, self.msg_len, 0); |
| 786 | out.* = self.state.finalizeMac(T.tag_length * 8, self.msg_len); |
| 787 | } |
| 788 | |
| 789 | /// Return an authentication tag for a message, a key and a nonce |
| 790 | pub fn createWithNonce(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8, nonce: *const [nonce_length]u8) void { |
| 791 | var ctx = Mac.initWithNonce(key, nonce); |
| 792 | ctx.update(msg); |
| 793 | ctx.final(out); |
| 638 | 794 | } |
| 639 | 795 | |
| 640 | 796 | /// Return an authentication tag for a message and a key |
| ... | ... | @@ -820,29 +976,72 @@ test "Aegis MAC" { |
| 820 | 976 | st.update(msg[0..32]); |
| 821 | 977 | st.update(msg[32..]); |
| 822 | 978 | st.final(&tag); |
| 823 | | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 979 | try htest.assertEqual("f5eb88d90b7d31c9a679eb94ed1374cd14816b19cdb77930d1a5158f8595983b", &tag); |
| 824 | 980 | |
| 825 | 981 | st = st_init; |
| 826 | 982 | st.update(msg[0..31]); |
| 827 | 983 | st.update(msg[31..]); |
| 828 | 984 | st.final(&tag); |
| 829 | | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 985 | try htest.assertEqual("f5eb88d90b7d31c9a679eb94ed1374cd14816b19cdb77930d1a5158f8595983b", &tag); |
| 830 | 986 | |
| 831 | 987 | st = st_init; |
| 832 | 988 | st.update(msg[0..14]); |
| 833 | 989 | st.update(msg[14..30]); |
| 834 | 990 | st.update(msg[30..]); |
| 835 | 991 | st.final(&tag); |
| 836 | | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 837 | | |
| 838 | | var empty: [0]u8 = undefined; |
| 839 | | const nonce = [_]u8{0x00} ** Aegis128L_256.nonce_length; |
| 840 | | Aegis128L_256.encrypt(&empty, &tag, &empty, &msg, nonce, key); |
| 841 | | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 992 | try htest.assertEqual("f5eb88d90b7d31c9a679eb94ed1374cd14816b19cdb77930d1a5158f8595983b", &tag); |
| 842 | 993 | |
| 843 | 994 | // An update whose size is not a multiple of the block size |
| 844 | 995 | st = st_init; |
| 845 | 996 | st.update(msg[0..33]); |
| 846 | 997 | st.final(&tag); |
| 847 | | try htest.assertEqual("c7cf649a844c1a6676cf6d91b1658e0aee54a4da330b0a8d3bc7ea4067551d1b", &tag); |
| 998 | try htest.assertEqual("07b3ba5ad9ceee5ef1906e3396f0fa540fbcd2f33833ef97c35bdc2ae9ae0535", &tag); |
| 999 | } |
| 1000 | |
| 1001 | test "AEGISMAC-128* test vectors" { |
| 1002 | const key = [_]u8{ 0x10, 0x01 } ++ [_]u8{0x00} ** (16 - 2); |
| 1003 | const nonce = [_]u8{ 0x10, 0x00, 0x02 } ++ [_]u8{0x00} ** (16 - 3); |
| 1004 | var msg: [35]u8 = undefined; |
| 1005 | for (&msg, 0..) |*byte, i| byte.* = @truncate(i); |
| 1006 | var mac128: [16]u8 = undefined; |
| 1007 | var mac256: [32]u8 = undefined; |
| 1008 | |
| 1009 | Aegis128LMac.createWithNonce(&mac256, &msg, &key, &nonce); |
| 1010 | Aegis128LMac_128.createWithNonce(&mac128, &msg, &key, &nonce); |
| 1011 | try htest.assertEqual("d3f09b2842ad301687d6902c921d7818", &mac128); |
| 1012 | try htest.assertEqual("9490e7c89d420c9f37417fa625eb38e8cad53c5cbec55285e8499ea48377f2a3", &mac256); |
| 1013 | |
| 1014 | Aegis128X2Mac.createWithNonce(&mac256, &msg, &key, &nonce); |
| 1015 | Aegis128X2Mac_128.createWithNonce(&mac128, &msg, &key, &nonce); |
| 1016 | try htest.assertEqual("7aa41edfd57a95c1108d83c63b8d4d01", &mac128); |
| 1017 | try htest.assertEqual("55b6449929cd2b01d04786e57698b3ddfb5cbf6e421bbd022637a33d60f40294", &mac256); |
| 1018 | |
| 1019 | Aegis128X4Mac.createWithNonce(&mac256, &msg, &key, &nonce); |
| 1020 | Aegis128X4Mac_128.createWithNonce(&mac128, &msg, &key, &nonce); |
| 1021 | try htest.assertEqual("46a194ea4337bb32c2186a99e312f3a7", &mac128); |
| 1022 | try htest.assertEqual("ea884072699569532fb68ae9fb2653c9ffef3e974333d3a17d77be02453cc12f", &mac256); |
| 1023 | } |
| 1024 | |
| 1025 | test "AEGISMAC-256* test vectors" { |
| 1026 | const key = [_]u8{ 0x10, 0x01 } ++ [_]u8{0x00} ** (32 - 2); |
| 1027 | const nonce = [_]u8{ 0x10, 0x00, 0x02 } ++ [_]u8{0x00} ** (32 - 3); |
| 1028 | var msg: [35]u8 = undefined; |
| 1029 | for (&msg, 0..) |*byte, i| byte.* = @truncate(i); |
| 1030 | var mac128: [16]u8 = undefined; |
| 1031 | var mac256: [32]u8 = undefined; |
| 1032 | |
| 1033 | Aegis256Mac.createWithNonce(&mac256, &msg, &key, &nonce); |
| 1034 | Aegis256Mac_128.createWithNonce(&mac128, &msg, &key, &nonce); |
| 1035 | try htest.assertEqual("c08e20cfc56f27195a46c9cef5c162d4", &mac128); |
| 1036 | try htest.assertEqual("a5c906ede3d69545c11e20afa360b221f936e946ed2dba3d7c75ad6dc2784126", &mac256); |
| 1037 | |
| 1038 | Aegis256X2Mac.createWithNonce(&mac256, &msg, &key, &nonce); |
| 1039 | Aegis256X2Mac_128.createWithNonce(&mac128, &msg, &key, &nonce); |
| 1040 | try htest.assertEqual("fb319cb6dd728a764606fb14d37f2a5e", &mac128); |
| 1041 | try htest.assertEqual("0844b20ed5147ceae89c7a160263afd4b1382d6b154ecf560ce8a342cb6a8fd1", &mac256); |
| 1042 | |
| 1043 | Aegis256X4Mac.createWithNonce(&mac256, &msg, &key, &nonce); |
| 1044 | Aegis256X4Mac_128.createWithNonce(&mac128, &msg, &key, &nonce); |
| 1045 | try htest.assertEqual("a51f9bc5beae60cce77f0dbc60761edd", &mac128); |
| 1046 | try htest.assertEqual("b36a16ef07c36d75a91f437502f24f545b8dfa88648ed116943c29fead3bf10c", &mac256); |
| 848 | 1047 | } |