docs(manual): batch 2 review — recovery phrase '256-word list' was wrong (it's 248)
The Recovery phrase flow said RecoveryKeyManager.generateRecoveryPhrase()
draws from a 256-word list. Verified the actual list size in
RecoveryKeyManager.kt: the hard-coded WORDLIST array has 248 entries
(python re.findall over the array literal), and the iOS wordlist file
iphone/Closer/Crypto/Resources/wordlist.txt is 247 lines (last word
'real' with no trailing newline, so 248 entries). The '256' is also
wrong in the inline comment in RecoveryKeyManager.kt - a pre-existing
comment bug from before the R24 iOS port (IOS_E2EE_STATUS.md notes the
iOS SPEC.md originally said 256 too, corrected in 922364f). The Android
side never got the same comment fix.
Manual now says 248 with the source-of-truth pointers, and computes
the entropy as 248^10 (a quick sanity check) so the next reader doesn't
trust the wrong number on either side.
Other Batch 2 claims verified clean:
- All 5 Android files in the 'Key Android files' list exist.
- All 2 Cloud Functions files in the 'Key Cloud Functions' list exist.
- Rate limit: 1h window, 10 max, 25h TTL on invite_attempts - all match
ACCEPT_RATE_LIMIT_WINDOW_MS / ACCEPT_RATE_LIMIT_MAX / ACCEPT_ATTEMPT_TTL_MS
in acceptInviteCallable.ts and the fieldOverrides entry in
firestore.indexes.json.
- Couples doc model fields (id, userIds, inviteCode, createdAt,
streakCount, lastAnsweredAt, currentQuestionId, activePackId,
encryptionVersion, wrappedCoupleKey, kdfSalt, kdfParams) all match
the create() payload in acceptInviteCallable.ts. createdAt uses
FieldValue.serverTimestamp() (manual says 'server-side' - correct).
- EncryptionVersion.STRICT = 2 in EncryptionVersion.kt.
This commit is contained in:
parent
f5194a4453
commit
1b36eba692
|
|
@ -350,7 +350,7 @@ This prevents brute-forcing the 6-character invite code space.
|
||||||
|
|
||||||
The recovery phrase is the only human-readable secret in the system. It is never sent to the server in plaintext.
|
The recovery phrase is the only human-readable secret in the system. It is never sent to the server in plaintext.
|
||||||
|
|
||||||
1. When an Android inviter creates a couple, `RecoveryKeyManager.generateRecoveryPhrase()` produces a 10-word phrase from a 256-word list. The phrase has roughly 80 bits of raw entropy; Argon2id makes brute-force infeasible.
|
1. When an Android inviter creates a couple, `RecoveryKeyManager.generateRecoveryPhrase()` produces a 10-word phrase from a 248-word list (the list is hard-coded as `WORDLIST` in `RecoveryKeyManager.kt`; the iOS `iphone/Closer/Crypto/Resources/wordlist.txt` is a verbatim copy with 248 entries). The phrase has roughly 80 bits of raw entropy (`248^10`); Argon2id makes brute-force infeasible.
|
||||||
2. The inviter encrypts the phrase with the invite code using `encryptPhraseWithCode` and stores the blob on the invite document.
|
2. The inviter encrypts the phrase with the invite code using `encryptPhraseWithCode` and stores the blob on the invite document.
|
||||||
3. The acceptor receives the encrypted blob, decrypts it with the same code, and stores the phrase locally.
|
3. The acceptor receives the encrypted blob, decrypts it with the same code, and stores the phrase locally.
|
||||||
4. The phrase is used to unwrap the couple keyset from `wrappedCoupleKey`.
|
4. The phrase is used to unwrap the couple keyset from `wrappedCoupleKey`.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue