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.

crypt_format

scrypt.crypt_format
const crypt_format = struct

File

lib/std/crypto/scrypt.zig:212

Code

const crypt_format = struct {
    /// String prefix for scrypt
    pub const prefix = "$7$";

    /// Standard type for a set of scrypt parameters, with the salt and hash.
    pub fn HashResult(comptime crypt_max_hash_len: usize) type {
        return struct {
            ln: u6,
            r: u30,
            p: u30,
            salt: []const u8,
            hash: BinValue(crypt_max_hash_len),
        };
    }

    const Codec = CustomB64Codec("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".*);

    /// A wrapped binary value whose maximum size is `max_len`.
    ///
    /// This type must be used whenever a binary value is encoded in a PHC-formatted string.
    /// This includes `salt`, `hash`, and any other binary parameters such as keys.
    ///
    /// Once initialized, the actual value can be read with the `constSlice()` function.
    pub fn BinValue(comptime max_len: usize) type {
        return struct {
            const Self = @This();
            const capacity = max_len;
            const max_encoded_length = Codec.encodedLen(max_len);

            buf: [max_len]u8 = undefined,
            len: usize = 0,

            /// Wrap an existing byte slice
            pub fn fromSlice(slice: []const u8) EncodingError!Self {
                if (slice.len > capacity) return EncodingError.NoSpaceLeft;
                var bin_value: Self = undefined;
                @memcpy(bin_value.buf[0..slice.len], slice);
                bin_value.len = slice.len;
                return bin_value;
            }

            /// Return the slice containing the actual value.
            pub fn constSlice(self: *const Self) []const u8 {
                return self.buf[0..self.len];
            }

            fn fromB64(self: *Self, str: []const u8) !void {
                const len = Codec.decodedLen(str.len);
                if (len > self.buf.len) return EncodingError.NoSpaceLeft;
                try Codec.decode(self.buf[0..len], str);
                self.len = len;
            }

            fn toB64(self: *const Self, buf: []u8) ![]const u8 {
                const value = self.constSlice();
                const len = Codec.encodedLen(value.len);
                if (len > buf.len) return EncodingError.NoSpaceLeft;
                const encoded = buf[0..len];
                Codec.encode(encoded, value);
                return encoded;
            }
        };
    }

    /// Expand binary data into a salt for the modular crypt format.
    pub fn saltFromBin(comptime len: usize, salt: [len]u8) [Codec.encodedLen(len)]u8 {
        var buf: [Codec.encodedLen(len)]u8 = undefined;
        Codec.encode(&buf, &salt);
        return buf;
    }

    /// Deserialize a string into a structure `T` (matching `HashResult`).
    pub fn deserialize(comptime T: type, str: []const u8) EncodingError!T {
        var out: T = undefined;

        if (str.len < 16) return EncodingError.InvalidEncoding;
        if (!mem.eql(u8, prefix, str[0..3])) return EncodingError.InvalidEncoding;
        out.ln = try Codec.intDecode(u6, str[3..4]);
        out.r = try Codec.intDecode(u30, str[4..9]);
        out.p = try Codec.intDecode(u30, str[9..14]);

        var it = mem.splitScalar(u8, str[14..], '$');

        const salt = it.first();
        if (@hasField(T, "salt")) out.salt = salt;

        const hash_str = it.next() orelse return EncodingError.InvalidEncoding;
        if (@hasField(T, "hash")) try out.hash.fromB64(hash_str);

        return out;
    }

    /// Serialize parameters into a string in modular crypt format.
    pub fn serialize(params: anytype, str: []u8) EncodingError![]const u8 {
        var w: std.Io.Writer = .fixed(str);
        serializeTo(params, &w) catch |err| switch (err) {
            error.WriteFailed => return error.NoSpaceLeft,
            else => |e| return e,
        };
        return w.buffered();
    }

    /// Compute the number of bytes required to serialize `params`
    pub fn calcSize(params: anytype) usize {
        var trash: [128]u8 = undefined;
        var d: std.Io.Writer.Discarding = .init(&trash);
        serializeTo(params, &d.writer) catch unreachable;
        return @intCast(d.fullCount());
    }

    fn serializeTo(params: anytype, w: *std.Io.Writer) !void {
        var header: [14]u8 = undefined;
        header[0..3].* = prefix.*;
        Codec.intEncode(header[3..4], params.ln);
        Codec.intEncode(header[4..9], params.r);
        Codec.intEncode(header[9..14], params.p);
        try w.writeAll(&header);
        try w.writeAll(params.salt);
        try w.writeAll("$");
        var buf: [@TypeOf(params.hash).max_encoded_length]u8 = undefined;
        const hash_str = try params.hash.toB64(&buf);
        try w.writeAll(hash_str);
    }

    /// Custom codec that maps 6 bits into 8 like regular Base64, but uses its own alphabet,
    /// encodes bits in little-endian, and can also encode integers.
    fn CustomB64Codec(comptime map: [64]u8) type {
        return struct {
            const map64 = map;

            fn encodedLen(len: usize) usize {
                return (len * 4 + 2) / 3;
            }

            fn decodedLen(len: usize) usize {
                return len / 4 * 3 + (len % 4) * 3 / 4;
            }

            fn intEncode(dst: []u8, src: anytype) void {
                var n = src;
                for (dst) |*x| {
                    x.* = map64[@as(u6, @truncate(n))];
                    n = math.shr(@TypeOf(src), n, 6);
                }
            }

            fn intDecode(comptime T: type, src: *const [(@bitSizeOf(T) + 5) / 6]u8) !T {
                var v: T = 0;
                for (src, 0..) |x, i| {
                    const vi = mem.findScalar(u8, &map64, x) orelse return EncodingError.InvalidEncoding;
                    v |= @as(T, @intCast(vi)) << @as(math.Log2Int(T), @intCast(i * 6));
                }
                return v;
            }

            fn decode(dst: []u8, src: []const u8) !void {
                std.debug.assert(dst.len == decodedLen(src.len));
                var i: usize = 0;
                while (i < src.len / 4) : (i += 1) {
                    mem.writeInt(u24, dst[i * 3 ..][0..3], try intDecode(u24, src[i * 4 ..][0..4]), .little);
                }
                const leftover = src[i * 4 ..];
                var v: u24 = 0;
                for (leftover, 0..) |_, j| {
                    v |= @as(u24, try intDecode(u6, leftover[j..][0..1])) << @as(u5, @intCast(j * 6));
                }
                for (dst[i * 3 ..], 0..) |*x, j| {
                    x.* = @as(u8, @truncate(v >> @as(u5, @intCast(j * 8))));
                }
            }

            fn encode(dst: []u8, src: []const u8) void {
                std.debug.assert(dst.len == encodedLen(src.len));
                var i: usize = 0;
                while (i < src.len / 3) : (i += 1) {
                    intEncode(dst[i * 4 ..][0..4], mem.readInt(u24, src[i * 3 ..][0..3], .little));
                }
                const leftover = src[i * 3 ..];
                var v: u24 = 0;
                for (leftover, 0..) |x, j| {
                    v |= @as(u24, x) << @as(u5, @intCast(j * 8));
                }
                intEncode(dst[i * 4 ..], v);
            }
        };
    }
}