finalize computation of the code
This commit is contained in:
90
src/main.zig
90
src/main.zig
@@ -18,18 +18,17 @@ pub fn main() !void {
|
||||
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
|
||||
std.log.debug("arguments: {s}\n", .{args});
|
||||
const arg: Args = try parseArgs(allocator, args);
|
||||
//print("parsed Args: {?any}\n", arg);
|
||||
if (arg.list) {
|
||||
const names = try executeGetList(allocator, arg.config_location);
|
||||
|
||||
for (0..names.items.len) |i| {
|
||||
std.debug.print("{s}\n", .{names.items[i]});
|
||||
try std.io.getStdOut().writer().print("{s}\n", .{names.items[i]});
|
||||
}
|
||||
} else if (arg.show != null) {
|
||||
const authenticator = try getAuthenticator(allocator, arg.show.?, arg.config_location);
|
||||
debug("number for {s}", .{try authenticator.code()});
|
||||
try std.io.getStdOut().writer().print("{s}\n", .{try authenticator.code(allocator, std.time.timestamp)});
|
||||
} else {
|
||||
printHelp();
|
||||
}
|
||||
@@ -63,20 +62,67 @@ const OtpAuthUrl = struct { name: []const u8, secretEncoded: []const u8, url: []
|
||||
const Authenticator = struct {
|
||||
url: OtpAuthUrl,
|
||||
|
||||
pub fn code(self: Authenticator) ![]const u8 {
|
||||
pub fn code(self: Authenticator, allocator: Allocator, timeFunc: *const fn () i64) ![]const u8 {
|
||||
if (self.url.name.len > 0) {}
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
const secret = try Base32.decodeU8(allocator, self.url.secretEncoded);
|
||||
defer allocator.free(secret);
|
||||
if (false) {
|
||||
debug("secret: {s}\n", .{secret});
|
||||
}
|
||||
|
||||
return "code";
|
||||
const intervalNumber = @divTrunc(timeFunc(), self.url.period);
|
||||
|
||||
var intervalAsU8Array: [8]u8 = undefined;
|
||||
std.mem.writePackedInt(i64, intervalAsU8Array[0..], 0, intervalNumber, .big);
|
||||
//debug("interval packed: {X}\n", .{intervalAsU8Array});
|
||||
|
||||
//debug("intervalNumber: {d}\n", .{intervalNumber});
|
||||
|
||||
var out: [std.crypto.auth.hmac.HmacSha1.mac_length]u8 = undefined;
|
||||
std.crypto.auth.hmac.HmacSha1.create(out[0..], &intervalAsU8Array, secret);
|
||||
//debug("hmac: {X}\n", .{out});
|
||||
|
||||
// take the 4 least significant bits of the hash and use them as byte offset
|
||||
const leastSignificantByte = out[std.crypto.auth.hmac.HmacSha1.mac_length - 1];
|
||||
const byteIndex = leastSignificantByte & 0b1111;
|
||||
//debug("index: {d}\n", .{byteIndex});
|
||||
|
||||
const x: [4]u8 = [4]u8{ out[byteIndex], out[byteIndex + 1], out[byteIndex + 2], out[byteIndex + 3] };
|
||||
|
||||
const tokenBase = std.mem.readInt(i32, &x, .big) & 0x7fffffff;
|
||||
//debug("tokenBase: {d}\n", .{tokenBase});
|
||||
const token = @mod(tokenBase, (std.math.pow(i64, 10, self.url.digits)));
|
||||
|
||||
const result = try zeroPad(allocator, self.url.digits, token);
|
||||
//debug("code as 0-padded string: {s} ({d})\n", .{ result, token });
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
fn zeroPad(allocator: Allocator, digits: u4, x: anytype) ![]u8 {
|
||||
const result = try allocator.alloc(u8, digits);
|
||||
|
||||
const s = try std.fmt.allocPrint(allocator, "{d}", .{x});
|
||||
defer allocator.free(s);
|
||||
|
||||
var i: usize = 0;
|
||||
var j: usize = 0;
|
||||
|
||||
while (i < digits) {
|
||||
if (j < s.len) {
|
||||
result[@as(usize, digits) - i - 1] = s[s.len - j - 1];
|
||||
} else {
|
||||
result[@as(usize, digits) - i - 1] = '0';
|
||||
}
|
||||
|
||||
j += 1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const ArgumentError = error{ InvalidOtpAuthUrl, UnknownParameter, MissingConfigLocation, AuthenticatorNotFound };
|
||||
|
||||
fn parseArgs(allocator: Allocator, args: []const []const u8) !Args {
|
||||
@@ -169,7 +215,7 @@ fn configLocation(allocator: Allocator) ![]const u8 {
|
||||
|
||||
if (xdg_config_home) |base| {
|
||||
const config_location = try std.mem.concat(allocator, u8, &[_][]const u8{ base, "/zig-totp" });
|
||||
debug("config_location: {s}", .{config_location});
|
||||
//debug("config_location: {s}", .{config_location});
|
||||
return config_location;
|
||||
}
|
||||
|
||||
@@ -177,7 +223,7 @@ fn configLocation(allocator: Allocator) ![]const u8 {
|
||||
const base = home orelse unreachable;
|
||||
const config_location = try std.mem.concat(allocator, u8, &[_][]const u8{ base, "/.zig-totp" });
|
||||
|
||||
debug("config_location: {s}", .{config_location});
|
||||
//debug("config_location: {s}", .{config_location});
|
||||
return config_location;
|
||||
}
|
||||
|
||||
@@ -262,3 +308,29 @@ test "parse oth pauth url" {
|
||||
try std.testing.expectEqual(31, actual.period);
|
||||
try std.testing.expectEqual(7, actual.digits);
|
||||
}
|
||||
|
||||
test "zero padding" {
|
||||
const actual = try zeroPad(std.testing.allocator, 6, 123);
|
||||
defer std.testing.allocator.free(actual);
|
||||
|
||||
try std.testing.expectEqualStrings("000123", actual);
|
||||
}
|
||||
|
||||
test "authenticator generate code with 6 digits" {
|
||||
const authenticator = Authenticator{ .url = OtpAuthUrl{ .name = "TestAuthenticator", .secretEncoded = "HJ5PX4IFQKD37HFNXLJYBAVD5G6NMMUOKUCDXJ4XTLUUBWSXRXEN5SR4MLAZA5M2", .url = "not needed", .period = 30, .digits = 6 } };
|
||||
|
||||
const code = try authenticator.code(std.testing.allocator, _closure_return_1725695340);
|
||||
defer std.testing.allocator.free(code);
|
||||
try std.testing.expectEqualStrings("218139", code);
|
||||
}
|
||||
test "authenticator generate code with 10 digits and zero padding" {
|
||||
const authenticator = Authenticator{ .url = OtpAuthUrl{ .name = "TestAuthenticator", .secretEncoded = "HJ5PX4IFQKD37HFNXLJYBAVD5G6NMMUOKUCDXJ4XTLUUBWSXRXEN5SR4MLAZA5M2", .url = "not needed", .period = 60, .digits = 10 } };
|
||||
|
||||
const code = try authenticator.code(std.testing.allocator, _closure_return_1725695340);
|
||||
defer std.testing.allocator.free(code);
|
||||
try std.testing.expectEqualStrings("0844221464", code);
|
||||
}
|
||||
|
||||
fn _closure_return_1725695340() i64 {
|
||||
return 1725695340;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user