Ascon-CXOF128 as specified in NIST SP 800-232 Section 5
pub const AsconCxof128 = struct
pub const AsconCxof128 = struct {
pub const block_length = 8;
pub const max_custom_length = 256; // 2048 bits
st: AsconState,
squeezed: bool,
buf: [block_length]u8,
buf_len: usize,
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, .buf = @splat(0), .buf_len = 0 };
// 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); // 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 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) {
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);
}
}
}
}