Move OPAQUE into Iota and harden password authentication
Some checks failed
Validate authentication / Validate authentication (push) Failing after 1s

This commit is contained in:
Alex-Emmet 2026-10-02 21:29:04 +02:00
commit fdba718306
No known key found for this signature in database
27 changed files with 1177 additions and 134 deletions

View file

@ -0,0 +1,37 @@
# Credential envelope version 1
The plaintext is arbitrary canonical credential bytes. Serialization of `.tu` credentials belongs to the consumer.
## Key derivation
Input secret is the OPAQUE client export key. Use HKDF-SHA-256 with absent salt and 32-byte output. HKDF info concatenates:
1. ASCII `tensamin:opaque-export-key:credential:v1\0`.
2. OPAQUE profile ID `1` as signed i64 big-endian.
3. Credential version `1` as unsigned u16 big-endian.
## Associated data
Concatenate:
1. ASCII `tensamin:opaque-credential-aad:v1\0`.
2. OPAQUE profile ID `1` as signed i64 big-endian.
3. Principal UTF-8 byte length as unsigned u32 big-endian.
4. Principal UTF-8 bytes.
5. Signed i64 Iota ID big-endian.
6. The 32-byte SHA-256 digest of the canonical account public key bundle.
The provisioning session UUID is not credential AAD. Enrollment ciphertext must decrypt in later provisioning sessions.
## Envelope bytes
| Offset | Length | Value |
| --- | --- | --- |
| 0 | 8 | `TSCRED\0\0` |
| 8 | 2 | Version `1`, unsigned big-endian |
| 10 | 24 | Fresh random XChaCha20 nonce |
| 34 | Remaining | XChaCha20-Poly1305 ciphertext followed by its 16-byte tag |
Minimum version-1 envelope length is 50 bytes, including an empty plaintext. The parser rejects wrong magic and incomplete framing, reports unknown versions explicitly, and authenticates before returning plaintext. It never guesses a format or falls back to version 1. The fixed magic and version are enforced by the parser; all identity AAD and nonce bytes affect authentication.
Derived key buffers and decrypted plaintext zeroize on drop. Iota stores and returns only the envelope, never the export key or plaintext.

View file

@ -0,0 +1,32 @@
# Tensamin OPAQUE profile 1
Profile ID is signed i64 `1`. The implementation is pinned to `opaque-ke 4.0.1`.
The OPRF group is Ristretto255. The authenticated key exchange is TripleDH over Ristretto255 with SHA-512.
The key stretching function is Argon2id, version `0x13`, with `m_cost = 19456` KiB, `t_cost = 2`, `p_cost = 1`. It uses the deployed deterministic 16-byte zero salt. The output length equals the input length. A known-answer vector is in `tests/vectors/profile_v1.json`.
## Identifiers and login context
The server identifier is ASCII `tensamin:iota-password\0` followed by the signed i64 Iota ID in big-endian order. The client identifier and credential identifier are the UTF-8 Omega principal, normally `{omega-authority}#{user_id}`. Callers supply the canonical principal without normalization by this library.
Login context concatenates these bytes:
1. ASCII `tensamin:password-provisioning\0`.
2. Principal UTF-8 byte length as unsigned u32 big-endian.
3. Principal UTF-8 bytes.
4. Signed i64 Iota ID big-endian.
5. The 16 raw provisioning UUID bytes.
6. A 32-byte SHA-256 digest of canonical MTP PublicKeyBundle bytes.
The caller computes the fingerprint. The library has no MTP dependency. Both client and server construct this context from `PasswordLoginBindingV1`. Pending server state also retains the initial context and rejects a changed finish binding.
Any change to these persistent values requires a new profile ID.
## Persistence and secrets
Setup files and registration records use opaque-ke's profile-1 serialization directly, with no new framing. Iota owns setup file location and lifecycle, record persistence, account discovery, throttling, and protected transport. Missing setup with enrolled accounts must remain an Iota error.
Client registration and login states are consumed by finish. Password buffers and native server exchange state are zeroized on drop. OPAQUE export keys remain in `OpaqueExportKey` and credential operations borrow them without exporting their bytes. Servers discard the OPAQUE session key and never receive the export key.
The permanent vectors preserve the deployed server identifier, login context, and KSF bytes. There is no legacy implementation or fallback.