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.

SalsaVecImpl

salsa20.SalsaVecImpl
fn SalsaVecImpl(comptime rounds: comptime_int) type

File

lib/std/crypto/salsa20.zig:22

Code

fn SalsaVecImpl(comptime rounds: comptime_int) type {
    return struct {
        const Lane = @Vector(4, u32);
        const Half = @Vector(2, u32);
        const BlockVec = [4]Lane;

        fn initContext(key: [8]u32, d: [4]u32) BlockVec {
            const c = "expand 32-byte k";
            const constant_le = comptime [4]u32{
                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{
                Lane{ key[0], key[1], key[2], key[3] },
                Lane{ key[4], key[5], key[6], key[7] },
                Lane{ constant_le[0], constant_le[1], constant_le[2], constant_le[3] },
                Lane{ d[0], d[1], d[2], d[3] },
            };
        }

        fn salsaCore(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
            const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] };
            const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] };
            const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] };
            const k0k1 = Half{ input[0][0], input[0][1] };
            const k2k3 = Half{ input[0][2], input[0][3] };
            const k4k5 = Half{ input[1][0], input[1][1] };
            const k6k7 = Half{ input[1][2], input[1][3] };
            const n0k0 = Half{ n3n0[1], k0k1[0] };
            const k0n0 = Half{ n0k0[1], n0k0[0] };
            const k4k5k0n0 = Lane{ k4k5[0], k4k5[1], k0n0[0], k0n0[1] };
            const k1k6 = Half{ k0k1[1], k6k7[0] };
            const k6k1 = Half{ k1k6[1], k1k6[0] };
            const n1n2k6k1 = Lane{ n1n2[0], n1n2[1], k6k1[0], k6k1[1] };
            const k7n3 = Half{ k6k7[1], n3n0[0] };
            const n3k7 = Half{ k7n3[1], k7n3[0] };
            const k2k3n3k7 = Lane{ k2k3[0], k2k3[1], n3k7[0], n3k7[1] };

            var diag0 = input[2];
            var diag1 = @shuffle(u32, k4k5k0n0, undefined, [_]i32{ 1, 2, 3, 0 });
            var diag2 = @shuffle(u32, n1n2k6k1, undefined, [_]i32{ 1, 2, 3, 0 });
            var diag3 = @shuffle(u32, k2k3n3k7, undefined, [_]i32{ 1, 2, 3, 0 });

            const start0 = diag0;
            const start1 = diag1;
            const start2 = diag2;
            const start3 = diag3;

            var i: usize = 0;
            while (i < rounds) : (i += 2) {
                diag3 ^= math.rotl(Lane, diag1 +% diag0, 7);
                diag2 ^= math.rotl(Lane, diag0 +% diag3, 9);
                diag1 ^= math.rotl(Lane, diag3 +% diag2, 13);
                diag0 ^= math.rotl(Lane, diag2 +% diag1, 18);

                diag3 = @shuffle(u32, diag3, undefined, [_]i32{ 3, 0, 1, 2 });
                diag2 = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 });
                diag1 = @shuffle(u32, diag1, undefined, [_]i32{ 1, 2, 3, 0 });

                diag1 ^= math.rotl(Lane, diag3 +% diag0, 7);
                diag2 ^= math.rotl(Lane, diag0 +% diag1, 9);
                diag3 ^= math.rotl(Lane, diag1 +% diag2, 13);
                diag0 ^= math.rotl(Lane, diag2 +% diag3, 18);

                diag1 = @shuffle(u32, diag1, undefined, [_]i32{ 3, 0, 1, 2 });
                diag2 = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 });
                diag3 = @shuffle(u32, diag3, undefined, [_]i32{ 1, 2, 3, 0 });
            }

            if (feedback) {
                diag0 +%= start0;
                diag1 +%= start1;
                diag2 +%= start2;
                diag3 +%= start3;
            }

            const x0x1x10x11 = Lane{ diag0[0], diag1[1], diag0[2], diag1[3] };
            const x12x13x6x7 = Lane{ diag1[0], diag2[1], diag1[2], diag2[3] };
            const x8x9x2x3 = Lane{ diag2[0], diag3[1], diag2[2], diag3[3] };
            const x4x5x14x15 = Lane{ diag3[0], diag0[1], diag3[2], diag0[3] };

            x[0] = Lane{ x0x1x10x11[0], x0x1x10x11[1], x8x9x2x3[2], x8x9x2x3[3] };
            x[1] = Lane{ x4x5x14x15[0], x4x5x14x15[1], x12x13x6x7[2], x12x13x6x7[3] };
            x[2] = Lane{ x8x9x2x3[0], x8x9x2x3[1], x0x1x10x11[2], x0x1x10x11[3] };
            x[3] = Lane{ x12x13x6x7[0], x12x13x6x7[1], x4x5x14x15[2], x4x5x14x15[3] };
        }

        fn hashToBytes(out: *[64]u8, x: BlockVec) void {
            var i: usize = 0;
            while (i < 4) : (i += 1) {
                mem.writeInt(u32, out[16 * i + 0 ..][0..4], x[i][0], .little);
                mem.writeInt(u32, out[16 * i + 4 ..][0..4], x[i][1], .little);
                mem.writeInt(u32, out[16 * i + 8 ..][0..4], x[i][2], .little);
                mem.writeInt(u32, out[16 * i + 12 ..][0..4], x[i][3], .little);
            }
        }

        fn salsaXor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void {
            var ctx = initContext(key, d);
            var x: BlockVec = undefined;
            var buf: [64]u8 = undefined;
            var i: usize = 0;
            while (i + 64 <= in.len) : (i += 64) {
                salsaCore(x[0..], ctx, true);
                hashToBytes(buf[0..], x);
                var xout = out[i..];
                const xin = in[i..];
                var j: usize = 0;
                while (j < 64) : (j += 1) {
                    xout[j] = xin[j];
                }
                j = 0;
                while (j < 64) : (j += 1) {
                    xout[j] ^= buf[j];
                }
                ctx[3][2] +%= 1;
                if (ctx[3][2] == 0) {
                    ctx[3][3] += 1;
                }
            }
            if (i < in.len) {
                salsaCore(x[0..], ctx, true);
                hashToBytes(buf[0..], x);

                var xout = out[i..];
                const xin = in[i..];
                var j: usize = 0;
                while (j < in.len % 64) : (j += 1) {
                    xout[j] = xin[j] ^ buf[j];
                }
            }
        }

        fn hsalsa(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;
            salsaCore(x[0..], ctx, false);
            var out: [32]u8 = undefined;
            mem.writeInt(u32, out[0..4], x[0][0], .little);
            mem.writeInt(u32, out[4..8], x[1][1], .little);
            mem.writeInt(u32, out[8..12], x[2][2], .little);
            mem.writeInt(u32, out[12..16], x[3][3], .little);
            mem.writeInt(u32, out[16..20], x[1][2], .little);
            mem.writeInt(u32, out[20..24], x[1][3], .little);
            mem.writeInt(u32, out[24..28], x[2][0], .little);
            mem.writeInt(u32, out[28..32], x[2][1], .little);
            return out;
        }
    };
}