Skip to content

fix(cryptography): derive HKDF from HMAC for an empty IKM - #11024

Closed
tylerc-consensys wants to merge 2 commits into
mainfrom
fix/cryptography-hkdf-empty-ikm
Closed

tylerc-consensys wants to merge 2 commits into
mainfrom
fix/cryptography-hkdf-empty-ikm

Conversation

@tylerc-consensys

@tylerc-consensys tylerc-consensys commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Explanation

The bug

hkdfSha256 / hkdfSha384 / hkdfSha512 import the IKM as a WebCrypto HKDF key. On React Native, react-native-quick-crypto rejects an empty HKDF key (HKDF derivation failed), even with unsafeInputKeyingMaterial: true. Browsers and Node accept it.

When it happens

Any HKDF call with an empty IKM on React Native. The Noise Split step always does this (the Noise spec defines Split as HKDF(ck, zerolen)), so every Noise handshake in @metamask/mfa-wallet-network fails on mobile. That blocks MPC key creation for the Money account (@metamask/eth-money-mpc-keyring + @metamask/mpc-react-native), which surfaces as Cl24Error: Rust panic.

The fix

When the IKM is empty, compute HKDF from WebCrypto HMAC using the Extract and Expand steps of RFC 5869 (an empty salt becomes HashLen zero bytes). Non-empty IKM still uses WebCrypto HKDF. The output is the same as WebCrypto HKDF for the same inputs. No new dependencies.

References

  • Ship after MetaMask/mfa-wallet-sdk#68. Until then, Split passes a chaining key that is zeroed while HKDF is running. This path reads it before it is zeroed, so keys would not match peers that read it after.

Testing

  • New tests: the empty-IKM path does not import a WebCrypto HKDF key; output matches Node crypto.hkdfSync for SHA-256/384/512, an empty salt, info, and multi-block lengths; the 255-block limit throws.
  • Verified on React Native (iOS and Android, simulator and physical devices): empty-IKM HKDF output matches the reference, and a two-party Noise handshake decrypts both ways.

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tylerc-consensys tylerc-consensys self-assigned this Oct 23, 2026
@matthiasgeihs

Copy link
Copy Markdown
Contributor

If this is fixed upstream, we can use react-native-quick-crypto with zero IKM.
margelo/react-native-quick-crypto#1080

Comment on lines +111 to +112
// Some WebCrypto implementations, such as react-native-quick-crypto, reject
// an empty HKDF key, so derive it from HMAC as RFC 5869 defines it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this seems like a bug in react-native-quick-crypto, where it doesn't conform to the spec and I have verified that our existing implementation works in both Node.js and the browser I think we should prefer upstreaming a fix. @matthiasgeihs has already opened a PR for this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related PR that bumps react-native-quick-crypto on mobile.
MetaMask/metamask-mobile#38279
Once that's in, we might be able to drop this one here.

ccharly added a commit to MetaMask/metamask-mobile that referenced this pull request Nov 6, 2026
Extracts the real MPC money keyring from feat/money-mfa-mpc-account
without the Money MFA UI or 2FA approval path:

- `buildRealMpcMoneyKeyringBuilder`: DKLS23/CL24 via
  `@metamask/mpc-react-native`, dev MPC service endpoints, pinned MFA
  whitelist config, placeholder backup key. Registered in
  `getKeyringBuilders` under `MM_MONEY_MPC_REAL_KEYRING` (needs
  `MM_MONEY_MPC_KEYRING_ENABLED`).
- `empty-ikm-hkdf-workaround`: lets `crypto.subtle` run the HKDF with
  an empty IKM that the Noise `Split` in `@metamask/mfa-wallet-network`
  needs and `react-native-quick-crypto` rejects. Temporary until
  MetaMask/core#11024 and MetaMask/mfa-wallet-sdk#68.
- Developer-options section that creates the MPC money account via
  `MoneyAccountController.createMoneyAccount({ type: 'mpc' })` and
  signs and recovers a test message.
- Adds `@metamask/mpc-react-native` and pins
  `uniffi-bindgen-react-native`; transpiles the ESM-only TSS packages
  in jest; maps `@metamask/eth-money-mpc-keyring/v2` types in tsconfig.

This branch was previously deployed

1 inactive deployment
default-branch — 719c6dbe Deployed Oct 23, 2026 by tylerc-consensys via Determine whether this PR is a release PR #5940
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.

3 participants