Implement pass-otp compatible TOTP and HOTP

This commit is contained in:
Hermes Agent
2026-08-10 01:22:53 +00:00
parent b8c614e141
commit 4bd39b1ef2
12 changed files with 2382 additions and 28 deletions

50
docs/otp.md Normal file
View File

@@ -0,0 +1,50 @@
# Pass-OTP compatibility
`crates/storage` owns OTP key URI parsing, code generation, entry discovery and
mutation, HOTP counter updates, and presentation payloads. The CLI only reads
input, asks for typed confirmations, invokes the Rust service, and presents the
returned zeroizing bytes. Runtime code never launches `pass`, `gpg`,
`oathtool`, `otptool`, `qrencode`, or a shell.
## Key URIs and entry layout
IronStorage reads and writes the standard `otpauth://totp/...` and
`otpauth://hotp/...` lines used by `pass-otp`. URI validation covers a Base32
secret, decoded issuer and account label, SHA-1/SHA-256/SHA-512 algorithm,
six- or eight-digit output, positive TOTP period, and required HOTP counter.
Defaults are SHA-1, six digits, and a 30-second TOTP period. Duplicate known
parameters, mismatched label/query issuers, invalid type-specific parameters,
and malformed percent or Base32 encoding are typed failures.
An explicitly supplied URI is preserved byte-for-byte. `otp insert --secret`
constructs the same default TOTP URI shape as upstream and percent-encodes its
issuer and account. Without an explicit entry path, the decoded issuer and
account produce `issuer/account`; an account without an issuer produces the
account path. The frontend must confirm that derived path before storage
changes anything.
Within a multiline password entry, the URI must begin at the start of its own
line. `otp append` adds a line when none exists or replaces the existing line
while preserving every other byte. Multiple URI lines are rejected as
ambiguous instead of guessing which token to use. Validation, confirmation,
recipient resolution, encryption, the atomic repository write, and the
embedded Git commit form one storage-owned operation with rollback on commit
failure.
## Codes and counters
HOTP implements RFC 4226 dynamic truncation. TOTP implements RFC 6238 by using
the selected Unix-time step as the HOTP counter. The HMAC digest and formatted
code are zeroized after use, and tests cover the published SHA-1, SHA-256, and
SHA-512 vectors, alternate periods, six/eight digits, and counters.
For `pass-otp` compatibility, a stored HOTP counter records the last-used
counter. Code generation checks for concurrent entry changes, increments the
counter, rewrites only its URI value, atomically encrypts the updated entry,
and commits `Increment HOTP counter for <entry>.` before returning the code. A
validation, encryption, concurrency, or Git failure therefore never exposes a
code whose counter update was not committed.
OTP codes support terminal or secret-safe clipboard presentation. URI output
supports terminal, clipboard, and the shared storage-owned QR matrix renderer.
Clipboard and QR requests never print the underlying code or URI as plaintext.