73 lines
2.4 KiB
Vala
73 lines
2.4 KiB
Vala
|
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));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|