@me2em/crypto
    Preparing search index...

    Function completeX3DH

    • RECIPIENT side of X3DH: completes the same computation.

      DH1 = DH(SPK_B_priv, IK_A_pub) // recipient SPK × initiator identity DH2 = DH(IK_B_priv, EK_A_pub) // recipient identity × initiator ephemeral DH3 = DH(SPK_B_priv, EK_A_pub) // recipient SPK × initiator ephemeral DH4 = DH(OTK_B_priv, EK_A_pub) // recipient OTK × initiator ephemeral

      → HKDF of the same 4 secrets in the same order → same 32B.

      ourOneTimePreKey is REQUIRED — the initiator MUST consume an OTK (zero-fallback prohibited on both sides). Passing undefined would produce a different shared secret, breaking the protocol.

      ⚠️ OTK CONSUMED: this function wipes ourOneTimePreKey.keyPair.privateKey IN PLACE after the DH that uses it (session-bound ephemeral-key model). Reusing the wiped buffer derives a corrupted secret SILENTLY — no error is thrown. Pass a copy if retention is needed. The identity and SPK private keys are NOT touched (they are reused across sessions).

      Parameters

      • ourIdentity: IdentityKeys

        Recipient identity (private needed).

      • ourSignedPreKey: SignedPreKey

        Recipient's SPK (private needed).

      • ourOneTimePreKey: OneTimePreKey

        The consumed OTK (private needed). Required.

      • theirIdentityPublicKey: Uint8Array

        Initiator identity public (32B Ed25519).

      • theirEphemeralPublicKey: Uint8Array

        From the first message (32B X25519).

      Returns Promise<Uint8Array>

      The same 32-byte shared secret as the initiator computed. The shared secret is the CALLER's responsibility — wipe it after use (see unwrapKeyForMe for the pattern).

      'INVALID_ARGUMENT' if ourOneTimePreKey is undefined.

      Memory hygiene: every DH intermediate, the IKM and the derived Montgomery copy of the identity key are wiped on ALL exit paths (success or throw); the OTK private key is wiped only after its DH actually ran — an unconsumed OTK stays usable.

      const shared = await completeX3DH(
      bobIdentity,
      bobSPK,
      bobOTK,
      aliceIdentityPub,
      message.ephemeralPublicKey
      );