| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | // https://tools.ietf.org/html/rfc7914 | 1 | // https://tools.ietf.org/html/rfc7914 |
| 2 | // https://github.com/golang/crypto/blob/master/scrypt/scrypt.go | 2 | // https://github.com/golang/crypto/blob/master/scrypt/scrypt.go |
| | 3 | // https://github.com/Tarsnap/scrypt |
| 3 | | 4 | |
| 4 | const std = @import("std"); | 5 | const std = @import("std"); |
| 5 | const crypto = std.crypto; | 6 | 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) | ... | @@ -119,11 +120,19 @@ fn smix(b: []align(16) u8, r: u30, n: usize, v: []align(16) u32, xy: []align(16) |
| 119 | } | 120 | } |
| 120 | } | 121 | } |
| 121 | | 122 | |
| | 123 | /// Scrypt parameters |
| 122 | pub const Params = struct { | 124 | pub const Params = struct { |
| 123 | const Self = @This(); | 125 | const Self = @This(); |
| 124 | | 126 | |
| | 127 | /// The CPU/Memory cost parameter [ln] is log2(N). |
| 125 | ln: u6, | 128 | ln: u6, |
| | 129 | |
| | 130 | /// The [r]esource usage parameter specifies the block size. |
| 126 | r: u30, | 131 | 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. |
| 127 | p: u30, | 136 | p: u30, |
| 128 | | 137 | |
| 129 | /// Baseline parameters for interactive logins | 138 | /// Baseline parameters for interactive logins |
| ... | @@ -132,7 +141,7 @@ pub const Params = struct { | ... | @@ -132,7 +141,7 @@ pub const Params = struct { |
| 132 | /// Baseline parameters for offline usage | 141 | /// Baseline parameters for offline usage |
| 133 | pub const sensitive = Self.fromLimits(33554432, 1073741824); | 142 | pub const sensitive = Self.fromLimits(33554432, 1073741824); |
| 134 | | 143 | |
| 135 | /// Create parameters from ops and mem limits | 144 | /// Create parameters from ops and mem limits, where mem_limit given in bytes |
| 136 | pub fn fromLimits(ops_limit: u64, mem_limit: usize) Self { | 145 | pub fn fromLimits(ops_limit: u64, mem_limit: usize) Self { |
| 137 | const ops = math.max(32768, ops_limit); | 146 | const ops = math.max(32768, ops_limit); |
| 138 | const r: u30 = 8; | 147 | const r: u30 = 8; |
| ... | @@ -170,7 +179,8 @@ pub fn kdf( | ... | @@ -170,7 +179,8 @@ pub fn kdf( |
| 170 | salt: []const u8, | 179 | salt: []const u8, |
| 171 | params: Params, | 180 | params: Params, |
| 172 | ) KdfError!void { | 181 | ) 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; |
| 174 | if (params.ln == 0 or params.r == 0 or params.p == 0) return KdfError.WeakParameters; | 184 | if (params.ln == 0 or params.r == 0 or params.p == 0) return KdfError.WeakParameters; |
| 175 | | 185 | |
| 176 | const n64 = @as(u64, 1) << params.ln; | 186 | const n64 = @as(u64, 1) << params.ln; |
| ... | @@ -484,6 +494,8 @@ const CryptFormatHasher = struct { | ... | @@ -484,6 +494,8 @@ const CryptFormatHasher = struct { |
| 484 | }; | 494 | }; |
| 485 | | 495 | |
| 486 | /// Options for hashing a password. | 496 | /// Options for hashing a password. |
| | 497 | /// |
| | 498 | /// Allocator is required for scrypt. |
| 487 | pub const HashOptions = struct { | 499 | pub const HashOptions = struct { |
| 488 | allocator: ?*mem.Allocator, | 500 | allocator: ?*mem.Allocator, |
| 489 | params: Params, | 501 | params: Params, |
| ... | @@ -505,6 +517,8 @@ pub fn strHash( | ... | @@ -505,6 +517,8 @@ pub fn strHash( |
| 505 | } | 517 | } |
| 506 | | 518 | |
| 507 | /// Options for hash verification. | 519 | /// Options for hash verification. |
| | 520 | /// |
| | 521 | /// Allocator is required for scrypt. |
| 508 | pub const VerifyOptions = struct { | 522 | pub const VerifyOptions = struct { |
| 509 | allocator: ?*mem.Allocator, | 523 | allocator: ?*mem.Allocator, |
| 510 | }; | 524 | }; |
| ... | @@ -609,14 +623,16 @@ test "kdf rfc 4" { | ... | @@ -609,14 +623,16 @@ test "kdf rfc 4" { |
| 609 | test "password hashing (crypt format)" { | 623 | test "password hashing (crypt format)" { |
| 610 | if (!run_long_tests) return error.SkipZigTest; | 624 | if (!run_long_tests) return error.SkipZigTest; |
| 611 | | 625 | |
| | 626 | const alloc = std.testing.allocator; |
| | 627 | |
| 612 | const str = "$7$A6....1....TrXs5Zk6s8sWHpQgWDIXTR8kUU3s6Jc3s.DtdS8M2i4$a4ik5hGDN7foMuHOW.cp.CtX01UyCeO0.JAG.AHPpx5"; | 628 | const str = "$7$A6....1....TrXs5Zk6s8sWHpQgWDIXTR8kUU3s6Jc3s.DtdS8M2i4$a4ik5hGDN7foMuHOW.cp.CtX01UyCeO0.JAG.AHPpx5"; |
| 613 | const password = "Y0!?iQa9M%5ekffW(`"; | 629 | const password = "Y0!?iQa9M%5ekffW(`"; |
| 614 | try CryptFormatHasher.verify(std.testing.allocator, str, password); | 630 | try CryptFormatHasher.verify(alloc, str, password); |
| 615 | | 631 | |
| 616 | const params = Params.interactive; | 632 | const params = Params.interactive; |
| 617 | var buf: [CryptFormatHasher.pwhash_str_length]u8 = undefined; | 633 | var buf: [CryptFormatHasher.pwhash_str_length]u8 = undefined; |
| 618 | const str2 = try CryptFormatHasher.create(std.testing.allocator, password, params, &buf); | 634 | const str2 = try CryptFormatHasher.create(alloc, password, params, &buf); |
| 619 | try CryptFormatHasher.verify(std.testing.allocator, str2, password); | 635 | try CryptFormatHasher.verify(alloc, str2, password); |
| 620 | } | 636 | } |
| 621 | | 637 | |
| 622 | test "strHash and strVerify" { | 638 | test "strHash and strVerify" { |
| ... | @@ -625,22 +641,26 @@ test "strHash and strVerify" { | ... | @@ -625,22 +641,26 @@ test "strHash and strVerify" { |
| 625 | const alloc = std.testing.allocator; | 641 | const alloc = std.testing.allocator; |
| 626 | | 642 | |
| 627 | const password = "testpass"; | 643 | const password = "testpass"; |
| | 644 | const params = Params.interactive; |
| 628 | const verify_options = VerifyOptions{ .allocator = alloc }; | 645 | const verify_options = VerifyOptions{ .allocator = alloc }; |
| 629 | var buf: [128]u8 = undefined; | 646 | var buf: [128]u8 = undefined; |
| 630 | | 647 | |
| 631 | const s = try strHash( | 648 | { |
| 632 | password, | 649 | const str = try strHash( |
| 633 | HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .crypt }, | 650 | password, |
| 634 | &buf, | 651 | .{ .allocator = alloc, .params = params, .encoding = .crypt }, |
| 635 | ); | 652 | &buf, |
| 636 | try strVerify(s, password, verify_options); | 653 | ); |
| 637 | | 654 | try strVerify(str, password, verify_options); |
| 638 | const s1 = try strHash( | 655 | } |
| 639 | password, | 656 | { |
| 640 | HashOptions{ .allocator = alloc, .params = Params.interactive, .encoding = .phc }, | 657 | const str = try strHash( |
| 641 | &buf, | 658 | password, |
| 642 | ); | 659 | .{ .allocator = alloc, .params = params, .encoding = .phc }, |
| 643 | try strVerify(s1, password, verify_options); | 660 | &buf, |
| | 661 | ); |
| | 662 | try strVerify(str, password, verify_options); |
| | 663 | } |
| 644 | } | 664 | } |
| 645 | | 665 | |
| 646 | test "unix-scrypt" { | 666 | test "unix-scrypt" { |
| ... | @@ -669,3 +689,28 @@ test "crypt format" { | ... | @@ -669,3 +689,28 @@ test "crypt format" { |
| 669 | const s1 = try crypt_format.serialize(params, &buf); | 689 | const s1 = try crypt_format.serialize(params, &buf); |
| 670 | try std.testing.expectEqualStrings(s1, str); | 690 | try std.testing.expectEqualStrings(s1, str); |
| 671 | } | 691 | } |
| | 692 | |
| | 693 | test "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 | } |