added totp
This commit is contained in:
parent
e191d82407
commit
e31daa191a
BIN
manual.pdf
BIN
manual.pdf
Binary file not shown.
61
src/misc.typ
61
src/misc.typ
@ -1,7 +1,8 @@
|
||||
#import "utils.typ": xor-bytes
|
||||
#import "sha.typ": sha1
|
||||
#import "md.typ": md4
|
||||
#import "utils.typ": utf8-to-utf16le
|
||||
#import "utils.typ": utf8-to-utf16le, z-fill
|
||||
#import "base.typ": b32-decode
|
||||
|
||||
#let _compute-block-sized-key(key, hash-func: sha1, block-size: 64) = {
|
||||
if key.len() > block-size {
|
||||
@ -15,6 +16,18 @@
|
||||
return key
|
||||
}
|
||||
|
||||
#let _extract31(value) = {
|
||||
let bytes = array(value)
|
||||
let i = bytes.last().bit-and(0xf)
|
||||
let selected-bytes = (
|
||||
bytes.at(i).bit-and(0x7f),
|
||||
bytes.at(i+1),
|
||||
bytes.at(i+2),
|
||||
bytes.at(i+3)
|
||||
)
|
||||
return selected-bytes.fold(0, (a, b) => a.bit-lshift(8).bit-or(b))
|
||||
}
|
||||
|
||||
/// Hash-based Message Authentication Code
|
||||
/// ```example
|
||||
/// #bytes-to-hex(hmac("Key", "Hello World!"))
|
||||
@ -56,4 +69,50 @@
|
||||
/// -> bytes
|
||||
#let ntlm(password) = {
|
||||
return md4(utf8-to-utf16le(password))
|
||||
}
|
||||
|
||||
/// Time-based One-Time Password
|
||||
/// ```example
|
||||
/// #let epoch = datetime(
|
||||
/// year: 1970, month: 1, day: 1,
|
||||
/// hour: 0, minute: 0, second: 0
|
||||
/// )
|
||||
/// #let date = datetime(
|
||||
/// year: 2025, month: 1, day: 4,
|
||||
/// hour: 12, minute: 53, second: 30
|
||||
/// )
|
||||
/// #totp(
|
||||
/// b32-encode(bytes("YOUPI")),
|
||||
/// (date - epoch).seconds()
|
||||
/// )
|
||||
/// ```
|
||||
/// -> str
|
||||
#let totp(
|
||||
/// Secret key. Either bytes or a base32-encode value
|
||||
/// -> str | bytes
|
||||
secret,
|
||||
/// Current time (seconds since t0)
|
||||
/// -> int
|
||||
time,
|
||||
/// Time origin
|
||||
/// -> int
|
||||
t0: 0,
|
||||
/// Code duration
|
||||
/// -> int
|
||||
period: 30,
|
||||
/// Code length
|
||||
/// -> int
|
||||
digits: 6
|
||||
) = {
|
||||
let secret = if type(secret) == str {b32-decode(secret)} else {secret}
|
||||
assert(type(secret) == bytes, message: "secret must be a string or bytes, but is " + repr(type(secret)))
|
||||
|
||||
let count = int(calc.div-euclid(time - t0, period))
|
||||
let count-bytes = count.to-bytes(endian: "big", size: 8)
|
||||
|
||||
let digest = hmac(secret, count-bytes)
|
||||
let hotp = _extract31(digest)
|
||||
let code = calc.rem(hotp, calc.pow(10, digits))
|
||||
|
||||
return z-fill(str(code), digits)
|
||||
}
|
Loading…
Reference in New Issue
Block a user