Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

State128X

aegis.State128X
fn State128X(comptime degree: u7) type

File

lib/std/crypto/aegis.zig:66

Code

fn State128X(comptime degree: u7) type {
    return struct {
        const AesBlockVec = crypto.core.aes.BlockVec(degree);
        const State = @This();

        blocks: [8]AesBlockVec,

        const aes_block_length = AesBlockVec.block_length;
        const rate = aes_block_length * 2;
        const alignment = AesBlockVec.native_word_size;

        fn init(key: [16]u8, nonce: [16]u8) State {
            const c1 = AesBlockVec.fromBytes(&repeat16u8(degree, .{ 0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1, 0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd }));
            const c2 = AesBlockVec.fromBytes(&repeat16u8(degree, .{ 0x0, 0x1, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62 }));
            const key_block = AesBlockVec.fromBytes(&repeat16u8(degree, key));
            const nonce_block = AesBlockVec.fromBytes(&repeat16u8(degree, nonce));
            const blocks = [8]AesBlockVec{
                key_block.xorBlocks(nonce_block),
                c1,
                c2,
                c1,
                key_block.xorBlocks(nonce_block),
                key_block.xorBlocks(c2),
                key_block.xorBlocks(c1),
                key_block.xorBlocks(c2),
            };
            var state = State{ .blocks = blocks };
            if (degree > 1) {
                const context_block = ctx: {
                    var contexts_bytes: [aes_block_length]u8 = @splat(0);
                    for (0..degree) |i| {
                        contexts_bytes[i * 16] = @intCast(i);
                        contexts_bytes[i * 16 + 1] = @intCast(degree - 1);
                    }
                    break :ctx AesBlockVec.fromBytes(&contexts_bytes);
                };
                for (0..10) |_| {
                    state.blocks[3] = state.blocks[3].xorBlocks(context_block);
                    state.blocks[7] = state.blocks[7].xorBlocks(context_block);
                    state.update(nonce_block, key_block);
                }
            } else {
                for (0..10) |_| {
                    state.update(nonce_block, key_block);
                }
            }
            return state;
        }

        fn update(state: *State, d1: AesBlockVec, d2: AesBlockVec) void {
            const blocks = &state.blocks;
            const tmp = blocks[7];
            comptime var i: usize = 7;
            inline while (i > 0) : (i -= 1) {
                blocks[i] = blocks[i - 1].encrypt(blocks[i]);
            }
            blocks[0] = tmp.encrypt(blocks[0]);
            blocks[0] = blocks[0].xorBlocks(d1);
            blocks[4] = blocks[4].xorBlocks(d2);
        }

        fn absorb(state: *State, src: *const [rate]u8) void {
            const msg0 = AesBlockVec.fromBytes(src[0..aes_block_length]);
            const msg1 = AesBlockVec.fromBytes(src[aes_block_length..rate]);
            state.update(msg0, msg1);
        }

        fn enc(state: *State, dst: *[rate]u8, src: *const [rate]u8) void {
            const blocks = &state.blocks;
            const msg0 = AesBlockVec.fromBytes(src[0..aes_block_length]);
            const msg1 = AesBlockVec.fromBytes(src[aes_block_length..rate]);
            var tmp0 = msg0.xorBlocks(blocks[6]).xorBlocks(blocks[1]);
            var tmp1 = msg1.xorBlocks(blocks[2]).xorBlocks(blocks[5]);
            tmp0 = tmp0.xorBlocks(blocks[2].andBlocks(blocks[3]));
            tmp1 = tmp1.xorBlocks(blocks[6].andBlocks(blocks[7]));
            dst[0..aes_block_length].* = tmp0.toBytes();
            dst[aes_block_length..rate].* = tmp1.toBytes();
            state.update(msg0, msg1);
        }

        fn dec(state: *State, dst: *[rate]u8, src: *const [rate]u8) void {
            const blocks = &state.blocks;
            var msg0 = AesBlockVec.fromBytes(src[0..aes_block_length]).xorBlocks(blocks[6]).xorBlocks(blocks[1]);
            var msg1 = AesBlockVec.fromBytes(src[aes_block_length..rate]).xorBlocks(blocks[2]).xorBlocks(blocks[5]);
            msg0 = msg0.xorBlocks(blocks[2].andBlocks(blocks[3]));
            msg1 = msg1.xorBlocks(blocks[6].andBlocks(blocks[7]));
            dst[0..aes_block_length].* = msg0.toBytes();
            dst[aes_block_length..rate].* = msg1.toBytes();
            state.update(msg0, msg1);
        }

        fn decLast(state: *State, dst: []u8, src: []const u8) void {
            const blocks = &state.blocks;
            const z0 = blocks[6].xorBlocks(blocks[1]).xorBlocks(blocks[2].andBlocks(blocks[3]));
            const z1 = blocks[2].xorBlocks(blocks[5]).xorBlocks(blocks[6].andBlocks(blocks[7]));
            var pad: [rate]u8 = @splat(0);
            pad[0..aes_block_length].* = z0.toBytes();
            pad[aes_block_length..].* = z1.toBytes();
            for (pad[0..src.len], src) |*p, x| p.* ^= x;
            @memcpy(dst, pad[0..src.len]);
            @memset(pad[src.len..], 0);
            const msg0 = AesBlockVec.fromBytes(pad[0..aes_block_length]);
            const msg1 = AesBlockVec.fromBytes(pad[aes_block_length..rate]);
            state.update(msg0, msg1);
        }

        fn finalize(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
            const blocks = &state.blocks;
            var sizes: [aes_block_length]u8 = undefined;
            mem.writeInt(u64, sizes[0..8], @as(u64, adlen) * 8, .little);
            mem.writeInt(u64, sizes[8..16], @as(u64, mlen) * 8, .little);
            for (1..degree) |i| {
                @memcpy(sizes[i * 16 ..][0..16], sizes[0..16]);
            }
            const tmp = AesBlockVec.fromBytes(&sizes).xorBlocks(blocks[2]);
            for (0..7) |_| {
                state.update(tmp, tmp);
            }
            switch (tag_bits) {
                128 => {
                    var tag_multi = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
                    var tag = tag_multi[0..16].*;
                    @memcpy(tag[0..], tag_multi[0..16]);
                    for (1..degree) |d| {
                        for (0..16) |i| {
                            tag[i] ^= tag_multi[d * 16 + i];
                        }
                    }
                    return tag;
                },
                256 => {
                    const tag_multi_1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes();
                    const tag_multi_2 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes();
                    var tag = tag_multi_1[0..16].* ++ tag_multi_2[0..16].*;
                    for (1..degree) |d| {
                        for (0..16) |i| {
                            tag[i] ^= tag_multi_1[d * 16 + i];
                            tag[i + 16] ^= tag_multi_2[d * 16 + i];
                        }
                    }
                    return tag;
                },
                else => unreachable,
            }
        }

        fn finalizeMac(state: *State, comptime tag_bits: u9, datalen: usize) [tag_bits / 8]u8 {
            const blocks = &state.blocks;
            var sizes: [aes_block_length]u8 = undefined;
            mem.writeInt(u64, sizes[0..8], @as(u64, datalen) * 8, .little);
            mem.writeInt(u64, sizes[8..16], tag_bits, .little);
            for (1..degree) |i| {
                @memcpy(sizes[i * 16 ..][0..16], sizes[0..16]);
            }
            var t = blocks[2].xorBlocks(AesBlockVec.fromBytes(&sizes));
            for (0..7) |_| {
                state.update(t, t);
            }
            if (degree > 1) {
                var v: [rate]u8 = @splat(0);
                switch (tag_bits) {
                    128 => {
                        const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
                        for (0..degree / 2) |d| {
                            v[0..16].* = tags[d * 32 ..][0..16].*;
                            v[rate / 2 ..][0..16].* = tags[d * 32 ..][16..32].*;
                            state.absorb(&v);
                        }
                    },
                    256 => {
                        const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes();
                        const tags_1 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes();
                        for (1..degree) |d| {
                            v[0..16].* = tags_0[d * 16 ..][0..16].*;
                            v[rate / 2 ..][0..16].* = tags_1[d * 16 ..][0..16].*;
                            state.absorb(&v);
                        }
                    },
                    else => unreachable,
                }
                mem.writeInt(u64, sizes[0..8], degree, .little);
                mem.writeInt(u64, sizes[8..16], tag_bits, .little);
                t = blocks[2].xorBlocks(AesBlockVec.fromBytes(&sizes));
                for (0..7) |_| {
                    state.update(t, t);
                }
            }
            switch (tag_bits) {
                128 => {
                    const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
                    return tags[0..16].*;
                },
                256 => {
                    const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes();
                    const tags_1 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes();
                    return tags_0[0..16].* ++ tags_1[0..16].*;
                },
                else => unreachable,
            }
        }
    };
}