Ascon-XOF128 as specified in NIST SP 800-232 Section 5
pub const AsconXof128 = struct
pub const AsconXof128 = struct {
pub const block_length = 8;
st: AsconState,
squeezed: bool,
buf: [block_length]u8,
buf_len: usize,
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, .buf = @splat(0), .buf_len = 0 };
}
/// 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;
if (self.buf_len > 0) {
const to_fill = @min(block_length - self.buf_len, b.len);
@memcpy(self.buf[self.buf_len..][0..to_fill], b[0..to_fill]);
self.buf_len += to_fill;
i += to_fill;
if (self.buf_len == block_length) {
self.st.addBytes(&self.buf);
self.st.permuteR(12);
self.buf_len = 0;
}
}
while (i + block_length <= b.len) : (i += block_length) {
self.st.addBytes(b[i..][0..block_length]);
self.st.permuteR(12);
}
if (i < b.len) {
self.buf_len = b.len - i;
@memcpy(self.buf[0..self.buf_len], b[i..]);
}
}
/// 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) {
var padded: [block_length]u8 = @splat(0);
@memcpy(padded[0..self.buf_len], self.buf[0..self.buf_len]);
padded[self.buf_len] = 0x01;
self.st.addBytes(&padded);
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);
}
}
}
}