Skip to content

fix(e2ee): try expired subkeys when decrypting historical messages#426

Merged
mremond merged 2 commits into
mainfrom
mr/elastic-herschel-708a0d
Jun 1, 2026
Merged

fix(e2ee): try expired subkeys when decrypting historical messages#426
mremond merged 2 commits into
mainfrom
mr/elastic-herschel-708a0d

Conversation

@mremond
Copy link
Copy Markdown
Member

@mremond mremond commented Jun 1, 2026

Summary

  • The Sequoia DecryptionHelper only tried encryption subkeys passing the current-time policy filter. After a subkey rotation, the retired subkey is expired and Sequoia skipped it — making MAM-replayed messages encrypted to the old subkey permanently undecryptable.
  • Adds a second pass that iterates over ALL secret subkeys regardless of policy/expiry. The first pass (policy-filtered) still runs for the common case; the fallback only fires when no current subkey matched.
  • The existing test ciphertext_encrypted_to_old_subkey_still_decrypts_after_rotation did not catch this because it ran within the 1-second validity window of the expired binding. A new test sleeps past that window to exercise the fallback path.

mremond added 2 commits June 1, 2026 10:47
eslint-plugin-react-hooks@7.1.1 hard-imports `zod-validation-error/v4`, a
subpath that only exists in zod-validation-error v4. The root package.json
already pins `zod-validation-error: 4.0.2` via overrides (added during the
React 19 migration, e3dfe4c), but the lockfile was never regenerated — so
3.5.4 stayed resolved, and 3.5.4 only exports `.`, making `npm run lint`
crash with ERR_PACKAGE_PATH_NOT_EXPORTED in the @xmpp/fluux workspace.

Pin only the zod-validation-error lockfile entry to 4.0.2 (its peer
`zod: ^3.25.0 || ^4.0.0` is satisfied by the project's zod 3.25.76). No
other dependency resolutions change.
The DecryptionHelper only tried encryption subkeys that pass the
current-time policy filter. After a subkey rotation the retired subkey
is expired, so Sequoia skipped it — making MAM-replayed messages
encrypted to the old subkey permanently undecryptable.

Add a second pass that iterates over ALL secret subkeys regardless of
policy/expiry. The first pass (policy-filtered) still runs for the
common case; the fallback only fires when no current subkey matched.

The existing test `ciphertext_encrypted_to_old_subkey_still_decrypts_after_rotation`
did not catch this because it ran within the 1-second validity window.
A new test sleeps past that window to exercise the fallback path.
@mremond mremond force-pushed the mr/elastic-herschel-708a0d branch from f348129 to 5d8af90 Compare June 1, 2026 08:49
@mremond mremond merged commit 796ed87 into main Jun 1, 2026
2 checks passed
@mremond mremond deleted the mr/elastic-herschel-708a0d branch June 1, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant