| ... | ... | @@ -386,6 +386,9 @@ pub const AsconAead128 = struct { |
| 386 | 386 | self.st.st[0] ^= mem.readInt(u64, &buf, .little); |
| 387 | 387 | } |
| 388 | 388 | self.st.permuteR(8); |
| 389 | } else { |
| 390 | self.st.st[0] ^= 0x01; |
| 391 | self.st.permuteR(8); |
| 389 | 392 | } |
| 390 | 393 | } |
| 391 | 394 | |
| ... | ... | @@ -589,6 +592,7 @@ pub const AsconHash256 = struct { |
| 589 | 592 | pub const block_length = 8; |
| 590 | 593 | |
| 591 | 594 | st: AsconState, |
| 595 | offset: usize, |
| 592 | 596 | |
| 593 | 597 | pub const Options = struct {}; |
| 594 | 598 | |
| ... | ... | @@ -606,7 +610,7 @@ pub const AsconHash256 = struct { |
| 606 | 610 | const words: [5]u64 = .{ iv, 0, 0, 0, 0 }; |
| 607 | 611 | var st = AsconState.initFromWords(words); |
| 608 | 612 | st.permuteR(12); |
| 609 | | return AsconHash256{ .st = st }; |
| 613 | return AsconHash256{ .st = st, .offset = 0 }; |
| 610 | 614 | } |
| 611 | 615 | |
| 612 | 616 | /// Compute Ascon-Hash256 hash of input data in one call. |
| ... | ... | @@ -630,6 +634,13 @@ pub const AsconHash256 = struct { |
| 630 | 634 | pub fn update(self: *AsconHash256, b: []const u8) void { |
| 631 | 635 | var i: usize = 0; |
| 632 | 636 | |
| 637 | // Finish the block left partially absorbed by a previous call |
| 638 | while (self.offset > 0 and i < b.len) : (i += 1) { |
| 639 | self.st.addByte(b[i], self.offset); |
| 640 | self.offset = (self.offset + 1) % block_length; |
| 641 | if (self.offset == 0) self.st.permuteR(12); |
| 642 | } |
| 643 | |
| 633 | 644 | // Process full 64-bit blocks |
| 634 | 645 | while (i + 8 <= b.len) : (i += 8) { |
| 635 | 646 | self.st.addBytes(b[i..][0..8]); |
| ... | ... | @@ -638,16 +649,8 @@ pub const AsconHash256 = struct { |
| 638 | 649 | |
| 639 | 650 | // Store partial block for finalization |
| 640 | 651 | if (i < b.len) { |
| 641 | | var padded: [8]u8 = @splat(0); |
| 642 | | const remaining = b.len - i; |
| 643 | | @memcpy(padded[0..remaining], b[i..]); |
| 644 | | padded[remaining] = 0x01; |
| 645 | | self.st.addBytes(&padded); |
| 646 | | } else { |
| 647 | | // Add padding block |
| 648 | | var padded: [8]u8 = @splat(0); |
| 649 | | padded[0] = 0x01; |
| 650 | | self.st.addBytes(&padded); |
| 652 | self.st.addBytes(b[i..]); |
| 653 | self.offset = b.len - i; |
| 651 | 654 | } |
| 652 | 655 | } |
| 653 | 656 | |
| ... | ... | @@ -658,6 +661,7 @@ pub const AsconHash256 = struct { |
| 658 | 661 | /// |
| 659 | 662 | /// Note: After calling final(), the hasher should not be used again |
| 660 | 663 | pub fn final(self: *AsconHash256, out: *[digest_length]u8) void { |
| 664 | self.st.addByte(0x01, self.offset); |
| 661 | 665 | // Final permutation after padding |
| 662 | 666 | self.st.permuteR(12); |
| 663 | 667 | |
| ... | ... | @@ -772,19 +776,21 @@ pub const AsconXof128 = struct { |
| 772 | 776 | self.st.addBytes(&padded); |
| 773 | 777 | self.st.permuteR(12); |
| 774 | 778 | self.squeezed = true; |
| 779 | self.buf_len = 0; |
| 775 | 780 | } |
| 776 | 781 | |
| 777 | 782 | var i: usize = 0; |
| 778 | 783 | while (i < out.len) { |
| 779 | | const to_copy = @min(8, out.len - i); |
| 784 | if (self.buf_len == block_length) { |
| 785 | self.st.permuteR(12); |
| 786 | self.buf_len = 0; |
| 787 | } |
| 788 | const to_copy = @min(block_length - self.buf_len, out.len - i); |
| 780 | 789 | var block: [8]u8 = undefined; |
| 781 | 790 | mem.writeInt(u64, &block, self.st.st[0], .little); |
| 782 | | @memcpy(out[i..][0..to_copy], block[0..to_copy]); |
| 791 | @memcpy(out[i..][0..to_copy], block[self.buf_len..][0..to_copy]); |
| 783 | 792 | i += to_copy; |
| 784 | | |
| 785 | | if (i < out.len) { |
| 786 | | self.st.permuteR(12); |
| 787 | | } |
| 793 | self.buf_len += to_copy; |
| 788 | 794 | } |
| 789 | 795 | } |
| 790 | 796 | }; |
| ... | ... | @@ -924,19 +930,21 @@ pub const AsconCxof128 = struct { |
| 924 | 930 | self.st.addBytes(&padded); |
| 925 | 931 | self.st.permuteR(12); |
| 926 | 932 | self.squeezed = true; |
| 933 | self.buf_len = 0; |
| 927 | 934 | } |
| 928 | 935 | |
| 929 | 936 | var i: usize = 0; |
| 930 | 937 | while (i < out.len) { |
| 931 | | const to_copy = @min(8, out.len - i); |
| 938 | if (self.buf_len == block_length) { |
| 939 | self.st.permuteR(12); |
| 940 | self.buf_len = 0; |
| 941 | } |
| 942 | const to_copy = @min(block_length - self.buf_len, out.len - i); |
| 932 | 943 | var block: [8]u8 = undefined; |
| 933 | 944 | mem.writeInt(u64, &block, self.st.st[0], .little); |
| 934 | | @memcpy(out[i..][0..to_copy], block[0..to_copy]); |
| 945 | @memcpy(out[i..][0..to_copy], block[self.buf_len..][0..to_copy]); |
| 935 | 946 | i += to_copy; |
| 936 | | |
| 937 | | if (i < out.len) { |
| 938 | | self.st.permuteR(12); |
| 939 | | } |
| 947 | self.buf_len += to_copy; |
| 940 | 948 | } |
| 941 | 949 | } |
| 942 | 950 | }; |
| ... | ... | @@ -949,6 +957,11 @@ test "Ascon-Hash256 basic test" { |
| 949 | 957 | |
| 950 | 958 | // Verify hash is generated (exact value depends on test vectors) |
| 951 | 959 | try testing.expect(hash.len == 32); |
| 960 | |
| 961 | var h = AsconHash256.init(.{}); |
| 962 | h.update(message[0..5]); |
| 963 | h.update(message[5..]); |
| 964 | try testing.expectEqualSlices(u8, &hash, &h.finalResult()); |
| 952 | 965 | } |
| 953 | 966 | |
| 954 | 967 | test "Ascon-XOF128 basic test" { |
| ... | ... | @@ -1122,6 +1135,10 @@ test "Ascon-AEAD128 official test vectors" { |
| 1122 | 1135 | var expected_tag: [16]u8 = undefined; |
| 1123 | 1136 | _ = std.fmt.hexToBytes(&expected_tag, "CCCB674FE18A09A285D6AB11B35675C0") catch unreachable; |
| 1124 | 1137 | try testing.expectEqualSlices(u8, &expected_tag, &tag); |
| 1138 | |
| 1139 | AsconAead128.encrypt(&ciphertext, &tag, plaintext, "0123456789:;<=>?", nonce, key); |
| 1140 | _ = std.fmt.hexToBytes(&expected_tag, "E4230CDB8330EE9DC0CFD7C7B346E6DC") catch unreachable; |
| 1141 | try testing.expectEqualSlices(u8, &expected_tag, &tag); |
| 1125 | 1142 | } |
| 1126 | 1143 | |
| 1127 | 1144 | // Test vector 34: Single byte plaintext 0x20 |
| ... | ... | @@ -1297,7 +1314,8 @@ test "Ascon-XOF128/CXOF128 streaming chunking invariance" { |
| 1297 | 1314 | var xof2 = AsconXof128.init(.{}); |
| 1298 | 1315 | xof2.update("Hello, "); |
| 1299 | 1316 | xof2.update("World!"); |
| 1300 | | xof2.squeeze(&out2); |
| 1317 | xof2.squeeze(out2[0..3]); |
| 1318 | xof2.squeeze(out2[3..]); |
| 1301 | 1319 | try testing.expectEqualSlices(u8, &out1, &out2); |
| 1302 | 1320 | |
| 1303 | 1321 | // CXOF128: one-shot vs split must match |
| ... | ... | @@ -1309,7 +1327,8 @@ test "Ascon-XOF128/CXOF128 streaming chunking invariance" { |
| 1309 | 1327 | var cxof2 = AsconCxof128.init(.{ .custom = "cust" }); |
| 1310 | 1328 | cxof2.update("Hello, "); |
| 1311 | 1329 | cxof2.update("World!"); |
| 1312 | | cxof2.squeeze(&cout2); |
| 1330 | cxof2.squeeze(cout2[0..8]); |
| 1331 | cxof2.squeeze(cout2[8..]); |
| 1313 | 1332 | try testing.expectEqualSlices(u8, &cout1, &cout2); |
| 1314 | 1333 | } |
| 1315 | 1334 | |