///|
/// One-time recovery (backup) codes.
///
/// Recovery codes are issued alongside TOTP enrollment so users can regain
/// access when their authenticator device is unavailable. Each code is used
/// at most once.
// Unambiguous alphabet: no I/L/O/0/1 to avoid confusion (30 symbols).
let recovery_alphabet : FixedArray[Byte] = [
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'J', b'K', b'M', b'N', b'P', b'Q',
b'R', b'S', b'T', b'V', b'W', b'X', b'Y', b'Z', b'2', b'3', b'4', b'5', b'6', b'7',
b'8', b'9',
]
///|
fn random_recovery_code(
groups : Int,
group_size : Int,
) -> String raise OtpError {
let total = groups * group_size
let random = match @env.rand(total) {
Some(bytes) if bytes.length() == total => bytes
_ => raise RandomUnavailable
}
let out = Buffer()
for g in 0.. 0 {
out.write_byte(b'-')
}
for i in 0.. Array[String] raise OtpError {
if count < 1 || groups < 1 || group_size < 1 {
raise InvalidUri("count, groups and group_size must be positive")
}
let codes = []
let mut made = 0
while made < count {
let code = random_recovery_code(groups, group_size)
if !codes.contains(code) {
codes.push(code)
made += 1
}
}
codes
}