schollz/croc · error · Error
Sender provided an invalid ${peerPake.b2.byteLength}-byte PA
Error message
Sender provided an invalid ${peerPake.b2.byteLength}-byte PAKE salt What it means
Thrown by the recipient when the sender's pake reply carries a b2 (salt) field whose byte length is not exactly 32 (PAKE_SALT_SIZE). The salt feeds derivePeerKeys, so a wrong-length salt means key derivation would diverge from the sender's; the recipient rejects it rather than derive mismatched keys. The message includes the offending length to aid diagnosis.
Source
Thrown at web/src/protocol/client.ts:690
textEncoder.encode(passphrase),
0,
curve,
PAKE_PURPOSE_TRANSFER,
room,
);
await sendControl(control, {
t: "pake",
v: PAKE_PROTOCOL_VERSION,
b: pake.bytes,
b2: textEncoder.encode(curve),
});
const peerPake = await receiveControl(control);
if (peerPake.t !== "pake" || !peerPake.b || !peerPake.b2) {
throw new Error("Sender did not complete the croc PAKE handshake");
}
requirePakeVersion(peerPake.v);
if (peerPake.b2.byteLength !== PAKE_SALT_SIZE) {
throw new Error(`Sender provided an invalid ${peerPake.b2.byteLength}-byte PAKE salt`);
}
const finished = await wasm().pakeUpdate(pake.handle, peerPake.b);
const peerKeys = await wasm().derivePeerKeys(
finished.key,
peerPake.b2,
PAKE_PURPOSE_TRANSFER,
room,
curve,
pake.bytes,
peerPake.b,
);
await sendControl(control, {
t: "pake-confirm",
v: PAKE_PROTOCOL_VERSION,
b: peerKeys.confirmationA,
});
const confirmationB = await receiveControl(control);
if (confirmationB.t !== "pake-confirm" || !confirmationB.b) {View on GitHub (pinned to e25f1bdc04)
Solutions
- Align both peers on PAKE_PROTOCOL_VERSION 2 semantics: sender replies with b=PAKE bytes, b2=32-byte random salt
- Upgrade both sides so salt size constants match (PAKE_SALT_SIZE = 32)
- When writing a sender implementation, do not mirror the recipient's pake message layout — the b2 field has a different meaning in each direction
- Check requirePakeVersion passes first; an old-version peer may slip through with a different salt format if version negotiation is bypassed
Defensive patterns
Strategy: validation
Validate before calling
// Sender-side pre-flight: generate the salt with the agreed size before replying
const PAKE_SALT_SIZE = 32;
const salt = randomBytes(PAKE_SALT_SIZE);
if (salt.byteLength !== PAKE_SALT_SIZE) throw new Error("salt generation mismatch"); Type guard
function isValidPakeSalt(b2: Uint8Array | undefined): b2 is Uint8Array {
return !!b2 && b2.byteLength === 32;
} Try / catch
catch (e) {
if (e instanceof Error && /-byte PAKE salt$/.test(e.message)) {
// peer sent a wrong-shaped pake reply (role confusion or version skew): upgrade both peers
}
throw e;
} Prevention
- Remember b2 means curve name in the recipient's initial pake but salt in the sender's reply — do not mirror messages
- Pin both peers to the same PAKE_PROTOCOL_VERSION and salt size
- Fuzz-test your sender against the recipient's checks (type, b, b2 length, version) before shipping
When it happens
Trigger: Peer sends a pake message where b2 is the curve name text (as the recipient's own initial message uses b2 for the curve) instead of the salt — a role confusion in a custom sender; truncated or garbage b2 from an incompatible client; salt generated with a different size constant in a fork.
Common situations: Interoperating with the croc CLI or a fork where b2 semantics differ per direction; version skew after PAKE_PROTOCOL_VERSION changed salt handling; a hand-rolled sender that copies the recipient's message shape (v, b, b2=curve) instead of replying with (v, b, b2=salt).
Related errors
- Recipient PAKE confirmation failed
- Sender did not complete the croc PAKE handshake
- Sender did not confirm the croc PAKE handshake
- Sender PAKE confirmation failed
- Peer uses unsupported PAKE protocol version ${version ?? 0};
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/1dd285ab660ac531.
Report an issue: GitHub.