{"record":{"id":"9aa7ff5a4ebabc2c","repo":"gchq/CyberChef","slug":"invalid-key-length-key-length-bytes-salsa20","errorCode":null,"errorMessage":"Invalid key length: ${key.length} bytes.\n\nSalsa20 uses a key of 16 or 32 bytes (128 or 256 bits).","messagePattern":"Invalid key length: (.+?) bytes\\.\n\nSalsa20 uses a key of 16 or 32 bytes \\(128 or 256 bits\\)\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/Salsa20.mjs","lineNumber":80,"sourceCode":"                \"value\": [\"Raw\", \"Hex\"]\n            }\n        ];\n    }\n\n    /**\n     * @param {string} input\n     * @param {Object[]} args\n     * @returns {string}\n     */\n    run(input, args) {\n        const key = Utils.convertToByteArray(args[0].string, args[0].option),\n            nonceType = args[1].option,\n            rounds = parseInt(args[3], 10),\n            inputType = args[4],\n            outputType = args[5];\n\n        if (key.length !== 16 && key.length !== 32) {\n            throw new OperationError(`Invalid key length: ${key.length} bytes.\n\nSalsa20 uses a key of 16 or 32 bytes (128 or 256 bits).`);\n        }\n\n        let counter, nonce;\n        if (nonceType === \"Integer\") {\n            nonce = Utils.intToByteArray(parseInt(args[1].string, 10), 8, \"little\");\n        } else {\n            nonce = Utils.convertToByteArray(args[1].string, args[1].option);\n            if (!(nonce.length === 8)) {\n                throw new OperationError(`Invalid nonce length: ${nonce.length} bytes.\n\nSalsa20 uses a nonce of 8 bytes (64 bits).`);\n            }\n        }\n        counter = Utils.intToByteArray(args[2], 8, \"little\");\n\n        const output = [];","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/Salsa20.mjs#L62-L98","documentation":"Thrown by the Salsa20 operation when the decoded key is neither 16 nor 32 bytes. Salsa20 supports 128-bit (16-byte) or 256-bit (32-byte) keys only. The key is decoded from its toggle option (Hex/UTF8/Latin1/Base64) before the length check.","triggerScenarios":"A key whose decoded byte length is not 16 or 32 — e.g. a 20-byte key, or a toggle mismatch turning a 32-char hex key into 32 UTF8 bytes (which happens to be valid) while a 16-char one becomes 16 bytes (also coincidentally valid), but a 24-char key or wrong toggle fails.","commonSituations":"Toggle set to UTF8 while the key is hex (or vice versa); using a ChaCha20 32-byte key with wrong toggle; blank key (0 bytes); truncated key.","solutions":["Provide a 16-byte or 32-byte key; for hex keys use the 'Hex' toggle so char count halves to bytes.","Confirm the toggle matches the key's encoding.","Remove trailing whitespace/newlines from the key."],"exampleFix":"// before: 16 hex chars read as UTF8 -> 16 bytes (ok), but 64 hex chars -> 64 bytes (fails)\nsalsa20.run(pt, [{string:\"<64 hex>\", option:\"UTF8\"}, ...])\n// after\nsalsa20.run(pt, [{string:\"<64 hex>\", option:\"Hex\"}, ...])","handlingStrategy":"validation","validationCode":"const key = Utils.convertToByteArray(keyArg.string, keyArg.option);\nif (key.length !== 16 && key.length !== 32) {\n  throw new Error(`Salsa20 key must be 16 or 32 bytes, got ${key.length}. Check toggle.`);\n}","typeGuard":"function isSalsa20KeyArg(arg) {\n  const len = Utils.convertToByteArray(arg.string, arg.option).length;\n  return len === 16 || len === 32;\n}","tryCatchPattern":null,"preventionTips":["Decode the key to exactly 16 or 32 bytes using the correct toggle.","For hex keys use the 'Hex' toggle."],"tags":["crypto","salsa20","cipher","key","operation","validation"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}