Type Function Ascon [src]

Alias for std.crypto.ascon.State

An Ascon state. The state is represented as 5 64-bit words. The original NIST submission (v1.2) serializes these words as big-endian, but NIST SP 800-232 switched to a little-endian representation. Software implementations are free to use native endianness with no security degradation.

Prototype

pub fn State(comptime endian: std.builtin.Endian) type

Parameters

endian: std.builtin.Endian

Source

pub fn State(comptime endian: std.builtin.Endian) type { return struct { const Self = @This(); /// Number of bytes in the state. pub const block_bytes = 40; const Block = [5]u64; st: Block, /// Initialize the state from a slice of bytes. pub fn init(initial_state: [block_bytes]u8) Self { var state = Self{ .st = undefined }; @memcpy(state.asBytes(), &initial_state); state.endianSwap(); return state; } /// Initialize the state from u64 words in native endianness. pub fn initFromWords(initial_state: [5]u64) Self { return .{ .st = initial_state }; } /// Initialize the state for Ascon XOF pub fn initXof() Self { return Self{ .st = Block{ 0xb57e273b814cd416, 0x2b51042562ae2420, 0x66a3a7768ddf2218, 0x5aad0a7a8153650c, 0x4f3e0e32539493b6, } }; } /// Initialize the state for Ascon XOFa pub fn initXofA() Self { return Self{ .st = Block{ 0x44906568b77b9832, 0xcd8d6cae53455532, 0xf7b5212756422129, 0x246885e1de0d225b, 0xa8cb5ce33449973f, } }; } /// A representation of the state as bytes. The byte order is architecture-dependent. 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. pub fn endianSwap(self: *Self) void { for (&self.st) |*w| { w.* = mem.toNative(u64, w.*, endian); } } /// Set bytes starting at the beginning of the state. 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; @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. pub fn addByte(self: *Self, byte: u8, offset: usize) void { const z = switch (endian) { .big => 64 - 8 - 8 * @as(u6, @truncate(offset % 8)), .little => 8 * @as(u6, @truncate(offset % 8)), }; self.st[offset / 8] ^= @as(u64, byte) << z; } /// XOR bytes into the beginning of the state. 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; @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. 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; 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. pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void { debug.assert(out.len == in.len); var i: usize = 0; while (i + 8 <= in.len) : (i += 8) { const x = mem.readInt(u64, in[i..][0..8], native_endian) ^ mem.nativeTo(u64, self.st[i / 8], endian); mem.writeInt(u64, out[i..][0..8], x, native_endian); } if (i < in.len) { var padded = [_]u8{0} ** 8; @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); @memcpy(out[i..], padded[0 .. in.len - i]); } } /// Set the words storing the bytes of a given range to zero. 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. pub fn secureZero(self: *Self) void { std.crypto.secureZero(u64, &self.st); } /// Apply a reduced-round permutation to the state. pub inline 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| { state.round(rk); } } /// Apply a full-round permutation to the state. pub inline 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). pub inline 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 var mask: [capacity / 8]u64 = undefined; inline for (&mask, state.st[state.st.len - mask.len ..]) |*m, x| m.* = x; state.permuteR(rounds); inline for (mask, state.st[state.st.len - mask.len ..]) |m, *x| x.* ^= m; } // Core Ascon permutation. inline fn round(state: *Self, rk: u64) void { const x = &state.st; x[2] ^= rk; x[0] ^= x[4]; x[4] ^= x[3]; x[2] ^= x[1]; var t: Block = .{ x[0] ^ (~x[1] & x[2]), x[1] ^ (~x[2] & x[3]), x[2] ^ (~x[3] & x[4]), x[3] ^ (~x[4] & x[0]), x[4] ^ (~x[0] & x[1]), }; t[1] ^= t[0]; t[3] ^= t[2]; t[0] ^= t[4]; x[2] = t[2] ^ rotr(u64, t[2], 6 - 1); x[3] = t[3] ^ rotr(u64, t[3], 17 - 10); x[4] = t[4] ^ rotr(u64, t[4], 41 - 7); x[0] = t[0] ^ rotr(u64, t[0], 28 - 19); x[1] = t[1] ^ rotr(u64, t[1], 61 - 39); x[2] = t[2] ^ rotr(u64, x[2], 1); x[3] = t[3] ^ rotr(u64, x[3], 10); x[4] = t[4] ^ rotr(u64, x[4], 7); x[0] = t[0] ^ rotr(u64, x[0], 19); x[1] = t[1] ^ rotr(u64, x[1], 39); x[2] = ~x[2]; } }; }