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.

ChaChaVecImpl

chacha20.ChaChaVecImpl
fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type

File

lib/std/crypto/chacha20.zig:80

Code

fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type {
    return struct {
        const Lane = @Vector(4 * degree, u32);
        const BlockVec = [4]Lane;

        fn initContext(key: [8]u32, d: [4]u32) BlockVec {
            const c = "expand 32-byte k";
            switch (degree) {
                1 => {
                    const constant_le = Lane{
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                    };
                    return BlockVec{
                        constant_le,
                        Lane{ key[0], key[1], key[2], key[3] },
                        Lane{ key[4], key[5], key[6], key[7] },
                        Lane{ d[0], d[1], d[2], d[3] },
                    };
                },
                2 => {
                    const constant_le = Lane{
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                    };
                    const n1 = @addWithOverflow(d[0], 1);
                    return BlockVec{
                        constant_le,
                        Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] },
                        Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] },
                        Lane{ d[0], d[1], d[2], d[3], n1[0], d[1] +% n1[1], d[2], d[3] },
                    };
                },
                4 => {
                    const n1 = @addWithOverflow(d[0], 1);
                    const n2 = @addWithOverflow(d[0], 2);
                    const n3 = @addWithOverflow(d[0], 3);
                    const constant_le = Lane{
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                        mem.readInt(u32, c[0..4], .little),
                        mem.readInt(u32, c[4..8], .little),
                        mem.readInt(u32, c[8..12], .little),
                        mem.readInt(u32, c[12..16], .little),
                    };
                    return BlockVec{
                        constant_le,
                        Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] },
                        Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] },
                        Lane{ d[0], d[1], d[2], d[3], n1[0], d[1] +% n1[1], d[2], d[3], n2[0], d[1] +% n2[1], d[2], d[3], n3[0], d[1] +% n3[1], d[2], d[3] },
                    };
                },
                else => @compileError("invalid degree"),
            }
        }

        fn chacha20Core(x: *BlockVec, input: BlockVec) void {
            x.* = input;

            const m0 = switch (degree) {
                1 => [_]i32{ 3, 0, 1, 2 },
                2 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 },
                4 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 } ++ [_]i32{ 11, 8, 9, 10 } ++ [_]i32{ 15, 12, 13, 14 },
                else => @compileError("invalid degree"),
            };
            const m1 = switch (degree) {
                1 => [_]i32{ 2, 3, 0, 1 },
                2 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 },
                4 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 } ++ [_]i32{ 10, 11, 8, 9 } ++ [_]i32{ 14, 15, 12, 13 },
                else => @compileError("invalid degree"),
            };
            const m2 = switch (degree) {
                1 => [_]i32{ 1, 2, 3, 0 },
                2 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 },
                4 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 } ++ [_]i32{ 9, 10, 11, 8 } ++ [_]i32{ 13, 14, 15, 12 },
                else => @compileError("invalid degree"),
            };

            var r: usize = 0;
            while (r < rounds_nb) : (r += 2) {
                x[0] +%= x[1];
                x[3] ^= x[0];
                x[3] = math.rotl(Lane, x[3], 16);

                x[2] +%= x[3];
                x[1] ^= x[2];
                x[1] = math.rotl(Lane, x[1], 12);

                x[0] +%= x[1];
                x[3] ^= x[0];
                x[0] = @shuffle(u32, x[0], undefined, m0);
                x[3] = math.rotl(Lane, x[3], 8);

                x[2] +%= x[3];
                x[3] = @shuffle(u32, x[3], undefined, m1);
                x[1] ^= x[2];
                x[2] = @shuffle(u32, x[2], undefined, m2);
                x[1] = math.rotl(Lane, x[1], 7);

                x[0] +%= x[1];
                x[3] ^= x[0];
                x[3] = math.rotl(Lane, x[3], 16);

                x[2] +%= x[3];
                x[1] ^= x[2];
                x[1] = math.rotl(Lane, x[1], 12);

                x[0] +%= x[1];
                x[3] ^= x[0];
                x[0] = @shuffle(u32, x[0], undefined, m2);
                x[3] = math.rotl(Lane, x[3], 8);

                x[2] +%= x[3];
                x[3] = @shuffle(u32, x[3], undefined, m1);
                x[1] ^= x[2];
                x[2] = @shuffle(u32, x[2], undefined, m0);
                x[1] = math.rotl(Lane, x[1], 7);
            }
        }

        fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: *const BlockVec) void {
            inline for (0..dm) |d| {
                for (0..4) |i| {
                    mem.writeInt(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d], .little);
                    mem.writeInt(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d], .little);
                    mem.writeInt(u32, out[64 * d + 16 * i + 8 ..][0..4], x[i][2 + 4 * d], .little);
                    mem.writeInt(u32, out[64 * d + 16 * i + 12 ..][0..4], x[i][3 + 4 * d], .little);
                }
            }
        }

        fn contextFeedback(x: *BlockVec, ctx: BlockVec) void {
            x[0] +%= ctx[0];
            x[1] +%= ctx[1];
            x[2] +%= ctx[2];
            x[3] +%= ctx[3];
        }

        fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void {
            var ctx = initContext(key, nonce_and_counter);
            var x: BlockVec = undefined;
            var buf: [64 * degree]u8 = undefined;
            var i: usize = 0;
            inline for ([_]comptime_int{ 4, 2, 1 }) |d| {
                while (degree >= d and i + 64 * d <= in.len) : (i += 64 * d) {
                    chacha20Core(x[0..], ctx);
                    contextFeedback(&x, ctx);
                    hashToBytes(d, buf[0 .. 64 * d], &x);

                    var xout = out[i..];
                    const xin = in[i..];
                    for (0..64 * d) |j| {
                        xout[j] = xin[j];
                    }
                    for (0..64 * d) |j| {
                        xout[j] ^= buf[j];
                    }
                    inline for (0..d) |d_| {
                        if (count64) {
                            const next = @addWithOverflow(ctx[3][4 * d_], d);
                            ctx[3][4 * d_] = next[0];
                            ctx[3][4 * d_ + 1] +%= next[1];
                        } else {
                            ctx[3][4 * d_] +%= d;
                        }
                    }
                }
            }
            if (i < in.len) {
                chacha20Core(x[0..], ctx);
                contextFeedback(&x, ctx);
                hashToBytes(1, buf[0..64], &x);

                var xout = out[i..];
                const xin = in[i..];
                for (0..in.len % 64) |j| {
                    xout[j] = xin[j] ^ buf[j];
                }
            }
        }

        fn chacha20Stream(out: []u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void {
            var ctx = initContext(key, nonce_and_counter);
            var x: BlockVec = undefined;
            var i: usize = 0;
            inline for ([_]comptime_int{ 4, 2, 1 }) |d| {
                while (degree >= d and i + 64 * d <= out.len) : (i += 64 * d) {
                    chacha20Core(x[0..], ctx);
                    contextFeedback(&x, ctx);
                    hashToBytes(d, out[i..][0 .. 64 * d], &x);
                    inline for (0..d) |d_| {
                        if (count64) {
                            const next = @addWithOverflow(ctx[3][4 * d_], d);
                            ctx[3][4 * d_] = next[0];
                            ctx[3][4 * d_ + 1] +%= next[1];
                        } else {
                            ctx[3][4 * d_] +%= d;
                        }
                    }
                }
            }
            if (i < out.len) {
                chacha20Core(x[0..], ctx);
                contextFeedback(&x, ctx);

                var buf: [64]u8 = undefined;
                hashToBytes(1, buf[0..], &x);
                @memcpy(out[i..], buf[0 .. out.len - i]);
            }
        }

        fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 {
            var c: [4]u32 = undefined;
            for (c, 0..) |_, i| {
                c[i] = mem.readInt(u32, input[4 * i ..][0..4], .little);
            }
            const ctx = initContext(keyToWords(key), c);
            var x: BlockVec = undefined;
            chacha20Core(x[0..], ctx);
            var out: [32]u8 = undefined;
            mem.writeInt(u32, out[0..4], x[0][0], .little);
            mem.writeInt(u32, out[4..8], x[0][1], .little);
            mem.writeInt(u32, out[8..12], x[0][2], .little);
            mem.writeInt(u32, out[12..16], x[0][3], .little);
            mem.writeInt(u32, out[16..20], x[3][0], .little);
            mem.writeInt(u32, out[20..24], x[3][1], .little);
            mem.writeInt(u32, out[24..28], x[3][2], .little);
            mem.writeInt(u32, out[28..32], x[3][3], .little);
            return out;
        }
    };
}