Expand description
RFC 6238 time-based one-time passwords.
§Two callers, one generator
Phase 19 added this module for sub-user login: UnENVerse checking a code its own user typed. Phase 22 added the mirror image — a TOTP seed the vault stores on behalf of a third party, the way Bitwarden and 1Password hold an authenticator entry, where UnENVerse produces the code and a website checks it.
They share every line of arithmetic and deliberately nothing else. The login
path is fixed at SHA-1/6 digits/30 seconds because that is what this product
mints and there is no interoperability question; the stored-seed path is
parameterised (Params) because the issuer chose those numbers years ago
and a generator that cannot follow is a generator that produces confidently
wrong codes. Only the login path has an anti-replay mark — see verify —
because only the login path is a verifier.
§Why this exists twice
Phase 5.1 shipped a TOTP implementation and Phase 7 removed it, because
nothing in the product ever reached it: there was no enrollment surface, no
CLI verb and no UI, so the only thing the code did was carry a schema column.
It comes back here with all three, and with the anti-replay rule that the
original had — see verify.
§Sub-users only, deliberately
The vault owner authenticates by deriving the SQLCipher key from the master password. There is no stored hash to check and therefore nothing for a second factor to gate: an attacker who can derive the key does not go through a login form, they open the file. Offering the owner a TOTP toggle would be a control that protects nothing while looking as though it protects everything.
§No new crates
hmac is already a dependency (entropy.rs builds HKDF on it), sha1 is
added for the one algorithm RFC 6238 pins for interoperability, and sha2
was already here for the KDF — which is the whole cost of supporting the
SHA-256 and SHA-512 variants a stored third-party seed may name. Base32 is
forty lines and lives here rather than behind a crate, for the same reason
HKDF does: the encoding is part of what a reader has to check.
Structs§
- Accepted
- The outcome of checking a code, carrying the step that was accepted.
- Live
Code - A code and how long it has left — the shape the desktop app and the CLI both render.
- Params
- The three numbers a stored seed is generated under.
- Stored
- A parsed seed: the base32 secret plus everything the URI said about it.
Enums§
- Algorithm
- The HMAC a stored seed names.
- Kind
- What a stored seed is, which decides what “the current code” means for it.
Constants§
- DIGITS
- Digits in a generated code. Six is what every authenticator app assumes when
the
otpauth://URI omits the parameter, and omitting it is what keeps the QR-less manual-entry path working. - MAX_
DIGITS - Most digits a code may carry.
- MAX_
PERIOD_ SECS - Longest step a stored seed may name, in seconds.
- MIN_
DIGITS - Fewest digits a code may carry. RFC 4226 sets six as the floor.
- SECRET_
BYTES - Bytes of secret material. RFC 4226 requires at least 128 bits and recommends 160 — which is also the SHA-1 block output, so nothing is truncated.
- SKEW_
STEPS - How many steps either side of the current one are accepted.
- STEAM_
ALPHABET - The alphabet Steam Guard draws its five characters from.
- STEAM_
DIGITS - How many characters a Steam code carries. Not configurable; Steam fixes it.
- STEP_
SECS - Seconds per counter step. RFC 6238’s recommended default.
Functions§
- base32_
decode - Decodes unpadded (or padded) RFC 4648 base32, case-insensitively.
- base32_
encode - Encodes bytes as unpadded RFC 4648 base32.
- code_at
- The code a stored seed produces at a given time.
- code_
for_ counter - The code for one explicit counter value.
- generate_
secret - Generates a fresh base32 secret from the OS CSPRNG.
- grouped
- Splits a secret into space-separated groups of four for manual entry.
- hotp
- HOTP (RFC 4226) for one counter value, SHA-1 and six digits.
- hotp_
with - HOTP (RFC 4226) for one counter value, with the algorithm and digit count the issuer chose.
- live_
code - The code a stored seed produces right now, with its countdown.
- live_
code_ with - The same, optionally carrying the code that comes next.
- next_
code_ at - The code that replaces the current one.
- normalize_
b32 - Normalises a base32 secret: uppercase, no spaces, dashes or padding.
- now_
unix - Current Unix time in seconds.
- otpauth_
uri - Builds the
otpauth://URI an authenticator scans or imports. - parse_
otpauth - Parses an
otpauth://totp/...URI. - parse_
seed - Reads either a bare base32 seed or a full
otpauth://URI. - remaining_
secs - Seconds until the current code is replaced.
- step_at
- The counter step for a Unix timestamp.
- totp_at
- TOTP for a base32 secret at a given time. Exposed so a caller can show the user the code their authenticator should be showing — used by nothing that authenticates, only by diagnostics.
- verify
- Verifies a code against a secret, refusing anything at or before
last_step.