Initiator's Ed25519 identity key pair.
Recipient's PreKeyBundle (MUST be verified via verifySignedPreKey BEFORE calling this function).
X3DHInitiationResult with shared secret and ephemeral pubkey.
The shared secret is the CALLER's responsibility — wipe it after
use (see wrapKeyForRecipient for the pattern).
'VERIFICATION_FAILED' if SPK signature invalid, 'INVALID_ARGUMENT' if bundle lacks OTK.
Memory hygiene: every DH intermediate, the IKM, the derived Montgomery copy of the identity key and the ephemeral private key are wiped on ALL exit paths (success or throw) — a failed DH against a hostile bundle must not leak live key bytes.
INITIATOR side of X3DH.
Establishes a shared secret with an OFFLINE recipient using their published PreKeyBundle. The recipient completes the same computation later using their private pre-keys.
CANONICAL DH ORDER (Signal spec §3.3) — DO NOT REORDER
DH1 = DH(IK_A, SPK_B) // initiator identity × recipient signed prekey DH2 = DH(EK_A, IK_B) // ephemeral × recipient identity DH3 = DH(EK_A, SPK_B) // ephemeral × recipient signed prekey DH4 = DH(EK_A, OPK_B) // ephemeral × recipient one-time prekey
All keys are converted Ed25519→X25519 (Montgomery) where needed. DH4 uses the recipient's OPK public key with the initiator's EPHEMERAL private key — zero-fallback is not allowed (enforces forward secrecy).
SK = HKDF(DH1 || DH2 || DH3 || DH4, salt=zero, info=X3DH_INFO)