Commit 4406127cca
Changed files (3)
lib
std
lib/std/crypto/ascon.zig
@@ -9,6 +9,7 @@
const std = @import("std");
const builtin = @import("builtin");
+const crypto = std.crypto;
const debug = std.debug;
const mem = std.mem;
const testing = std.testing;
@@ -34,6 +35,11 @@ pub fn State(comptime endian: std.builtin.Endian) type {
st: Block,
/// Initialize the state from a slice of bytes.
+ ///
+ /// Parameters:
+ /// - initial_state: A 40-byte array to initialize the state
+ ///
+ /// Returns: A new State initialized with the provided bytes
pub fn init(initial_state: [block_bytes]u8) Self {
var state = Self{ .st = undefined };
@memcpy(state.asBytes(), &initial_state);
@@ -42,11 +48,18 @@ pub fn State(comptime endian: std.builtin.Endian) type {
}
/// Initialize the state from u64 words in native endianness.
+ ///
+ /// Parameters:
+ /// - initial_state: An array of 5 u64 words in native endianness
+ ///
+ /// Returns: A new State with the provided words
pub fn initFromWords(initial_state: [5]u64) Self {
return .{ .st = initial_state };
}
- /// Initialize the state for Ascon XOF
+ /// Initialize the state for Ascon XOF.
+ ///
+ /// Returns: A new State initialized with the Ascon XOF initialization vector
pub fn initXof() Self {
return Self{ .st = Block{
0xb57e273b814cd416,
@@ -57,7 +70,9 @@ pub fn State(comptime endian: std.builtin.Endian) type {
} };
}
- /// Initialize the state for Ascon XOFa
+ /// Initialize the state for Ascon XOFa.
+ ///
+ /// Returns: A new State initialized with the Ascon XOFa initialization vector
pub fn initXofA() Self {
return Self{ .st = Block{
0x44906568b77b9832,
@@ -69,11 +84,15 @@ pub fn State(comptime endian: std.builtin.Endian) type {
}
/// A representation of the state as bytes. The byte order is architecture-dependent.
+ ///
+ /// Returns: A pointer to the state's internal byte representation
pub fn asBytes(self: *Self) *[block_bytes]u8 {
return mem.asBytes(&self.st);
}
/// Byte-swap the entire state if the architecture doesn't match the required endianness.
+ ///
+ /// This ensures the state is in the correct endianness for the current platform.
pub fn endianSwap(self: *Self) void {
for (&self.st) |*w| {
w.* = mem.toNative(u64, w.*, endian);
@@ -81,19 +100,28 @@ pub fn State(comptime endian: std.builtin.Endian) type {
}
/// Set bytes starting at the beginning of the state.
+ ///
+ /// Parameters:
+ /// - bytes: Slice of bytes to write into the state (up to 40 bytes)
+ ///
+ /// Note: If bytes.len < 40, remaining state words are zero-padded
pub fn setBytes(self: *Self, bytes: []const u8) void {
var i: usize = 0;
while (i + 8 <= bytes.len) : (i += 8) {
self.st[i / 8] = mem.readInt(u64, bytes[i..][0..8], endian);
}
if (i < bytes.len) {
- var padded = [_]u8{0} ** 8;
+ var padded: [8]u8 = @splat(0);
@memcpy(padded[0 .. bytes.len - i], bytes[i..]);
self.st[i / 8] = mem.readInt(u64, padded[0..], endian);
}
}
/// XOR a byte into the state at a given offset.
+ ///
+ /// Parameters:
+ /// - byte: The byte to XOR into the state
+ /// - offset: The byte offset in the state (0-39)
pub fn addByte(self: *Self, byte: u8, offset: usize) void {
const z = switch (endian) {
.big => 64 - 8 - 8 * @as(u6, @truncate(offset % 8)),
@@ -103,32 +131,48 @@ pub fn State(comptime endian: std.builtin.Endian) type {
}
/// XOR bytes into the beginning of the state.
+ ///
+ /// Parameters:
+ /// - bytes: Slice of bytes to XOR into the state (up to 40 bytes)
+ ///
+ /// Note: Handles partial blocks with zero-padding
pub fn addBytes(self: *Self, bytes: []const u8) void {
var i: usize = 0;
while (i + 8 <= bytes.len) : (i += 8) {
self.st[i / 8] ^= mem.readInt(u64, bytes[i..][0..8], endian);
}
if (i < bytes.len) {
- var padded = [_]u8{0} ** 8;
+ var padded: [8]u8 = @splat(0);
@memcpy(padded[0 .. bytes.len - i], bytes[i..]);
self.st[i / 8] ^= mem.readInt(u64, padded[0..], endian);
}
}
/// Extract the first bytes of the state.
+ ///
+ /// Parameters:
+ /// - out: Output buffer to receive the extracted bytes
+ ///
+ /// Note: Extracts up to out.len bytes from the beginning of the state
pub fn extractBytes(self: *Self, out: []u8) void {
var i: usize = 0;
while (i + 8 <= out.len) : (i += 8) {
mem.writeInt(u64, out[i..][0..8], self.st[i / 8], endian);
}
if (i < out.len) {
- var padded = [_]u8{0} ** 8;
+ var padded: [8]u8 = @splat(0);
mem.writeInt(u64, padded[0..], self.st[i / 8], endian);
@memcpy(out[i..], padded[0 .. out.len - i]);
}
}
/// XOR the first bytes of the state into a slice of bytes.
+ ///
+ /// Parameters:
+ /// - out: Output buffer for the XORed result
+ /// - in: Input bytes to XOR with the state
+ ///
+ /// Requires: out.len == in.len
pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
debug.assert(out.len == in.len);
@@ -138,7 +182,7 @@ pub fn State(comptime endian: std.builtin.Endian) type {
mem.writeInt(u64, out[i..][0..8], x, native_endian);
}
if (i < in.len) {
- var padded = [_]u8{0} ** 8;
+ var padded: [8]u8 = @splat(0);
@memcpy(padded[0 .. in.len - i], in[i..]);
const x = mem.readInt(u64, &padded, native_endian) ^ mem.nativeTo(u64, self.st[i / 8], endian);
mem.writeInt(u64, &padded, x, native_endian);
@@ -147,16 +191,30 @@ pub fn State(comptime endian: std.builtin.Endian) type {
}
/// Set the words storing the bytes of a given range to zero.
+ ///
+ /// Parameters:
+ /// - from: Starting byte offset (inclusive)
+ /// - to: Ending byte offset (inclusive)
+ ///
+ /// Note: Clears complete words that contain the specified byte range
pub fn clear(self: *Self, from: usize, to: usize) void {
@memset(self.st[from / 8 .. (to + 7) / 8], 0);
}
/// Clear the entire state, disabling compiler optimizations.
+ ///
+ /// Uses secure zeroing to prevent the compiler from optimizing away
+ /// the clearing operation. Use for sensitive data cleanup.
pub fn secureZero(self: *Self) void {
- std.crypto.secureZero(u64, &self.st);
+ crypto.secureZero(u64, &self.st);
}
/// Apply a reduced-round permutation to the state.
+ ///
+ /// Parameters:
+ /// - rounds: Number of rounds to apply (1-12)
+ ///
+ /// Note: Uses the last `rounds` round constants from the full set
pub fn permuteR(state: *Self, comptime rounds: u4) void {
const rks = [16]u64{ 0x3c, 0x2d, 0x1e, 0x0f, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
inline for (rks[rks.len - rounds ..]) |rk| {
@@ -165,12 +223,20 @@ pub fn State(comptime endian: std.builtin.Endian) type {
}
/// Apply a full-round permutation to the state.
+ ///
+ /// Applies the standard 12-round Ascon permutation.
pub fn permute(state: *Self) void {
state.permuteR(12);
}
/// Apply a permutation to the state and prevent backtracking.
- /// The rate is expressed in bytes and must be a multiple of the word size (8).
+ ///
+ /// Parameters:
+ /// - rounds: Number of permutation rounds to apply
+ /// - rate: Rate in bytes (must be multiple of 8, < 40)
+ ///
+ /// The capacity portion is XORed before and after permutation to
+ /// provide forward security (ratcheting).
pub fn permuteRatchet(state: *Self, comptime rounds: u4, comptime rate: u6) void {
const capacity = block_bytes - rate;
debug.assert(capacity > 0 and capacity % 8 == 0); // capacity must be a multiple of 64 bits
@@ -180,7 +246,12 @@ pub fn State(comptime endian: std.builtin.Endian) type {
inline for (mask, state.st[state.st.len - mask.len ..]) |m, *x| x.* ^= m;
}
- // Core Ascon permutation.
+ /// Core Ascon permutation round function.
+ ///
+ /// Parameters:
+ /// - rk: Round constant for this round
+ ///
+ /// Implements one round of the Ascon permutation with S-box and linear layer.
fn round(state: *Self, rk: u64) void {
const x = &state.st;
x[2] ^= rk;
@@ -216,7 +287,8 @@ pub fn State(comptime endian: std.builtin.Endian) type {
test "ascon" {
const Ascon = State(.big);
- const bytes = [_]u8{0x01} ** Ascon.block_bytes;
+ var bytes: [Ascon.block_bytes]u8 = undefined;
+ @memset(&bytes, 1);
var st = Ascon.init(bytes);
var out: [Ascon.block_bytes]u8 = undefined;
st.permute();
@@ -237,3 +309,1002 @@ test "ascon" {
const expected4 = [_]u8{ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 168, 207, 64, 19, 214, 96, 79, 107, 119, 80, 210, 151, 53, 16, 116, 65, 217, 44, 149, 241, 64, 180, 91, 181 };
try testing.expectEqualSlices(u8, &expected4, &out);
}
+
+const AsconState = State(.little);
+const AuthenticationError = crypto.errors.AuthenticationError;
+
+/// Ascon-AEAD128 as specified in NIST SP 800-232 Section 4
+pub const AsconAead128 = struct {
+ pub const tag_length = 16;
+ pub const nonce_length = 16;
+ pub const key_length = 16;
+ pub const block_length = 16;
+
+ const AeadState = struct {
+ st: AsconState,
+ k0: u64,
+ k1: u64,
+
+ /// Initialize AEAD state with key and nonce.
+ ///
+ /// Parameters:
+ /// - key: 16-byte secret key
+ /// - nonce: 16-byte nonce
+ ///
+ /// Returns: Initialized AEAD state ready for processing
+ fn init(key: [16]u8, nonce: [16]u8) AeadState {
+ const k0 = mem.readInt(u64, key[0..8], .little);
+ const k1 = mem.readInt(u64, key[8..16], .little);
+ const n0 = mem.readInt(u64, nonce[0..8], .little);
+ const n1 = mem.readInt(u64, nonce[8..16], .little);
+
+ // IV for Ascon-AEAD128 (Ascon-128a)
+ const iv: u64 = 0x00001000808C0001;
+ const words: [5]u64 = .{ iv, k0, k1, n0, n1 };
+
+ var st = AsconState.initFromWords(words);
+ st.permuteR(12);
+
+ st.st[3] ^= k0;
+ st.st[4] ^= k1;
+
+ return AeadState{ .st = st, .k0 = k0, .k1 = k1 };
+ }
+
+ /// Process associated data for authentication.
+ ///
+ /// Parameters:
+ /// - ad: Associated data to authenticate
+ ///
+ /// Updates the state to include AD in authentication tag computation.
+ fn processAd(self: *AeadState, ad: []const u8) void {
+ if (ad.len == 0) return;
+
+ var i: usize = 0;
+ // Process full 128-bit blocks
+ while (i + 16 <= ad.len) : (i += 16) {
+ self.st.addBytes(ad[i..][0..16]);
+ self.st.permuteR(8);
+ }
+
+ // Process final partial AD block
+ const adrem = ad.len - i;
+ if (adrem > 0) {
+ if (adrem >= 8) {
+ var buf: [8]u8 = @splat(0);
+ @memcpy(buf[0..8], ad[i..][0..8]);
+ self.st.st[0] ^= mem.readInt(u64, &buf, .little);
+
+ buf = @splat(0);
+ @memcpy(buf[0 .. adrem - 8], ad[i + 8 ..]);
+ buf[adrem - 8] = 0x01;
+ self.st.st[1] ^= mem.readInt(u64, &buf, .little);
+ } else {
+ var buf: [8]u8 = @splat(0);
+ @memcpy(buf[0..adrem], ad[i..]);
+ buf[adrem] = 0x01;
+ self.st.st[0] ^= mem.readInt(u64, &buf, .little);
+ }
+ self.st.permuteR(8);
+ }
+ }
+
+ /// Finalize the AEAD operation and prepare tag.
+ ///
+ /// Applies final permutation and XORs key for tag generation.
+ fn finalize(self: *AeadState) void {
+ // XOR key before final permutation
+ self.st.st[2] ^= self.k0;
+ self.st.st[3] ^= self.k1;
+ self.st.permuteR(12);
+
+ // XOR key again for tag generation
+ self.st.st[3] ^= self.k0;
+ self.st.st[4] ^= self.k1;
+ }
+ };
+
+ /// Encrypt a message with Ascon-AEAD128.
+ ///
+ /// Parameters:
+ /// - c: Output buffer for ciphertext (must be same length as m)
+ /// - tag: Output buffer for authentication tag (16 bytes)
+ /// - m: Plaintext message to encrypt
+ /// - ad: Associated data to authenticate but not encrypt
+ /// - npub: Public nonce (16 bytes, must be unique per message)
+ /// - k: Secret key (16 bytes)
+ ///
+ /// Note: The ciphertext and tag must be transmitted together for decryption
+ pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
+ debug.assert(c.len == m.len);
+
+ var state = AeadState.init(k, npub);
+
+ // Process associated data
+ state.processAd(ad);
+
+ // Domain separation (DSEP = 0x80 at byte 7 in little-endian)
+ state.st.st[4] ^= 0x8000000000000000;
+
+ // Process plaintext
+ var i: usize = 0;
+ while (i + 16 <= m.len) : (i += 16) {
+ state.st.addBytes(m[i..][0..16]);
+ state.st.extractBytes(c[i..][0..16]);
+ state.st.permuteR(8);
+ }
+
+ // Process final partial block
+ const remaining = m.len - i;
+ if (remaining > 8) {
+ // Split between two words
+ state.st.addBytes(m[i..][0..8]);
+ state.st.extractBytes(c[i..][0..8]);
+
+ var buf: [8]u8 = @splat(0);
+ @memcpy(buf[0 .. remaining - 8], m[i + 8 ..]);
+ const m1 = mem.readInt(u64, &buf, .little);
+ state.st.st[1] ^= m1;
+ mem.writeInt(u64, buf[0..], state.st.st[1], .little);
+ @memcpy(c[i + 8 ..], buf[0 .. remaining - 8]);
+
+ // Add padding
+ state.st.st[1] ^= @as(u64, 0x01) << @intCast((remaining - 8) * 8);
+ } else if (remaining == 8) {
+ // Exactly 8 bytes - all in word 0, padding in word 1
+ state.st.addBytes(m[i..][0..8]);
+ state.st.extractBytes(c[i..][0..8]);
+
+ // Add padding to word 1 at position 0
+ state.st.st[1] ^= 0x01;
+ } else if (remaining > 0) {
+ // All in first word
+ var temp: [8]u8 = @splat(0);
+ @memcpy(temp[0..remaining], m[i..]);
+ state.st.addBytes(&temp);
+ state.st.extractBytes(c[i..][0..remaining]);
+ // Add padding
+ temp = @splat(0);
+ temp[remaining] = 0x01;
+ state.st.addBytes(&temp);
+ // Second word stays zero
+ } else {
+ // Empty message or exact multiple - add padding block
+ var padded: [16]u8 = @splat(0);
+ padded[0] = 0x01;
+ state.st.addBytes(&padded);
+ }
+
+ // Finalization
+ state.finalize();
+
+ // Extract tag
+ mem.writeInt(u64, tag[0..8], state.st.st[3], .little);
+ mem.writeInt(u64, tag[8..16], state.st.st[4], .little);
+ }
+
+ /// Decrypt a message with Ascon-AEAD128.
+ ///
+ /// Parameters:
+ /// - m: Output buffer for plaintext (must be same length as c)
+ /// - c: Ciphertext to decrypt
+ /// - tag: Authentication tag (16 bytes)
+ /// - ad: Associated data that was authenticated
+ /// - npub: Public nonce used during encryption (16 bytes)
+ /// - k: Secret key (16 bytes)
+ ///
+ /// Returns: AuthenticationError if tag verification fails
+ ///
+ /// Note: On authentication failure, the output buffer is securely zeroed
+ pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
+ debug.assert(m.len == c.len);
+
+ var state = AeadState.init(k, npub);
+
+ // Process associated data
+ state.processAd(ad);
+
+ // Domain separation (DSEP = 0x80 at byte 7 in little-endian)
+ state.st.st[4] ^= 0x8000000000000000;
+
+ // Process ciphertext
+ var i: usize = 0;
+ while (i + 16 <= c.len) : (i += 16) {
+ const ct_block = c[i..][0..16].*; // Save ciphertext block for in-place operation support
+ state.st.xorBytes(m[i..][0..16], &ct_block);
+ state.st.setBytes(&ct_block);
+ state.st.permuteR(8);
+ }
+
+ // Final partial ciphertext block
+ const crem = c.len - i;
+ if (crem > 8) {
+ // Save ciphertext for in-place operation support
+ var saved_ct: [16]u8 = undefined;
+ @memcpy(saved_ct[0..crem], c[i..]);
+
+ const c0 = mem.readInt(u64, saved_ct[0..8], .little);
+ state.st.st[0] ^= c0;
+ mem.writeInt(u64, m[i..][0..8], state.st.st[0], .little);
+ state.st.st[0] = c0;
+
+ var buf: [8]u8 = @splat(0);
+ @memcpy(buf[0 .. crem - 8], saved_ct[8..][0 .. crem - 8]);
+ const c1 = mem.readInt(u64, &buf, .little);
+ const m1 = state.st.st[1] ^ c1;
+ mem.writeInt(u64, buf[0..], m1, .little);
+ @memcpy(m[i + 8 ..], buf[0 .. crem - 8]);
+
+ // Replace only the bytes we've read, keeping upper bytes intact
+ const mask = (@as(u64, 1) << @intCast((crem - 8) * 8)) - 1;
+ state.st.st[1] = (state.st.st[1] & ~mask) | (c1 & mask);
+
+ state.st.st[1] ^= @as(u64, 0x01) << @intCast((crem - 8) * 8);
+ } else if (crem == 8) {
+ // Exactly 8 bytes - process only word 0, add padding to word 1
+ const saved_ct = c[i..][0..8].*;
+
+ const c0 = mem.readInt(u64, &saved_ct, .little);
+ state.st.st[0] ^= c0;
+ mem.writeInt(u64, m[i..][0..8], state.st.st[0], .little);
+ state.st.st[0] = c0;
+
+ // Add padding to word 1 at position 0
+ state.st.st[1] ^= 0x01;
+ } else if (crem > 0) {
+ var buf: [8]u8 = @splat(0);
+ @memcpy(buf[0..crem], c[i..]);
+ const c0 = mem.readInt(u64, &buf, .little);
+ const m0 = state.st.st[0] ^ c0;
+ mem.writeInt(u64, buf[0..], m0, .little);
+ @memcpy(m[i..], buf[0..crem]);
+
+ // Replace only the bytes we've read, keeping upper bytes intact
+ const mask = (@as(u64, 1) << @intCast(crem * 8)) - 1;
+ state.st.st[0] = (state.st.st[0] & ~mask) | (c0 & mask);
+
+ state.st.st[0] ^= @as(u64, 0x01) << @intCast(crem * 8);
+ } else {
+ state.st.st[0] ^= 0x01;
+ }
+
+ // Finalization
+ state.finalize();
+
+ // Verify tag
+ var computed_tag: [tag_length]u8 = undefined;
+ mem.writeInt(u64, computed_tag[0..8], state.st.st[3], .little);
+ mem.writeInt(u64, computed_tag[8..16], state.st.st[4], .little);
+
+ if (!crypto.timing_safe.eql([tag_length]u8, tag, computed_tag)) {
+ crypto.secureZero(u8, m);
+ return error.AuthenticationFailed;
+ }
+ }
+};
+
+/// Ascon-Hash256 as specified in NIST SP 800-232 Section 5
+pub const AsconHash256 = struct {
+ pub const digest_length = 32;
+ pub const block_length = 8;
+
+ st: AsconState,
+
+ pub const Options = struct {};
+
+ /// Initialize a new Ascon-Hash256 hasher.
+ ///
+ /// Parameters:
+ /// - options: Configuration options (currently unused)
+ ///
+ /// Returns: An initialized AsconHash256 hasher
+ pub fn init(options: Options) AsconHash256 {
+ _ = options;
+
+ // IV for Ascon-Hash256: 0x0000080100cc0002
+ const iv: u64 = 0x0000080100cc0002;
+ const words: [5]u64 = .{ iv, 0, 0, 0, 0 };
+ var st = AsconState.initFromWords(words);
+ st.permuteR(12);
+ return AsconHash256{ .st = st };
+ }
+
+ /// Compute Ascon-Hash256 hash of input data in one call.
+ ///
+ /// Parameters:
+ /// - b: Input data to hash
+ /// - out: Output buffer for 32-byte hash digest
+ /// - options: Configuration options (currently unused)
+ pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
+ var h = init(options);
+ h.update(b);
+ h.final(out);
+ }
+
+ /// Update the hash state with additional data.
+ ///
+ /// Parameters:
+ /// - b: Data to add to the hash
+ ///
+ /// Note: Can be called multiple times before final()
+ pub fn update(self: *AsconHash256, b: []const u8) void {
+ var i: usize = 0;
+
+ // Process full 64-bit blocks
+ while (i + 8 <= b.len) : (i += 8) {
+ self.st.addBytes(b[i..][0..8]);
+ self.st.permuteR(12);
+ }
+
+ // Store partial block for finalization
+ if (i < b.len) {
+ var padded: [8]u8 = @splat(0);
+ const remaining = b.len - i;
+ @memcpy(padded[0..remaining], b[i..]);
+ padded[remaining] = 0x01;
+ self.st.addBytes(&padded);
+ } else {
+ // Add padding block
+ var padded: [8]u8 = @splat(0);
+ padded[0] = 0x01;
+ self.st.addBytes(&padded);
+ }
+ }
+
+ /// Finalize the hash and output the digest.
+ ///
+ /// Parameters:
+ /// - out: Output buffer for 32-byte hash digest
+ ///
+ /// Note: After calling final(), the hasher should not be used again
+ pub fn final(self: *AsconHash256, out: *[digest_length]u8) void {
+ // Final permutation after padding
+ self.st.permuteR(12);
+
+ // Extract hash output (4 ร 64 bits = 256 bits)
+ var h: [4]u64 = undefined;
+ for (0..4) |i| {
+ h[i] = self.st.st[0];
+ self.st.permuteR(12);
+ }
+
+ // Write output
+ for (0..4) |i| {
+ mem.writeInt(u64, out[i * 8 ..][0..8], h[i], .little);
+ }
+ }
+};
+
+/// Ascon-XOF128 as specified in NIST SP 800-232 Section 5
+pub const AsconXof128 = struct {
+ pub const block_length = 8;
+
+ st: AsconState,
+ squeezed: bool,
+
+ pub const Options = struct {};
+
+ /// Initialize a new Ascon-XOF128 extendable output function.
+ ///
+ /// Parameters:
+ /// - options: Configuration options (currently unused)
+ ///
+ /// Returns: An initialized AsconXof128 instance
+ pub fn init(options: Options) AsconXof128 {
+ _ = options;
+
+ // IV for Ascon-XOF128: 0x0000080000cc0003
+ const iv: u64 = 0x0000080000cc0003;
+ const words: [5]u64 = .{ iv, 0, 0, 0, 0 };
+ var st = AsconState.initFromWords(words);
+ st.permuteR(12);
+ return AsconXof128{ .st = st, .squeezed = false };
+ }
+
+ /// Hash a slice of bytes with variable-length output.
+ ///
+ /// Parameters:
+ /// - bytes: Input data to hash
+ /// - out: Output buffer (can be any length)
+ /// - options: Configuration options (currently unused)
+ ///
+ /// Note: Convenience function that combines init, update, and squeeze
+ pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
+ var st = init(options);
+ st.update(bytes);
+ st.squeeze(out);
+ }
+
+ /// Update the XOF state with additional data.
+ ///
+ /// Parameters:
+ /// - b: Data to absorb into the XOF state
+ ///
+ /// Note: Cannot be called after squeeze() has been called
+ pub fn update(self: *AsconXof128, b: []const u8) void {
+ debug.assert(!self.squeezed); // Cannot update after squeezing
+
+ var i: usize = 0;
+
+ // Process full 64-bit blocks
+ while (i + 8 <= b.len) : (i += 8) {
+ self.st.addBytes(b[i..][0..8]);
+ self.st.permuteR(12);
+ }
+
+ // Store partial block for finalization
+ if (i < b.len) {
+ var padded: [8]u8 = @splat(0);
+ const remaining = b.len - i;
+ @memcpy(padded[0..remaining], b[i..]);
+ padded[remaining] = 0x01;
+ self.st.addBytes(&padded);
+ } else {
+ // Add padding block
+ var padded: [8]u8 = @splat(0);
+ padded[0] = 0x01;
+ self.st.addBytes(&padded);
+ }
+ }
+
+ /// Squeeze output bytes from the XOF.
+ ///
+ /// Parameters:
+ /// - out: Output buffer to fill with pseudorandom bytes
+ ///
+ /// Note: Can be called multiple times to generate more output.
+ /// After first call, no more data can be absorbed with update().
+ pub fn squeeze(self: *AsconXof128, out: []u8) void {
+ if (!self.squeezed) {
+ // First squeeze - apply final permutation
+ self.st.permuteR(12);
+ self.squeezed = true;
+ }
+
+ var i: usize = 0;
+ while (i < out.len) {
+ const to_copy = @min(8, out.len - i);
+ var block: [8]u8 = undefined;
+ mem.writeInt(u64, &block, self.st.st[0], .little);
+ @memcpy(out[i..][0..to_copy], block[0..to_copy]);
+ i += to_copy;
+
+ if (i < out.len) {
+ self.st.permuteR(12);
+ }
+ }
+ }
+};
+
+/// Ascon-CXOF128 as specified in NIST SP 800-232 Section 5
+pub const AsconCxof128 = struct {
+ pub const block_length = 8;
+ pub const max_custom_length = 256; // 2048 bits
+
+ st: AsconState,
+ squeezed: bool,
+
+ pub const Options = struct { custom: []const u8 = "" };
+
+ /// Initialize a new Ascon-CXOF128 customizable XOF.
+ ///
+ /// Parameters:
+ /// - options: Configuration with optional customization string
+ /// - custom: Customization string (max 256 bytes)
+ ///
+ /// Returns: An initialized AsconCxof128 instance
+ ///
+ /// Note: Different customization strings produce independent XOF instances
+ pub fn init(options: Options) AsconCxof128 {
+ debug.assert(options.custom.len <= max_custom_length);
+
+ // IV for Ascon-CXOF128: 0x0000080000cc0004
+ const iv: u64 = 0x0000080000cc0004;
+ const words: [5]u64 = .{ iv, 0, 0, 0, 0 };
+ var st = AsconState.initFromWords(words);
+ st.permuteR(12);
+
+ var self = AsconCxof128{ .st = st, .squeezed = false };
+
+ // Process customization string - always process length and padding
+ // First block: length of customization string
+ const len_block = @as(u64, options.custom.len * 8); // Length in bits
+ self.st.st[0] ^= len_block;
+ self.st.permuteR(12);
+
+ if (options.custom.len > 0) {
+ // Process customization string blocks
+ var i: usize = 0;
+ while (i + 8 <= options.custom.len) : (i += 8) {
+ self.st.addBytes(options.custom[i..][0..8]);
+ self.st.permuteR(12);
+ }
+
+ // Process final partial block with padding
+ if (i < options.custom.len) {
+ var padded: [8]u8 = @splat(0);
+ const remaining = options.custom.len - i;
+ @memcpy(padded[0..remaining], options.custom[i..]);
+ padded[remaining] = 0x01;
+ self.st.addBytes(&padded);
+ self.st.permuteR(12);
+ } else {
+ // Add padding block
+ var padded: [8]u8 = @splat(0);
+ padded[0] = 0x01;
+ self.st.addBytes(&padded);
+ self.st.permuteR(12);
+ }
+ } else {
+ // Empty customization still needs padding
+ var padded: [8]u8 = @splat(0);
+ padded[0] = 0x01;
+ self.st.addBytes(&padded);
+ self.st.permuteR(12);
+ }
+
+ return self;
+ }
+
+ /// Hash a slice of bytes with customization and variable-length output.
+ ///
+ /// Parameters:
+ /// - bytes: Input data to hash
+ /// - out: Output buffer (can be any length)
+ /// - options: Configuration with optional customization string
+ ///
+ /// Note: Convenience function that combines init, update, and squeeze
+ pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
+ var st = init(options);
+ st.update(bytes);
+ st.squeeze(out);
+ }
+
+ /// Update the CXOF state with additional data.
+ ///
+ /// Parameters:
+ /// - b: Data to absorb into the CXOF state
+ ///
+ /// Note: Cannot be called after squeeze() has been called
+ pub fn update(self: *AsconCxof128, b: []const u8) void {
+ debug.assert(!self.squeezed);
+
+ var i: usize = 0;
+
+ // Process full 64-bit blocks
+ while (i + 8 <= b.len) : (i += 8) {
+ self.st.addBytes(b[i..][0..8]);
+ self.st.permuteR(12);
+ }
+
+ // Store partial block for finalization
+ if (i < b.len) {
+ var padded: [8]u8 = @splat(0);
+ const remaining = b.len - i;
+ @memcpy(padded[0..remaining], b[i..]);
+ padded[remaining] = 0x01;
+ self.st.addBytes(&padded);
+ } else {
+ // Add padding block
+ var padded: [8]u8 = @splat(0);
+ padded[0] = 0x01;
+ self.st.addBytes(&padded);
+ }
+ }
+
+ /// Squeeze output bytes from the customizable XOF.
+ ///
+ /// Parameters:
+ /// - out: Output buffer to fill with pseudorandom bytes
+ ///
+ /// Note: Can be called multiple times to generate more output.
+ /// After first call, no more data can be absorbed with update().
+ pub fn squeeze(self: *AsconCxof128, out: []u8) void {
+ if (!self.squeezed) {
+ // First squeeze - apply final permutation
+ self.st.permuteR(12);
+ self.squeezed = true;
+ }
+
+ var i: usize = 0;
+ while (i < out.len) {
+ const to_copy = @min(8, out.len - i);
+ var block: [8]u8 = undefined;
+ mem.writeInt(u64, &block, self.st.st[0], .little);
+ @memcpy(out[i..][0..to_copy], block[0..to_copy]);
+ i += to_copy;
+
+ if (i < out.len) {
+ self.st.permuteR(12);
+ }
+ }
+ }
+};
+
+test "Ascon-Hash256 basic test" {
+ const message = "The quick brown fox jumps over the lazy dog";
+ var hash: [32]u8 = undefined;
+
+ AsconHash256.hash(message, &hash, .{});
+
+ // Verify hash is generated (exact value depends on test vectors)
+ try testing.expect(hash.len == 32);
+}
+
+test "Ascon-XOF128 basic test" {
+ var xof = AsconXof128.init(.{});
+ xof.update("Hello, ");
+ xof.update("World!");
+
+ var out1: [16]u8 = undefined;
+ xof.squeeze(&out1);
+
+ var out2: [32]u8 = undefined;
+ xof.squeeze(&out2);
+
+ // XOF outputs should be continuous - out2 should NOT match out1
+ // Each squeeze produces new output
+ try testing.expect(!mem.eql(u8, &out1, out2[0..16]));
+}
+
+test "Ascon-CXOF128 with customization" {
+ const custom = "MyCustomString";
+ var xof = AsconCxof128.init(.{ .custom = custom });
+ xof.update("Test message");
+
+ var out: [32]u8 = undefined;
+ xof.squeeze(&out);
+
+ // Different customization should give different output
+ var xof2 = AsconCxof128.init(.{ .custom = "DifferentCustom" });
+ xof2.update("Test message");
+
+ var out2: [32]u8 = undefined;
+ xof2.squeeze(&out2);
+
+ try testing.expect(!mem.eql(u8, &out, &out2));
+}
+
+test "Ascon-AEAD128 round trip with various data sizes" {
+ const key = [_]u8{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 };
+ const nonce = [_]u8{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
+
+ // Test with empty plaintext
+ {
+ const plaintext = "";
+ const ad = "metadata";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ var decrypted: [plaintext.len]u8 = undefined;
+ try AsconAead128.decrypt(&decrypted, &ciphertext, tag, ad, nonce, key);
+ try testing.expectEqualStrings(plaintext, &decrypted);
+ }
+
+ // Test with small plaintext
+ {
+ const plaintext = "Short";
+ const ad = "";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ var decrypted: [plaintext.len]u8 = undefined;
+ try AsconAead128.decrypt(&decrypted, &ciphertext, tag, ad, nonce, key);
+ try testing.expectEqualStrings(plaintext, &decrypted);
+ }
+
+ // Test with longer plaintext and associated data
+ {
+ const plaintext = "This is a longer message to test the round trip encryption and decryption process";
+ const ad = "Additional authenticated data that is not encrypted but is authenticated";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ var decrypted: [plaintext.len]u8 = undefined;
+ try AsconAead128.decrypt(&decrypted, &ciphertext, tag, ad, nonce, key);
+ try testing.expectEqualStrings(plaintext, &decrypted);
+ }
+
+ // Test authentication failure with tampered ciphertext
+ {
+ const plaintext = "Tamper test";
+ const ad = "metadata";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ // Tamper with ciphertext
+ ciphertext[0] ^= 0xFF;
+
+ var decrypted: [plaintext.len]u8 = undefined;
+ const result = AsconAead128.decrypt(&decrypted, &ciphertext, tag, ad, nonce, key);
+ try testing.expectError(error.AuthenticationFailed, result);
+ }
+
+ // Test authentication failure with wrong tag
+ {
+ const plaintext = "Tag test";
+ const ad = "metadata";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ // Tamper with tag
+ var wrong_tag = tag;
+ wrong_tag[0] ^= 0xFF;
+
+ var decrypted: [plaintext.len]u8 = undefined;
+ const result = AsconAead128.decrypt(&decrypted, &ciphertext, wrong_tag, ad, nonce, key);
+ try testing.expectError(error.AuthenticationFailed, result);
+ }
+
+ // Test authentication failure with wrong associated data
+ {
+ const plaintext = "AD test";
+ const ad = "original";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ var decrypted: [plaintext.len]u8 = undefined;
+ const wrong_ad = "modified";
+ const result = AsconAead128.decrypt(&decrypted, &ciphertext, tag, wrong_ad, nonce, key);
+ try testing.expectError(error.AuthenticationFailed, result);
+ }
+}
+
+// Test vectors from NIST SP 800-232 / ascon-c reference implementation
+test "Ascon-AEAD128 official test vectors" {
+
+ // Test vector 1: Empty PT, Empty AD
+ {
+ var key: [16]u8 = undefined;
+ var nonce: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F") catch unreachable;
+ _ = std.fmt.hexToBytes(&nonce, "101112131415161718191A1B1C1D1E1F") catch unreachable;
+
+ const plaintext = "";
+ const ad = "";
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, ad, nonce, key);
+
+ var expected_tag: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected_tag, "4F9C278211BEC9316BF68F46EE8B2EC6") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected_tag, &tag);
+ }
+
+ // Test vector 2: Empty PT, AD = "30"
+ {
+ var key: [16]u8 = undefined;
+ var nonce: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F") catch unreachable;
+ _ = std.fmt.hexToBytes(&nonce, "101112131415161718191A1B1C1D1E1F") catch unreachable;
+
+ const plaintext = "";
+ var ad: [1]u8 = undefined;
+ _ = std.fmt.hexToBytes(&ad, "30") catch unreachable;
+ var ciphertext: [plaintext.len]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, plaintext, &ad, nonce, key);
+
+ var expected_tag: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected_tag, "CCCB674FE18A09A285D6AB11B35675C0") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected_tag, &tag);
+ }
+
+ // Test vector 34: Single byte plaintext 0x20
+ {
+ var key: [16]u8 = undefined;
+ var nonce: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F") catch unreachable;
+ _ = std.fmt.hexToBytes(&nonce, "101112131415161718191A1B1C1D1E1F") catch unreachable;
+
+ var plaintext: [1]u8 = undefined;
+ _ = std.fmt.hexToBytes(&plaintext, "20") catch unreachable;
+ const ad = "";
+ var ciphertext: [1]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, &plaintext, ad, nonce, key);
+
+ var expected_ct: [1]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected_ct, "E8") catch unreachable;
+ var expected_tag: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected_tag, "DD576ABA1CD3E6FC704DE02AEDB79588") catch unreachable;
+
+ try testing.expectEqualSlices(u8, &expected_ct, &ciphertext);
+ try testing.expectEqualSlices(u8, &expected_tag, &tag);
+
+ // Verify decryption
+ var decrypted: [1]u8 = undefined;
+ try AsconAead128.decrypt(&decrypted, &ciphertext, tag, ad, nonce, key);
+ try testing.expectEqualSlices(u8, &plaintext, &decrypted);
+ }
+
+ // Test vector with 3-byte plaintext
+ {
+ var key: [16]u8 = undefined;
+ var nonce: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F") catch unreachable;
+ _ = std.fmt.hexToBytes(&nonce, "101112131415161718191A1B1C1D1E1F") catch unreachable;
+
+ var plaintext: [3]u8 = undefined;
+ _ = std.fmt.hexToBytes(&plaintext, "202122") catch unreachable;
+ const ad = "";
+ var ciphertext: [3]u8 = undefined;
+ var tag: [16]u8 = undefined;
+
+ AsconAead128.encrypt(&ciphertext, &tag, &plaintext, ad, nonce, key);
+
+ var expected_ct: [3]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected_ct, "E8C3DE") catch unreachable;
+ var expected_tag: [16]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected_tag, "AF8E12816B8EDF39AD1571A9492B7CA2") catch unreachable;
+
+ try testing.expectEqualSlices(u8, &expected_ct, &ciphertext);
+ try testing.expectEqualSlices(u8, &expected_tag, &tag);
+
+ // Verify decryption
+ var decrypted: [3]u8 = undefined;
+ try AsconAead128.decrypt(&decrypted, &ciphertext, tag, ad, nonce, key);
+ try testing.expectEqualSlices(u8, &plaintext, &decrypted);
+ }
+}
+
+test "Ascon-Hash256 official test vectors" {
+
+ // Test vector 1: Empty message
+ {
+ const message = "";
+ var hash: [32]u8 = undefined;
+ AsconHash256.hash(message, &hash, .{});
+
+ var expected: [32]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "0B3BE5850F2F6B98CAF29F8FDEA89B64A1FA70AA249B8F839BD53BAA304D92B2") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &hash);
+ }
+
+ // Test vector 2: Single byte 0x00
+ {
+ const message = [_]u8{0x00};
+ var hash: [32]u8 = undefined;
+ AsconHash256.hash(&message, &hash, .{});
+
+ var expected: [32]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "0728621035AF3ED2BCA03BF6FDE900F9456F5330E4B5EE23E7F6A1E70291BC80") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &hash);
+ }
+
+ // Test vector 3: 0x00, 0x01
+ {
+ const message = [_]u8{ 0x00, 0x01 };
+ var hash: [32]u8 = undefined;
+ AsconHash256.hash(&message, &hash, .{});
+
+ var expected: [32]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "6115E7C9C4081C2797FC8FE1BC57A836AFA1C5381E556DD583860CA2DFB48DD2") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &hash);
+ }
+
+ // Test vector 4: 0x00, 0x01, 0x02
+ {
+ const message = [_]u8{ 0x00, 0x01, 0x02 };
+ var hash: [32]u8 = undefined;
+ AsconHash256.hash(&message, &hash, .{});
+
+ var expected: [32]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "265AB89A609F5A05DCA57E83FBBA700F9A2D2C4211BA4CC9F0A1A369E17B915C") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &hash);
+ }
+
+ // Test vector 5: 0x00..0x03
+ {
+ const message = [_]u8{ 0x00, 0x01, 0x02, 0x03 };
+ var hash: [32]u8 = undefined;
+ AsconHash256.hash(&message, &hash, .{});
+
+ var expected: [32]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "D7E4C7ED9B8A325CD08B9EF259F8877054ECD8304FE1B2D7FD847137DF6727EE") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &hash);
+ }
+}
+
+test "Ascon-XOF128 official test vectors" {
+
+ // Test vector 1: Empty message, 64-byte output
+ {
+ var xof = AsconXof128.init(.{});
+ xof.update("");
+
+ var output: [64]u8 = undefined;
+ xof.squeeze(&output);
+
+ var expected: [64]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "473D5E6164F58B39DFD84AACDB8AE42EC2D91FED33388EE0D960D9B3993295C6AD77855A5D3B13FE6AD9E6098988373AF7D0956D05A8F1665D2C67D1A3AD10FF") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &output);
+ }
+
+ // Test vector 2: Single byte 0x00, 64-byte output
+ {
+ var xof = AsconXof128.init(.{});
+ const msg = [_]u8{0x00};
+ xof.update(&msg);
+
+ var output: [64]u8 = undefined;
+ xof.squeeze(&output);
+
+ var expected: [64]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "51430E0438ECDF642B393630D977625F5F337656BA58AB1E960784AC32A16E0D446405551F5469384F8EA283CF12E64FA72C426BFEBAEA3AA1529E2C4AB23A2F") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &output);
+ }
+
+ // Test vector 3: 0x00, 0x01, 64-byte output
+ {
+ var xof = AsconXof128.init(.{});
+ const msg = [_]u8{ 0x00, 0x01 };
+ xof.update(&msg);
+
+ var output: [64]u8 = undefined;
+ xof.squeeze(&output);
+
+ var expected: [64]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "A05383077AF971D3830BD37E7B981497A773D441DB077C6494CC73125953846EB6427FBA4CD308FF90A11385D51101341BF5379249217BFDACE9CCA1148CC966") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &output);
+ }
+}
+
+test "Ascon-CXOF128 official test vectors" {
+
+ // Test vector 1: Empty message, empty customization, 64-byte output
+ {
+ var xof = AsconCxof128.init(.{});
+ xof.update("");
+
+ var output: [64]u8 = undefined;
+ xof.squeeze(&output);
+
+ var expected: [64]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "4F50159EF70BB3DAD8807E034EAEBD44C4FA2CBBC8CF1F05511AB66CDCC529905CA12083FC186AD899B270B1473DC5F7EC88D1052082DCDFE69FB75D269E7B74") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &output);
+ }
+
+ // Test vector 2: Empty message, customization = 0x10, 64-byte output
+ {
+ const custom = [_]u8{0x10};
+ var xof = AsconCxof128.init(.{ .custom = &custom });
+ xof.update("");
+
+ var output: [64]u8 = undefined;
+ xof.squeeze(&output);
+
+ var expected: [64]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "0C93A483E7D574D49FE52CCE03EE646117977D57A8AA57704AB4DAF44B501430FF6AC11A5D1FD6F2154B5C65728268270C8BB578508487B8965718ADA6272FD6") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &output);
+ }
+
+ // Test vector 3: Empty message, customization = 0x10, 0x11, 64-byte output
+ {
+ const custom = [_]u8{ 0x10, 0x11 };
+ var xof = AsconCxof128.init(.{ .custom = &custom });
+ xof.update("");
+
+ var output: [64]u8 = undefined;
+ xof.squeeze(&output);
+
+ var expected: [64]u8 = undefined;
+ _ = std.fmt.hexToBytes(&expected, "D1106C7622E79FE955BD9D79E03B918E770FE0E0CDDDE28BEB924B02C5FC936B33ACCA299C89ECA5D71886CBBFA4D54A21C55FDE2B679F5E2488063A1719DC32") catch unreachable;
+ try testing.expectEqualSlices(u8, &expected, &output);
+ }
+}
lib/std/crypto/benchmark.zig
@@ -19,6 +19,7 @@ const Crypto = struct {
};
const hashes = [_]Crypto{
+ Crypto{ .ty = crypto.hash.ascon.AsconHash256, .name = "ascon-256" },
Crypto{ .ty = crypto.hash.Md5, .name = "md5" },
Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" },
Crypto{ .ty = crypto.hash.sha2.Sha256, .name = "sha256" },
@@ -283,6 +284,7 @@ pub fn benchmarkKemKeyGen(comptime Kem: anytype, comptime kems_count: comptime_i
}
const aeads = [_]Crypto{
+ Crypto{ .ty = crypto.aead.ascon.AsconAead128, .name = "ascon-aead-128" },
Crypto{ .ty = crypto.aead.chacha_poly.ChaCha20Poly1305, .name = "chacha20Poly1305" },
Crypto{ .ty = crypto.aead.chacha_poly.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
Crypto{ .ty = crypto.aead.chacha_poly.XChaCha8Poly1305, .name = "xchacha8Poly1305" },
@@ -458,7 +460,9 @@ fn mode(comptime x: comptime_int) comptime_int {
}
pub fn main() !void {
- const stdout = std.fs.File.stdout().deprecatedWriter();
+ var stdout_buffer: [4096]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
+ const stdout = &stdout_writer.interface;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
@@ -471,6 +475,7 @@ pub fn main() !void {
while (i < args.len) : (i += 1) {
if (std.mem.eql(u8, args[i], "--mode")) {
try stdout.print("{}\n", .{builtin.mode});
+ try stdout.flush();
return;
} else if (std.mem.eql(u8, args[i], "--seed")) {
i += 1;
@@ -502,6 +507,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
try stdout.print("{s:>17}: {:10} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
+ try stdout.flush();
}
}
@@ -509,6 +515,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
try stdout.print("{s:>17}: {:10} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
+ try stdout.flush();
}
}
@@ -516,6 +523,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
try stdout.print("{s:>17}: {:10} exchanges/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -523,6 +531,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkSignature(E.ty, mode(1000));
try stdout.print("{s:>17}: {:10} signatures/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -530,6 +539,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkSignatureVerification(E.ty, mode(1000));
try stdout.print("{s:>17}: {:10} verifications/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -537,6 +547,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkBatchSignatureVerification(E.ty, mode(1000));
try stdout.print("{s:>17}: {:10} verifications/s (batch)\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -544,6 +555,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
try stdout.print("{s:>17}: {:10} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
+ try stdout.flush();
}
}
@@ -551,6 +563,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkAes(E.ty, mode(100000000));
try stdout.print("{s:>17}: {:10} ops/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -558,6 +571,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkAes8(E.ty, mode(10000000));
try stdout.print("{s:>17}: {:10} ops/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -565,6 +579,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64));
try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });
+ try stdout.flush();
}
}
@@ -572,6 +587,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkKem(E.ty, mode(1000));
try stdout.print("{s:>17}: {:10} encaps/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -579,6 +595,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkKemDecaps(E.ty, mode(25000));
try stdout.print("{s:>17}: {:10} decaps/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
@@ -586,6 +603,7 @@ pub fn main() !void {
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
const throughput = try benchmarkKemKeyGen(E.ty, mode(25000));
try stdout.print("{s:>17}: {:10} keygen/s\n", .{ E.name, throughput });
+ try stdout.flush();
}
}
}
lib/std/crypto.zig
@@ -36,6 +36,10 @@ pub const aead = struct {
pub const Aes256Ocb = @import("crypto/aes_ocb.zig").Aes256Ocb;
};
+ pub const ascon = struct {
+ pub const AsconAead128 = @import("crypto/ascon.zig").AsconAead128;
+ };
+
pub const chacha_poly = struct {
pub const ChaCha20Poly1305 = @import("crypto/chacha20.zig").ChaCha20Poly1305;
pub const ChaCha12Poly1305 = @import("crypto/chacha20.zig").ChaCha12Poly1305;
@@ -115,6 +119,12 @@ pub const ecc = struct {
/// Hash functions.
pub const hash = struct {
+ pub const ascon = struct {
+ const variants = @import("crypto/ascon.zig");
+ pub const AsconHash256 = variants.AsconHash256;
+ pub const AsconXof128 = variants.AsconXof128;
+ pub const AsconCxof128 = variants.AsconCxof128;
+ };
pub const blake2 = @import("crypto/blake2.zig");
pub const Blake3 = @import("crypto/blake3.zig").Blake3;
pub const Md5 = @import("crypto/md5.zig").Md5;
@@ -243,6 +253,8 @@ pub const SideChannelsMitigations = enum {
pub const default_side_channels_mitigations = .medium;
test {
+ _ = aead.ascon.AsconAead128;
+
_ = aead.aegis.Aegis128L;
_ = aead.aegis.Aegis256;
@@ -281,6 +293,7 @@ test {
_ = ecc.Ristretto255;
_ = ecc.Secp256k1;
+ _ = hash.ascon;
_ = hash.blake2;
_ = hash.Blake3;
_ = hash.Md5;