gchq/CyberChef · error · OperationError

Invalid nonce length: ${nonce.length} bytes. Salsa20 uses a

Error message

Invalid nonce length: ${nonce.length} bytes.

Salsa20 uses a nonce of 8 bytes (64 bits).

What it means

Thrown by Salsa20 when the nonce (decoded from its toggle) is not exactly 8 bytes, but only when the nonce type is not 'Integer' (the Integer branch builds an 8-byte little-endian nonce from the parsed number). Salsa20 uses a 64-bit nonce.

Source

Thrown at src/core/operations/Salsa20.mjs:91

        const key = Utils.convertToByteArray(args[0].string, args[0].option),
            nonceType = args[1].option,
            rounds = parseInt(args[3], 10),
            inputType = args[4],
            outputType = args[5];

        if (key.length !== 16 && key.length !== 32) {
            throw new OperationError(`Invalid key length: ${key.length} bytes.

Salsa20 uses a key of 16 or 32 bytes (128 or 256 bits).`);
        }

        let counter, nonce;
        if (nonceType === "Integer") {
            nonce = Utils.intToByteArray(parseInt(args[1].string, 10), 8, "little");
        } else {
            nonce = Utils.convertToByteArray(args[1].string, args[1].option);
            if (!(nonce.length === 8)) {
                throw new OperationError(`Invalid nonce length: ${nonce.length} bytes.

Salsa20 uses a nonce of 8 bytes (64 bits).`);
            }
        }
        counter = Utils.intToByteArray(args[2], 8, "little");

        const output = [];
        input = Utils.convertToByteArray(input, inputType);

        let counterAsInt = Utils.byteArrayToInt(counter, "little");
        for (let i = 0; i < input.length; i += 64) {
            counter = Utils.intToByteArray(counterAsInt, 8, "little");
            const stream = salsa20Block(key, nonce, counter, rounds);
            for (let j = 0; j < 64 && i + j < input.length; j++) {
                output.push(input[i + j] ^ stream[j]);
            }
            counterAsInt++;
        }

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Provide an 8-byte nonce, or switch the nonce toggle to 'Integer' and supply a numeric value (auto-converted to 8 bytes little-endian).
  2. For hex nonces use the 'Hex' toggle so 16 chars decode to 8 bytes.
  3. Do not reuse ChaCha20 nonces; Salsa20 requires exactly 64 bits.

Example fix

// before: 16 hex chars as UTF8 -> 16 bytes
salsa20.run(pt, [keyArg, {string:"0011223344556677", option:"UTF8"}, ...])
// after
salsa20.run(pt, [keyArg, {string:"0011223344556677", option:"Hex"}, ...])
Defensive patterns

Strategy: validation

Validate before calling

let nonce;
if (nonceType === "Integer") {
  nonce = Utils.intToByteArray(parseInt(nonceArg.string, 10), 8, "little");
} else {
  nonce = Utils.convertToByteArray(nonceArg.string, nonceArg.option);
  if (nonce.length !== 8) {
    throw new Error(`Salsa20 nonce must be 8 bytes, got ${nonce.length}`);
  }
}

Type guard

function isSalsa20NonceValid(nonceArg) {
  if (nonceArg.option === "Integer") return true;
  return Utils.convertToByteArray(nonceArg.string, nonceArg.option).length === 8;
}

Prevention

When it happens

Trigger: Nonce toggle set to Hex/UTF8/Latin1/Base64 with a decoded length other than 8 bytes. E.g. a 16-char hex nonce with 'Hex' toggle = 8 bytes (valid), but the same with UTF8 = 16 bytes (fails); a 12-byte nonce (ChaCha-style) always fails.

Common situations: Using a ChaCha20 12-byte/24-byte nonce by mistake; toggle mismatch; blank nonce (0 bytes) with a non-Integer toggle; hex nonce with UTF8 toggle.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/c218d2e1fe5d5e9e. Report an issue: GitHub.