67 lines
3.8 KiB
Markdown
67 lines
3.8 KiB
Markdown
# 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.
|
|
|
|
Every generated code carries an `OtpCodeValidity` value from `crates/storage`.
|
|
`Timed { valid_until }` identifies the exclusive Unix-time boundary for TOTP;
|
|
frontends call its `remaining_at` method to present a countdown and request a
|
|
replacement at zero. `CounterBased { counter }` identifies the HOTP counter
|
|
whose increment was committed and must be described as counter-based rather
|
|
than time-limited. Frontends must not recover periods from OTP URIs, decrement
|
|
an assumed interval, or infer the kind from formatted text. This same contract
|
|
is intended for the terminal, desktop, Apple, AutoFill, and watch interfaces.
|
|
|
|
The CLI preserves code-only standard output for pass-compatible pipelines and
|
|
reports the non-secret validity description on standard error. Clipboard
|
|
lifecycle feedback remains separate, and clipboard-only presentation does not
|
|
echo the code. The TUI observes the system clock during its normal repaint loop,
|
|
asks the storage validity value for the remaining seconds, and refreshes at the
|
|
exact boundary without treating repainting as user activity.
|