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.