///|
/// Runnable demo: HOTP/TOTP vectors and an otpauth enrollment round trip.
///
/// Run with: moon run examples/totp_demo
fn section(title : String) -> Unit {
println("")
println("== " + title + " ==")
}
///|
fn main raise {
// 1. HOTP RFC 4226 Appendix D.
section("HOTP (RFC 4226 Appendix D, key \"12345678901234567890\")")
let key = @utf8.encode("12345678901234567890")
for counter in 0..<10 {
println(
"counter \{counter}: " + @lib.hotp(@lib.Sha1, key, counter.to_uint64()),
)
}
// 2. TOTP RFC 6238 Appendix B at fixed timestamps.
section("TOTP (RFC 6238 Appendix B, 8 digits)")
let key256 = @utf8.encode("12345678901234567890123456789012")
let key512 = @utf8.encode(
"1234567890123456789012345678901234567890123456789012345678901234",
)
let timestamps : FixedArray[UInt64] = [
59UL, 1111111109UL, 1234567890UL, 2000000000UL,
]
for t in timestamps {
println(
"t=\{t} SHA1=" +
@lib.totp(@lib.Sha1, key, t, digits=8) +
" SHA256=" +
@lib.totp(@lib.Sha256, key256, t, digits=8) +
" SHA512=" +
@lib.totp(@lib.Sha512, key512, t, digits=8),
)
}
// 3. Enroll a new user: secret + otpauth URI, then parse it back.
section("Enrollment round trip")
let secret = @lib.generate_secret()
let otp : @lib.OtpAuth = {
issuer: "Acme Corp",
account: "alice@example.com",
secret,
algorithm: @lib.Sha256,
digits: 6,
period: 30,
otp_type: @lib.Totp,
counter: 0UL,
}
let uri = @lib.otpauth_uri(otp)
println("URI: " + uri)
let parsed = @lib.parse_otpauth_uri(uri)
println(
"parsed issuer=\{parsed.issuer} account=\{parsed.account} digits=\{parsed.digits}",
)
println("current code: " + @lib.totp_now(parsed.algorithm, parsed.secret))
}