| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const base64 = std.base64; |
| 2 | 3 | const crypto = std.crypto; |
| 3 | 4 | const debug = std.debug; |
| 4 | 5 | const fmt = std.fmt; |
| ... | ... | @@ -533,71 +534,10 @@ const crypt_format = struct { |
| 533 | 534 | pub const prefix = "$2"; |
| 534 | 535 | |
| 535 | 536 | // bcrypt has its own variant of base64, with its own alphabet and no padding |
| 536 | | const Codec = struct { |
| 537 | | const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
| 538 | | |
| 539 | | fn encode(b64: []u8, bin: []const u8) void { |
| 540 | | var i: usize = 0; |
| 541 | | var j: usize = 0; |
| 542 | | while (i < bin.len) { |
| 543 | | var c1 = bin[i]; |
| 544 | | i += 1; |
| 545 | | b64[j] = alphabet[c1 >> 2]; |
| 546 | | j += 1; |
| 547 | | c1 = (c1 & 3) << 4; |
| 548 | | if (i >= bin.len) { |
| 549 | | b64[j] = alphabet[c1]; |
| 550 | | j += 1; |
| 551 | | break; |
| 552 | | } |
| 553 | | var c2 = bin[i]; |
| 554 | | i += 1; |
| 555 | | c1 |= (c2 >> 4) & 0x0f; |
| 556 | | b64[j] = alphabet[c1]; |
| 557 | | j += 1; |
| 558 | | c1 = (c2 & 0x0f) << 2; |
| 559 | | if (i >= bin.len) { |
| 560 | | b64[j] = alphabet[c1]; |
| 561 | | j += 1; |
| 562 | | break; |
| 563 | | } |
| 564 | | c2 = bin[i]; |
| 565 | | i += 1; |
| 566 | | c1 |= (c2 >> 6) & 3; |
| 567 | | b64[j] = alphabet[c1]; |
| 568 | | b64[j + 1] = alphabet[c2 & 0x3f]; |
| 569 | | j += 2; |
| 570 | | } |
| 571 | | debug.assert(j == b64.len); |
| 572 | | } |
| 573 | | |
| 574 | | fn decode(bin: []u8, b64: []const u8) EncodingError!void { |
| 575 | | var i: usize = 0; |
| 576 | | var j: usize = 0; |
| 577 | | while (j < bin.len) { |
| 578 | | const c1 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i]) orelse |
| 579 | | return EncodingError.InvalidEncoding); |
| 580 | | const c2 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 1]) orelse |
| 581 | | return EncodingError.InvalidEncoding); |
| 582 | | bin[j] = (c1 << 2) | ((c2 & 0x30) >> 4); |
| 583 | | j += 1; |
| 584 | | if (j >= bin.len) { |
| 585 | | break; |
| 586 | | } |
| 587 | | const c3 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 2]) orelse |
| 588 | | return EncodingError.InvalidEncoding); |
| 589 | | bin[j] = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); |
| 590 | | j += 1; |
| 591 | | if (j >= bin.len) { |
| 592 | | break; |
| 593 | | } |
| 594 | | const c4 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 3]) orelse |
| 595 | | return EncodingError.InvalidEncoding); |
| 596 | | bin[j] = ((c3 & 0x03) << 6) | c4; |
| 597 | | j += 1; |
| 598 | | i += 4; |
| 599 | | } |
| 600 | | } |
| 537 | const bcrypt_alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".*; |
| 538 | const Codec = struct { Encoder: base64.Base64Encoder, Decoder: base64.Base64Decoder }{ |
| 539 | .Encoder = base64.Base64Encoder.init(bcrypt_alphabet, null), |
| 540 | .Decoder = base64.Base64Decoder.init(bcrypt_alphabet, null), |
| 601 | 541 | }; |
| 602 | 542 | |
| 603 | 543 | fn strHashInternal( |
| ... | ... | @@ -608,10 +548,10 @@ const crypt_format = struct { |
| 608 | 548 | var dk = bcrypt(password, salt, params); |
| 609 | 549 | |
| 610 | 550 | var salt_str: [salt_str_length]u8 = undefined; |
| 611 | | Codec.encode(salt_str[0..], salt[0..]); |
| 551 | _ = Codec.Encoder.encode(salt_str[0..], salt[0..]); |
| 612 | 552 | |
| 613 | 553 | var ct_str: [ct_str_length]u8 = undefined; |
| 614 | | Codec.encode(ct_str[0..], dk[0..]); |
| 554 | _ = Codec.Encoder.encode(ct_str[0..], dk[0..]); |
| 615 | 555 | |
| 616 | 556 | var s_buf: [hash_length]u8 = undefined; |
| 617 | 557 | const s = fmt.bufPrint( |
| ... | ... | @@ -709,7 +649,7 @@ const CryptFormatHasher = struct { |
| 709 | 649 | |
| 710 | 650 | const salt_str = str[7..][0..salt_str_length]; |
| 711 | 651 | var salt: [salt_length]u8 = undefined; |
| 712 | | try crypt_format.Codec.decode(salt[0..], salt_str[0..]); |
| 652 | crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding; |
| 713 | 653 | |
| 714 | 654 | const wanted_s = crypt_format.strHashInternal(password, salt, .{ .rounds_log = rounds_log }); |
| 715 | 655 | if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed; |
| ... | ... | @@ -764,9 +704,9 @@ test "bcrypt codec" { |
| 764 | 704 | var salt: [salt_length]u8 = undefined; |
| 765 | 705 | crypto.random.bytes(&salt); |
| 766 | 706 | var salt_str: [salt_str_length]u8 = undefined; |
| 767 | | crypt_format.Codec.encode(salt_str[0..], salt[0..]); |
| 707 | _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]); |
| 768 | 708 | var salt2: [salt_length]u8 = undefined; |
| 769 | | try crypt_format.Codec.decode(salt2[0..], salt_str[0..]); |
| 709 | try crypt_format.Codec.Decoder.decode(salt2[0..], salt_str[0..]); |
| 770 | 710 | try testing.expectEqualSlices(u8, salt[0..], salt2[0..]); |
| 771 | 711 | } |
| 772 | 712 | |