structstd.crypto.core[src]

Core functions, that should rarely be used directly by applications.

Types

Type FunctionState[src]

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.

Parameters

Fields

st: Block

Values

Constantblock_bytes[src]

Number of bytes in the state.

Source Code

Source code
pub const block_bytes = 40

Functions

Functioninit[src]

pub fn init(initial_state: [block_bytes]u8) Self

Initialize the state from a slice of bytes.

Parameters

initial_state: [block_bytes]u8

Source Code

Source code
pub fn init(initial_state: [block_bytes]u8) Self {
    var state = Self{ .st = undefined };
    @memcpy(state.asBytes(), &initial_state);
    state.endianSwap();
    return state;
}

FunctioninitFromWords[src]

pub fn initFromWords(initial_state: [5]u64) Self

Initialize the state from u64 words in native endianness.

Parameters

initial_state: [5]u64

Source Code

Source code
pub fn initFromWords(initial_state: [5]u64) Self {
    return .{ .st = initial_state };
}

FunctioninitXof[src]

pub fn initXof() Self

Initialize the state for Ascon XOF

Source Code

Source code
pub fn initXof() Self {
    return Self{ .st = Block{
        0xb57e273b814cd416,
        0x2b51042562ae2420,
        0x66a3a7768ddf2218,
        0x5aad0a7a8153650c,
        0x4f3e0e32539493b6,
    } };
}

FunctioninitXofA[src]

pub fn initXofA() Self

Initialize the state for Ascon XOFa

Source Code

Source code
pub fn initXofA() Self {
    return Self{ .st = Block{
        0x44906568b77b9832,
        0xcd8d6cae53455532,
        0xf7b5212756422129,
        0x246885e1de0d225b,
        0xa8cb5ce33449973f,
    } };
}

FunctionasBytes[src]

pub fn asBytes(self: *Self) *[block_bytes]u8

A representation of the state as bytes. The byte order is architecture-dependent.

Parameters

self: *Self

Source Code

Source code
pub fn asBytes(self: *Self) *[block_bytes]u8 {
    return mem.asBytes(&self.st);
}

FunctionendianSwap[src]

pub fn endianSwap(self: *Self) void

Byte-swap the entire state if the architecture doesn't match the required endianness.

Parameters

self: *Self

Source Code

Source code
pub fn endianSwap(self: *Self) void {
    for (&self.st) |*w| {
        w.* = mem.toNative(u64, w.*, endian);
    }
}

FunctionsetBytes[src]

pub fn setBytes(self: *Self, bytes: []const u8) void

Set bytes starting at the beginning of the state.

Parameters

self: *Self
bytes: []const u8

Source Code

Source code
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);
    }
}

FunctionaddByte[src]

pub fn addByte(self: *Self, byte: u8, offset: usize) void

XOR a byte into the state at a given offset.

Parameters

self: *Self
byte: u8
offset: usize

Source Code

Source code
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;
}

FunctionaddBytes[src]

pub fn addBytes(self: *Self, bytes: []const u8) void

XOR bytes into the beginning of the state.

Parameters

self: *Self
bytes: []const u8

Source Code

Source code
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);
    }
}

FunctionextractBytes[src]

pub fn extractBytes(self: *Self, out: []u8) void

Extract the first bytes of the state.

Parameters

self: *Self
out: []u8

Source Code

Source code
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]);
    }
}

FunctionxorBytes[src]

pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void

XOR the first bytes of the state into a slice of bytes.

Parameters

self: *Self
out: []u8
in: []const u8

Source Code

Source code
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]);
    }
}

Functionclear[src]

pub fn clear(self: *Self, from: usize, to: usize) void

Set the words storing the bytes of a given range to zero.

Parameters

self: *Self
from: usize
to: usize

Source Code

Source code
pub fn clear(self: *Self, from: usize, to: usize) void {
    @memset(self.st[from / 8 .. (to + 7) / 8], 0);
}

FunctionsecureZero[src]

pub fn secureZero(self: *Self) void

Clear the entire state, disabling compiler optimizations.

Parameters

self: *Self

Source Code

Source code
pub fn secureZero(self: *Self) void {
    std.crypto.secureZero(u64, &self.st);
}

FunctionpermuteR[src]

pub inline fn permuteR(state: *Self, comptime rounds: u4) void

Apply a reduced-round permutation to the state.

Parameters

state: *Self
rounds: u4

Source Code

Source code
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);
    }
}

Functionpermute[src]

pub inline fn permute(state: *Self) void

Apply a full-round permutation to the state.

Parameters

state: *Self

Source Code

Source code
pub inline fn permute(state: *Self) void {
    state.permuteR(12);
}

FunctionpermuteRatchet[src]

pub inline fn permuteRatchet(state: *Self, comptime rounds: u4, comptime rate: u6) void

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

state: *Self
rounds: u4
rate: u6

Source Code

Source code
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;
}

Source Code

Source code
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];
        }
    };
}

Source Code

Source code
pub const core = struct {
    pub const aes = @import("crypto/aes.zig");
    pub const keccak = @import("crypto/keccak_p.zig");

    pub const Ascon = @import("crypto/ascon.zig").State;

    /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations.
    ///
    /// These modes are designed to be building blocks for higher-level constructions, and should generally not be used directly by applications, as they may not provide the expected properties and security guarantees.
    ///
    /// Most applications may want to use AEADs instead.
    pub const modes = @import("crypto/modes.zig");
}