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

  1. Align both peers on PAKE_PROTOCOL_VERSION 2 semantics: sender replies with b=PAKE bytes, b2=32-byte random salt
  2. Upgrade both sides so salt size constants match (PAKE_SALT_SIZE = 32)
  3. When writing a sender implementation, do not mirror the recipient's pake message layout — the b2 field has a different meaning in each direction
  4. 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

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


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/1dd285ab660ac531. Report an issue: GitHub.