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 "utils.typ": xor-bytes
|
||||||
#import "sha.typ": sha1
|
#import "sha.typ": sha1
|
||||||
#import "md.typ": md4
|
#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) = {
|
#let _compute-block-sized-key(key, hash-func: sha1, block-size: 64) = {
|
||||||
if key.len() > block-size {
|
if key.len() > block-size {
|
||||||
@ -15,6 +16,18 @@
|
|||||||
return key
|
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
|
/// Hash-based Message Authentication Code
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #bytes-to-hex(hmac("Key", "Hello World!"))
|
/// #bytes-to-hex(hmac("Key", "Hello World!"))
|
||||||
@ -57,3 +70,49 @@
|
|||||||
#let ntlm(password) = {
|
#let ntlm(password) = {
|
||||||
return md4(utf8-to-utf16le(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