Commit 22b71b1376

Frank Denis <github@pureftpd.org>
2022-10-25 20:34:24
crypto/bcrypt: don't reimplement base64, just use a custom alphabet
Now that std.base64 supports everything bcrypt needs to encode its parameters, we don't need to include another implementation.
1 parent 55c5da1
Changed files (1)
lib
std
crypto
lib/std/crypto/bcrypt.zig
@@ -1,4 +1,5 @@
 const std = @import("std");
+const base64 = std.base64;
 const crypto = std.crypto;
 const debug = std.debug;
 const fmt = std.fmt;
@@ -533,71 +534,10 @@ const crypt_format = struct {
     pub const prefix = "$2";
 
     // bcrypt has its own variant of base64, with its own alphabet and no padding
-    const Codec = struct {
-        const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
-        fn encode(b64: []u8, bin: []const u8) void {
-            var i: usize = 0;
-            var j: usize = 0;
-            while (i < bin.len) {
-                var c1 = bin[i];
-                i += 1;
-                b64[j] = alphabet[c1 >> 2];
-                j += 1;
-                c1 = (c1 & 3) << 4;
-                if (i >= bin.len) {
-                    b64[j] = alphabet[c1];
-                    j += 1;
-                    break;
-                }
-                var c2 = bin[i];
-                i += 1;
-                c1 |= (c2 >> 4) & 0x0f;
-                b64[j] = alphabet[c1];
-                j += 1;
-                c1 = (c2 & 0x0f) << 2;
-                if (i >= bin.len) {
-                    b64[j] = alphabet[c1];
-                    j += 1;
-                    break;
-                }
-                c2 = bin[i];
-                i += 1;
-                c1 |= (c2 >> 6) & 3;
-                b64[j] = alphabet[c1];
-                b64[j + 1] = alphabet[c2 & 0x3f];
-                j += 2;
-            }
-            debug.assert(j == b64.len);
-        }
-
-        fn decode(bin: []u8, b64: []const u8) EncodingError!void {
-            var i: usize = 0;
-            var j: usize = 0;
-            while (j < bin.len) {
-                const c1 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i]) orelse
-                    return EncodingError.InvalidEncoding);
-                const c2 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 1]) orelse
-                    return EncodingError.InvalidEncoding);
-                bin[j] = (c1 << 2) | ((c2 & 0x30) >> 4);
-                j += 1;
-                if (j >= bin.len) {
-                    break;
-                }
-                const c3 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 2]) orelse
-                    return EncodingError.InvalidEncoding);
-                bin[j] = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
-                j += 1;
-                if (j >= bin.len) {
-                    break;
-                }
-                const c4 = @intCast(u8, mem.indexOfScalar(u8, alphabet, b64[i + 3]) orelse
-                    return EncodingError.InvalidEncoding);
-                bin[j] = ((c3 & 0x03) << 6) | c4;
-                j += 1;
-                i += 4;
-            }
-        }
+    const bcrypt_alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".*;
+    const Codec = struct { Encoder: base64.Base64Encoder, Decoder: base64.Base64Decoder }{
+        .Encoder = base64.Base64Encoder.init(bcrypt_alphabet, null),
+        .Decoder = base64.Base64Decoder.init(bcrypt_alphabet, null),
     };
 
     fn strHashInternal(
@@ -608,10 +548,10 @@ const crypt_format = struct {
         var dk = bcrypt(password, salt, params);
 
         var salt_str: [salt_str_length]u8 = undefined;
-        Codec.encode(salt_str[0..], salt[0..]);
+        _ = Codec.Encoder.encode(salt_str[0..], salt[0..]);
 
         var ct_str: [ct_str_length]u8 = undefined;
-        Codec.encode(ct_str[0..], dk[0..]);
+        _ = Codec.Encoder.encode(ct_str[0..], dk[0..]);
 
         var s_buf: [hash_length]u8 = undefined;
         const s = fmt.bufPrint(
@@ -709,7 +649,7 @@ const CryptFormatHasher = struct {
 
         const salt_str = str[7..][0..salt_str_length];
         var salt: [salt_length]u8 = undefined;
-        try crypt_format.Codec.decode(salt[0..], salt_str[0..]);
+        crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding;
 
         const wanted_s = crypt_format.strHashInternal(password, salt, .{ .rounds_log = rounds_log });
         if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed;
@@ -764,9 +704,9 @@ test "bcrypt codec" {
     var salt: [salt_length]u8 = undefined;
     crypto.random.bytes(&salt);
     var salt_str: [salt_str_length]u8 = undefined;
-    crypt_format.Codec.encode(salt_str[0..], salt[0..]);
+    _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);
     var salt2: [salt_length]u8 = undefined;
-    try crypt_format.Codec.decode(salt2[0..], salt_str[0..]);
+    try crypt_format.Codec.Decoder.decode(salt2[0..], salt_str[0..]);
     try testing.expectEqualSlices(u8, salt[0..], salt2[0..]);
 }