feat(keys): the password's own proof key (ADR 0020, D19)
A third subkey of the password's Argon2id (context `hlkpass1`, subkey 3) seeds an Ed25519 pair: `PasswordKeys.proofKeyPair` and `proofPublicKey`. An account that keeps no SRP verifier proves its password by signing the register's nonce with it, as it proves the recovery phrase; neither SRP's verifier nor the unwrapped account keys a signed-in device keeps can make it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
b4c93e03de
commit
e8953d1560
1 changed files with 114 additions and 10 deletions
|
|
@ -11,7 +11,7 @@ builds or tests a package depending on this one needs a C compiler and `make`.
|
|||
|
||||
| Piece | API | Parameters |
|
||||
|---|---|---|
|
||||
| The password's keys | `newPasswordSalt`, `derivePasswordKeys` → `PasswordKeys` (`srpSecret`, `srpPrivateValue`, `keyEncryptionKey`) | Argon2id v1.3, 64 MiB, 3 passes (`PasswordKdf.current`), a 16-byte salt, a 32-byte master key; libsodium's KDF (context `hlkpass1`) splits it into SRP's secret (subkey 1, whose 256 bits big-endian are SRP's x) and the key-encryption key (subkey 2) |
|
||||
| The password's keys | `newPasswordSalt`, `derivePasswordKeys` → `PasswordKeys` (`srpSecret`, `srpPrivateValue`, `keyEncryptionKey`, `proofKeyPair`, `proofPublicKey`) | Argon2id v1.3, 64 MiB, 3 passes (`PasswordKdf.current`), a 16-byte salt, a 32-byte master key; libsodium's KDF (context `hlkpass1`) splits it into SRP's secret (subkey 1, whose 256 bits big-endian are SRP's x), the key-encryption key (subkey 2) and the seed of the password's Ed25519 proof key (subkey 3, Halkyon Learn's ADR 0020, D19), with which an account that keeps no SRP verifier proves the password |
|
||||
| The password as typed | `normalizePassword` (`PasswordRefused`) | RFC 8265's OpaqueString: other spaces to U+0020, Unicode NFC (`unorm_dart`, pinned exactly), empty or control characters refused; `derivePasswordKeys` applies it, so composed and decomposed accents unlock alike |
|
||||
| The costs kept with an account | `PasswordKdf` (`toJson`, `fromJson`) | kept beside the salt, so they can be raised for new passwords; `fromJson` refuses another algorithm or costs below `PasswordKdf.minimum`, kept apart from the current costs so raising those never locks out an older account |
|
||||
| The account's key pairs | `AccountKeys.generate` | X25519 to encrypt to, Ed25519 to sign with |
|
||||
|
|
@ -23,7 +23,7 @@ builds or tests a package depending on this one needs a C compiler and `make`.
|
|||
| Refusals | `KeyRefused` with a `KeyRefusal` | `wrongKeyOrTampered`, `notTheAccountsKeys`, `wrongDataKey`, `forgedSignature`, `malformed` (bytes from elsewhere of another length, checked before libsodium sees them), `weakPasswordKdf`, `excessivePasswordKdf`, `phraseLength`, `phraseWord`, `phraseChecksum` |
|
||||
|
||||
Secrets stay in libsodium's guarded memory (`SecureKey`) where its API takes one:
|
||||
the master key, both password subkeys, the private halves of the key pairs, the
|
||||
the master key, the three password subkeys, the private halves of the key pairs, the
|
||||
recovery key, the phrase's proof key and data keys. `dispose` wipes and frees a
|
||||
holder's keys, which may not be read afterwards.
|
||||
|
||||
|
|
@ -45,9 +45,10 @@ What leaves guarded memory, exactly:
|
|||
|
||||
## What never leaves the device
|
||||
|
||||
The password, the master key, SRP's secret and x, the key-encryption key, the recovery
|
||||
phrase and its key, the private halves of the key pairs, data keys and content in the
|
||||
clear. What the register keeps is the salt and the costs, both public keys, the private
|
||||
The password, the master key, SRP's secret and x, the key-encryption key, the private
|
||||
half of the password's proof key, the recovery phrase and its key, the private halves of the key pairs, data keys and content in the
|
||||
clear. What the register keeps is the salt and the costs, both public keys, the public half of
|
||||
the password's proof key (for an account without SRP's verifier), the private
|
||||
keys wrapped twice (under the key-encryption key and under the recovery key), data keys
|
||||
sealed to the account, and sealed content: none of it opens without the password or the
|
||||
phrase.
|
||||
|
|
|
|||
Loading…
Reference in a new issue