Commit 590880158a

lucky <53379023+x13a@users.noreply.github.com>
2021-11-15 20:48:24
update docs (#10150)
add fast kdf test fix inconsistent kdf error refactor Co-authored-by: lucky <>
1 parent d3135f7
Changed files (1)
lib
std
crypto
lib/std/crypto/scrypt.zig
@@ -1,5 +1,6 @@
 // https://tools.ietf.org/html/rfc7914
 // https://github.com/golang/crypto/blob/master/scrypt/scrypt.go
+// https://github.com/Tarsnap/scrypt
 
 const std = @import("std");
 const crypto = std.crypto;
@@ -119,11 +120,19 @@ fn smix(b: []align(16) u8, r: u30, n: usize, v: []align(16) u32, xy: []align(16)
     }
 }
 
+/// Scrypt parameters
 pub const Params = struct {
     const Self = @This();
 
+    /// The CPU/Memory cost parameter [ln] is log2(N).
     ln: u6,
+
+    /// The [r]esource usage parameter specifies the block size.
     r: u30,
+
+    /// The [p]arallelization parameter.
+    /// A large value of [p] can be used to increase the computational cost of scrypt without 
+    /// increasing the memory usage.
     p: u30,
 
     /// Baseline parameters for interactive logins
@@ -132,7 +141,7 @@ pub const Params = struct {
     /// Baseline parameters for offline usage
     pub const sensitive = Self.fromLimits(33554432, 1073741824);
 
-    /// Create parameters from ops and mem limits
+    /// Create parameters from ops and mem limits, where mem_limit given in bytes
     pub fn fromLimits(ops_limit: u64, mem_limit: usize) Self {
         const ops = math.max(32768, ops_limit);
         const r: u30 = 8;
@@ -170,7 +179,8 @@ pub fn kdf(
     salt: []const u8,
     params: Params,
 ) KdfError!void {
-    if (derived_key.len == 0 or derived_key.len / 32 > 0xffff_ffff) return KdfError.OutputTooLong;
+    if (derived_key.len == 0) return KdfError.WeakParameters;
+    if (derived_key.len / 32 > 0xffff_ffff) return KdfError.OutputTooLong;
     if (params.ln == 0 or params.r == 0 or params.p == 0) return KdfError.WeakParameters;
 
     const n64 = @as(u64, 1) << params.ln;
@@ -484,6 +494,8 @@ const CryptFormatHasher = struct {
 };
 
 /// Options for hashing a password.
+///
+/// Allocator is required for scrypt.
 pub const HashOptions = struct {
     allocator: ?*mem.Allocator,
     params: Params,
@@ -505,6 +517,8 @@ pub fn strHash(
 }
 
 /// Options for hash verification.
+///
+/// Allocator is required for scrypt.
 pub const VerifyOptions = struct {
     allocator: ?*mem.Allocator,
 };
@@ -609,14 +623,16 @@ test "kdf rfc 4" {
 test "password hashing (crypt format)" {
     if (!run_long_tests) return error.SkipZigTest;
 
+    const alloc = std.testing.allocator;
+
     const str = "$7$A6....1....TrXs5Zk6s8sWHpQgWDIXTR8kUU3s6Jc3s.DtdS8M2i4$a4ik5hGDN7foMuHOW.cp.CtX01UyCeO0.JAG.AHPpx5";
     const password = "Y0!?iQa9M%5ekffW(`";
-    try CryptFormatHasher.verify(std.testing.allocator, str, password);
+    try CryptFormatHasher.verify(alloc, str, password);
 
     const params = Params.interactive;
     var buf: [CryptFormatHasher.pwhash_str_length]u8 = undefined;
-    const str2 = try CryptFormatHasher.create(std.testing.allocator, password, params, &buf);
-    try CryptFormatHasher.verify(std.testing.allocator, str2, password);
+    const str2 = try CryptFormatHasher.create(alloc, password, params, &buf);
+    try CryptFormatHasher.verify(alloc, str2, password);
 }
 
 test "strHash and strVerify" {
@@ -625,22 +641,26 @@ test "strHash and strVerify" {
     const alloc = std.testing.allocator;
 
     const password = "testpass";
+    const params = Params.interactive;
     const verify_options = VerifyOptions{ .allocator = alloc };
     var buf: [128]u8 = undefined;
 
-    const s = try strHash(
-        password,
-        HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .crypt },
-        &buf,
-    );
-    try strVerify(s, password, verify_options);
-
-    const s1 = try strHash(
-        password,
-        HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .phc },
-        &buf,
-    );
-    try strVerify(s1, password, verify_options);
+    {
+        const str = try strHash(
+            password,
+            .{ .allocator = alloc, .params = params, .encoding = .crypt },
+            &buf,
+        );
+        try strVerify(str, password, verify_options);
+    }
+    {
+        const str = try strHash(
+            password,
+            .{ .allocator = alloc, .params = params, .encoding = .phc },
+            &buf,
+        );
+        try strVerify(str, password, verify_options);
+    }
 }
 
 test "unix-scrypt" {
@@ -669,3 +689,28 @@ test "crypt format" {
     const s1 = try crypt_format.serialize(params, &buf);
     try std.testing.expectEqualStrings(s1, str);
 }
+
+test "kdf fast" {
+    const TestVector = struct {
+        password: []const u8,
+        salt: []const u8,
+        params: Params,
+        want: []const u8,
+    };
+    const test_vectors = [_]TestVector{
+        .{
+            .password = "p",
+            .salt = "s",
+            .params = .{ .ln = 1, .r = 1, .p = 1 },
+            .want = &([_]u8{
+                0x48, 0xb0, 0xd2, 0xa8, 0xa3, 0x27, 0x26, 0x11,
+                0x98, 0x4c, 0x50, 0xeb, 0xd6, 0x30, 0xaf, 0x52,
+            }),
+        },
+    };
+    inline for (test_vectors) |v| {
+        var dk: [v.want.len]u8 = undefined;
+        try kdf(std.testing.allocator, &dk, v.password, v.salt, v.params);
+        try std.testing.expectEqualSlices(u8, &dk, v.want);
+    }
+}