| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const extras = @import("extras"); | 2 | const extras = @import("extras"); |
| 3 | const url = @import("url"); | 3 | const url = @import("url"); |
| | 4 | const nio = @import("nio"); |
| 4 | | 5 | |
| 5 | pub fn totp(digits: comptime_int, Hash: type, epoch: u64, X: u64, K: *const [Hash.digest_length]u8, time_now_s: u64) [digits]u8 { | 6 | pub fn totp(digits: comptime_int, Hash: type, epoch: u64, X: u64, K: *const [Hash.digest_length]u8, time_now_s: u64) [digits]u8 { |
| 6 | const T = (time_now_s - epoch) / X; | 7 | const T = (time_now_s - epoch) / X; |
| ... | @@ -56,32 +57,32 @@ pub fn generateUrl(allocator: std.mem.Allocator, issuer: []const u8, account: [] | ... | @@ -56,32 +57,32 @@ pub fn generateUrl(allocator: std.mem.Allocator, issuer: []const u8, account: [] |
| 56 | std.debug.assert(secret_raw.len <= algo.digest_length()); | 57 | std.debug.assert(secret_raw.len <= algo.digest_length()); |
| 57 | std.debug.assert(digits == 6 or digits == 7 or digits == 8); | 58 | std.debug.assert(digits == 6 or digits == 7 or digits == 8); |
| 58 | std.debug.assert(period == 15 or period == 30 or period == 60); | 59 | std.debug.assert(period == 15 or period == 30 or period == 60); |
| 59 | var list: std.array_list.Managed(u8) = .init(allocator); | 60 | var list: nio.AllocatingWriter = .init(allocator); |
| 60 | errdefer list.deinit(); | 61 | errdefer list.deinit(); |
| 61 | try list.appendSlice("otpauth://"); | 62 | try list.writeAll("otpauth://"); |
| 62 | try list.appendSlice("totp/"); | 63 | try list.writeAll("totp/"); |
| 63 | try url.percentEncodeAL(&list, issuer, url.is_path_percent_char); | 64 | try url.percentEncodeAL(&list, issuer, url.is_path_percent_char); |
| 64 | try list.append(':'); | 65 | try list.writeAll(":"); |
| 65 | try url.percentEncodeAL(&list, account, url.is_path_percent_char); | 66 | try url.percentEncodeAL(&list, account, url.is_path_percent_char); |
| 66 | try list.appendSlice("?secret="); | 67 | try list.writeAll("?secret="); |
| 67 | try encodeBase32(&list, secret_raw); | 68 | try encodeBase32(&list, secret_raw); |
| 68 | try list.appendSlice("&algorithm="); | 69 | try list.writeAll("&algorithm="); |
| 69 | try list.appendSlice(@tagName(algo)); | 70 | try list.writeAll(@tagName(algo)); |
| 70 | try list.appendSlice("&digits="); | 71 | try list.writeAll("&digits="); |
| 71 | try list.writer().print("{d}", .{digits}); | 72 | try list.print("{d}", .{digits}); |
| 72 | try list.appendSlice("&period="); | 73 | try list.writeAll("&period="); |
| 73 | try list.writer().print("{d}", .{period}); | 74 | try list.print("{d}", .{period}); |
| 74 | try list.appendSlice("&issuer="); | 75 | try list.writeAll("&issuer="); |
| 75 | try url.percentEncodeAL(&list, issuer, url.is_query_percent_char); | 76 | try url.percentEncodeAL(&list, issuer, url.is_query_percent_char); |
| 76 | return list.toOwnedSlice(); | 77 | return list.toOwnedSlice(); |
| 77 | } | 78 | } |
| 78 | | 79 | |
| 79 | // RFC3548 base32 | 80 | // RFC3548 base32 |
| 80 | // input.len is gonna be 64 | 32 | 64 | 81 | // input.len is gonna be 64 | 32 | 64 |
| 81 | fn encodeBase32(list: *std.array_list.Managed(u8), input: []const u8) !void { | 82 | fn encodeBase32(list: *nio.AllocatingWriter, input: []const u8) !void { |
| 82 | const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; | 83 | const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; |
| 83 | var iter = BufBiterator.init(input); | 84 | var iter = BufBiterator.init(input); |
| 84 | while (iter.nextInt(u5)) |idx| try list.append(alphabet[idx]); | 85 | while (iter.nextInt(u5)) |idx| try list.writeAll(alphabet[idx..][0..1]); |
| 85 | } | 86 | } |
| 86 | | 87 | |
| 87 | const BufBiterator = struct { | 88 | const BufBiterator = struct { |