authorgravatar for 53379023+x13a@users.noreply.github.comlucky <53379023+x13a@users.noreply.github.com> 2021-11-15 22:48:24+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-15 20:48:24+01:00
log590880158a03fa97056b30a3379aab5328ccc977
tree2e253aa0712ac4036c9b7d376e42d97f9c67c7ee
parentd3135f76823a053c4e580830f6a6c080caae5ab5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

update docs (#10150)

add fast kdf test fix inconsistent kdf error refactor Co-authored-by: lucky <>

1 files changed, 63 insertions(+), 18 deletions(-)

lib/std/crypto/scrypt.zig+63-18
......@@ -1,5 +1,6 @@
11// https://tools.ietf.org/html/rfc7914
22// https://github.com/golang/crypto/blob/master/scrypt/scrypt.go
3// https://github.com/Tarsnap/scrypt
34
45const std = @import("std");
56const crypto = std.crypto;
......@@ -119,11 +120,19 @@ fn smix(b: []align(16) u8, r: u30, n: usize, v: []align(16) u32, xy: []align(16)
119120 }
120121}
121122
123/// Scrypt parameters
122124pub const Params = struct {
123125 const Self = @This();
124126
127 /// The CPU/Memory cost parameter [ln] is log2(N).
125128 ln: u6,
129
130 /// The [r]esource usage parameter specifies the block size.
126131 r: u30,
132
133 /// The [p]arallelization parameter.
134 /// A large value of [p] can be used to increase the computational cost of scrypt without
135 /// increasing the memory usage.
127136 p: u30,
128137
129138 /// Baseline parameters for interactive logins
......@@ -132,7 +141,7 @@ pub const Params = struct {
132141 /// Baseline parameters for offline usage
133142 pub const sensitive = Self.fromLimits(33554432, 1073741824);
134143
135 /// Create parameters from ops and mem limits
144 /// Create parameters from ops and mem limits, where mem_limit given in bytes
136145 pub fn fromLimits(ops_limit: u64, mem_limit: usize) Self {
137146 const ops = math.max(32768, ops_limit);
138147 const r: u30 = 8;
......@@ -170,7 +179,8 @@ pub fn kdf(
170179 salt: []const u8,
171180 params: Params,
172181) KdfError!void {
173 if (derived_key.len == 0 or derived_key.len / 32 > 0xffff_ffff) return KdfError.OutputTooLong;
182 if (derived_key.len == 0) return KdfError.WeakParameters;
183 if (derived_key.len / 32 > 0xffff_ffff) return KdfError.OutputTooLong;
174184 if (params.ln == 0 or params.r == 0 or params.p == 0) return KdfError.WeakParameters;
175185
176186 const n64 = @as(u64, 1) << params.ln;
......@@ -484,6 +494,8 @@ const CryptFormatHasher = struct {
484494};
485495
486496/// Options for hashing a password.
497///
498/// Allocator is required for scrypt.
487499pub const HashOptions = struct {
488500 allocator: ?*mem.Allocator,
489501 params: Params,
......@@ -505,6 +517,8 @@ pub fn strHash(
505517}
506518
507519/// Options for hash verification.
520///
521/// Allocator is required for scrypt.
508522pub const VerifyOptions = struct {
509523 allocator: ?*mem.Allocator,
510524};
......@@ -609,14 +623,16 @@ test "kdf rfc 4" {
609623test "password hashing (crypt format)" {
610624 if (!run_long_tests) return error.SkipZigTest;
611625
626 const alloc = std.testing.allocator;
627
612628 const str = "$7$A6....1....TrXs5Zk6s8sWHpQgWDIXTR8kUU3s6Jc3s.DtdS8M2i4$a4ik5hGDN7foMuHOW.cp.CtX01UyCeO0.JAG.AHPpx5";
613629 const password = "Y0!?iQa9M%5ekffW(`";
614 try CryptFormatHasher.verify(std.testing.allocator, str, password);
630 try CryptFormatHasher.verify(alloc, str, password);
615631
616632 const params = Params.interactive;
617633 var buf: [CryptFormatHasher.pwhash_str_length]u8 = undefined;
618 const str2 = try CryptFormatHasher.create(std.testing.allocator, password, params, &buf);
619 try CryptFormatHasher.verify(std.testing.allocator, str2, password);
634 const str2 = try CryptFormatHasher.create(alloc, password, params, &buf);
635 try CryptFormatHasher.verify(alloc, str2, password);
620636}
621637
622638test "strHash and strVerify" {
......@@ -625,22 +641,26 @@ test "strHash and strVerify" {
625641 const alloc = std.testing.allocator;
626642
627643 const password = "testpass";
644 const params = Params.interactive;
628645 const verify_options = VerifyOptions{ .allocator = alloc };
629646 var buf: [128]u8 = undefined;
630647
631 const s = try strHash(
632 password,
633 HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .crypt },
634 &buf,
635 );
636 try strVerify(s, password, verify_options);
637
638 const s1 = try strHash(
639 password,
640 HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .phc },
641 &buf,
642 );
643 try strVerify(s1, password, verify_options);
648 {
649 const str = try strHash(
650 password,
651 .{ .allocator = alloc, .params = params, .encoding = .crypt },
652 &buf,
653 );
654 try strVerify(str, password, verify_options);
655 }
656 {
657 const str = try strHash(
658 password,
659 .{ .allocator = alloc, .params = params, .encoding = .phc },
660 &buf,
661 );
662 try strVerify(str, password, verify_options);
663 }
644664}
645665
646666test "unix-scrypt" {
......@@ -669,3 +689,28 @@ test "crypt format" {
669689 const s1 = try crypt_format.serialize(params, &buf);
670690 try std.testing.expectEqualStrings(s1, str);
671691}
692
693test "kdf fast" {
694 const TestVector = struct {
695 password: []const u8,
696 salt: []const u8,
697 params: Params,
698 want: []const u8,
699 };
700 const test_vectors = [_]TestVector{
701 .{
702 .password = "p",
703 .salt = "s",
704 .params = .{ .ln = 1, .r = 1, .p = 1 },
705 .want = &([_]u8{
706 0x48, 0xb0, 0xd2, 0xa8, 0xa3, 0x27, 0x26, 0x11,
707 0x98, 0x4c, 0x50, 0xeb, 0xd6, 0x30, 0xaf, 0x52,
708 }),
709 },
710 };
711 inline for (test_vectors) |v| {
712 var dk: [v.want.len]u8 = undefined;
713 try kdf(std.testing.allocator, &dk, v.password, v.salt, v.params);
714 try std.testing.expectEqualSlices(u8, &dk, v.want);
715 }
716}