{"record":{"id":"c88a8e6b8ec88e0f","repo":"schollz/croc","slug":"sender-pake-confirmation-failed","errorCode":null,"errorMessage":"Sender PAKE confirmation failed","messagePattern":"Sender PAKE confirmation failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/protocol/client.ts","lineNumber":713,"sourceCode":"      peerPake.b2,\n      PAKE_PURPOSE_TRANSFER,\n      room,\n      curve,\n      pake.bytes,\n      peerPake.b,\n    );\n    await sendControl(control, {\n      t: \"pake-confirm\",\n      v: PAKE_PROTOCOL_VERSION,\n      b: peerKeys.confirmationA,\n    });\n    const confirmationB = await receiveControl(control);\n    if (confirmationB.t !== \"pake-confirm\" || !confirmationB.b) {\n      throw new Error(\"Sender did not confirm the croc PAKE handshake\");\n    }\n    requirePakeVersion(confirmationB.v);\n    if (!(await wasm().confirmPeerKey(peerKeys.confirmationB, confirmationB.b))) {\n      throw new Error(\"Sender PAKE confirmation failed\");\n    }\n    key = peerKeys.key;\n    data = await openDataConnections(\n      settings,\n      room,\n      dataPorts(relay.banner),\n      signal,\n    );\n    await sendControl(control, {\n      t: \"externalip\",\n      m: relay.externalIP,\n      b: peerPake.b,\n    }, key);\n    const peerIP = await receiveControl(control, key);\n    if (peerIP.t !== \"externalip\") throw new Error(\"Sender did not secure the channel\");\n\n    const fileInfo = await receiveControl(control, key);\n    if (fileInfo.t === \"error\") throw new Error(fileInfo.m || \"Sender cancelled\");","sourceCodeStart":695,"sourceCodeEnd":731,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/protocol/client.ts#L695-L731","documentation":"Thrown by the recipient when the sender's pake-confirm token fails wasm confirmPeerKey — the cryptographic proof that both parties derived the same shared key did not verify. This means the negotiated keys differ, almost always because the PAKE inputs (passphrase, room, purpose, curve, identities) were not identical on both sides. The channel is deliberately not established.","triggerScenarios":"Different passphrases derived from slightly different code phrases; room string differs (e.g. code phrase parsed differently by peers); PAKE purpose ('peer-transfer') or curve ('p256') mismatch between implementations; corrupted confirmation bytes in transit; wasm engine version skew changing key derivation.","commonSituations":"Users mistype or variant-type the code phrase (trailing spaces, unicode look-alikes); one peer uses a croc build whose codeComponents parses rooms differently; forked clients changing the PAKE purpose string; bit corruption on the relay path (rare since encryption would usually fail earlier).","solutions":["Regenerate and re-share the code phrase; both peers must enter the exact same string (validateSecret enforces printable ASCII >=6 chars, but equality is on the users)","Verify codeComponents output (room + passphrase) is identical on both peers — log the room hash, not the secret","Ensure PAKE_PURPOSE_TRANSFER and curve p256 are unchanged in any fork; both are hard-coded inputs to derivation","If it persists with identical phrases, suspect wasm build skew and rebuild/upgrade both clients"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-flight both sides derive identical components before connecting\nconst { room, passphrase } = await wasm().codeComponents(secret);\n// compare a hash of room+passphrase out-of-band (never send the secret itself)\nconst digest = await wasm().hashFinal(await wasm().hashInit()); // illustrative\n","typeGuard":null,"tryCatchPattern":"catch (e) {\n  if (e instanceof Error && e.message === \"Sender PAKE confirmation failed\") {\n    // keys diverged: almost always a code-phrase mismatch; re-share the code and retry\n  }\n  throw e;\n}","preventionTips":["Use copy-paste for code phrases; never retype unicode-prone strings","Confirm both peers run identical client builds so PAKE purpose/curve/identity inputs match","Treat confirmation failure as a credential problem first, a crypto bug only after identical phrases still fail"],"tags":["pake","handshake","security","key-derivation"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}