gauthenticator/src/otp.vala

73 lines
2.4 KiB
Vala
Raw Normal View History

2017-03-29 10:33:01 +00:00
namespace OTP {
abstract class Base : Object {
private GLib.Bytes _secret;
public string secret {
get {
// TODO: Terminate it with a zero!
return (string)_secret.get_data();
}
set {
_secret = Base32.b32decode(new GLib.Bytes(value.data), true, 0);
}
}
public int digits {get; set; default = 6;}
public GLib.ChecksumType digest {get; set; default = GLib.ChecksumType.SHA1;}
public Base(string secret_key, int digits, GLib.ChecksumType digest) {
Object(secret: secret_key, digits: digits, digest: digest);
}
private static GLib.Bytes int_to_bytestring(uint64 val) {
var size = (int) sizeof(uint64);
var result = new uint8[size];
for (int i = size - 1; i >= 0; i--) {
result[i] = (uint8)(val & 0xFF);
val >>= 8;
}
return new GLib.Bytes(result);
}
public uint64 generate_otp(uint64 input) {
// TODO: Make this dynamic based on the digest
uint8[256] result = {0};
size_t result_len = 256;
var hasher = new GLib.Hmac(digest, _secret.get_data());
hasher.update(int_to_bytestring(input).get_data());
hasher.get_digest(result, ref result_len);
var offset = result[result_len - 1] & 0xf;
uint64 code = ((uint64)(result[offset] & 0x7f) << 24 |
(uint64)(result[offset + 1] & 0xff) << 16 |
(uint64)(result[offset + 2] & 0xff) << 8 |
(uint64)(result[offset + 3] & 0xff));
return Math.llrint(code % GLib.Math.pow(10, digits));
}
}
class TOTP : Base {
public int interval {get; set; default = 30;}
public TOTP(string secret_key, int digits, GLib.ChecksumType digest, int interval) {
Object(secret: secret_key, digits: digits, digest: digest);
}
public uint64 at(GLib.DateTime for_time, int counter_offset) {
return generate_otp(timecode(for_time) + counter_offset);
}
public uint64 now() {
return generate_otp(timecode(new GLib.DateTime.now_utc()));
}
public uint64 timecode(GLib.DateTime for_time) {
return Math.llrint(Math.ceil(for_time.to_unix() / interval));
}
}
}