authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-03-02 13:41:50 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-03-02 13:41:50 -08:00
log09882358129ac88cecdea8808a47c3d3d7903d02
tree5b8fb6a67badb3db1f5396378a17f8d891ae3029
parenta951b17d475ed3f64d8b0c3bf1c85efd049e705c

add decodeBase32


2 files changed, 30 insertions(+), 0 deletions(-)

test.zig+8
...@@ -59,3 +59,11 @@ test {...@@ -59,3 +59,11 @@ test {
59 defer allocator.free(url);59 defer allocator.free(url);
60 try expect(url).toEqualString("otpauth://totp/otpauth%20demo:username@example.org?secret=AAAABBBB22223333YYYYZZZZ66667777&algorithm=SHA1&digits=6&period=30&issuer=otpauth%20demo");60 try expect(url).toEqualString("otpauth://totp/otpauth%20demo:username@example.org?secret=AAAABBBB22223333YYYYZZZZ66667777&algorithm=SHA1&digits=6&period=30&issuer=otpauth%20demo");
61}61}
62
63test {
64 const b32 = "SC5IH4S3HJLEWOGXJM4BL6CH5BDSYHBV";
65 const hex_expected = "90ba83f25b3a564b38d74b3815f847e8472c1c35";
66 const raw_actual = try totp.decodeBase32(b32, std.crypto.hash.Sha1);
67 const hex_actual = extras.to_hex(raw_actual);
68 try expect(&hex_actual).toEqualString(hex_expected);
69}
totp.zig+22
...@@ -123,3 +123,25 @@ const BufBiterator = struct {...@@ -123,3 +123,25 @@ const BufBiterator = struct {
123 return @intFromBool(result);123 return @intFromBool(result);
124 }124 }
125};125};
126
127pub fn decodeBase32(input: []const u8, Hash: type) ![Hash.digest_length]u8 {
128 var result: std.bit_set.ArrayBitSet(u8, Hash.digest_length * 8) = .initEmpty();
129 const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
130 for (input, 0..) |c, i| {
131 const x = std.mem.indexOfScalar(u8, alphabet, c) orelse return error.InvalidCharacter;
132 const n: u5 = @intCast(x);
133 inline for (0..5) |j| blk: {
134 if (i * 5 > result.capacity()) return error.Overflow;
135 const d = i * 5 + j;
136 if (d >= result.capacity()) break :blk;
137 result.setValue(d, n & (1 << 5 - 1 - (j)) != 0);
138 }
139 }
140 return bitReverse(result.masks);
141}
142
143fn bitReverse(array: anytype) [array.len]std.meta.Child(@TypeOf(array)) {
144 var result: [array.len]std.meta.Child(@TypeOf(array)) = @splat(0);
145 for (&array, &result) |x, *y| y.* = @bitReverse(x);
146 return result;
147}