| author | |
| committer | |
| log | 09882358129ac88cecdea8808a47c3d3d7903d02 |
| tree | 5b8fb6a67badb3db1f5396378a17f8d891ae3029 |
| parent | a951b17d475ed3f64d8b0c3bf1c85efd049e705c |
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 | |||
| 63 | test { | ||
| 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 | |||
| 127 | pub 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 | |||
| 143 | fn 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 | } |