gchq/CyberChef · error · OperationError
Letter ${elem} not in LS47
Error message
Letter ${elem} not in LS47 What it means
LS47's checkKey() intends to reject keys containing a character outside its 49-character alphabet. NOTE: as written, the loop iterates `letters` (the constant alphabet) rather than `key`, so letters.indexOf(elem) is always >= 0 and this branch is effectively unreachable in the current code. The intended behaviour is to flag any key byte not in "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()".
Source
Thrown at src/core/lib/LS47.mjs:105
i = (i + 1) % 7;
}
return k;
}
/**
* Checks the key is a valid key.
*
* @param {string} key
*/
function checkKey(key) {
if (key.length !== letters.length)
throw new OperationError("Wrong key size");
const counts = new Array();
for (let i = 0; i < letters.length; i++)
counts[letters.charAt(i)] = 0;
for (const elem of letters) {
if (letters.indexOf(elem) === -1)
throw new OperationError("Letter " + elem + " not in LS47");
counts[elem]++;
if (counts[elem] > 1)
throw new OperationError("Letter duplicated in the key");
}
}
/**
* Finds the position of a letter in they key.
*
* @param {letter} key
* @param {string} letter
* @returns {object}
*/
function findPos (key, letter) {
const index = key.indexOf(letter);
if (index >= 0 && index < 49)
return [Math.floor(index/7), index%7];
throw new OperationError("Letter " + letter + " is not in the key");View on GitHub (pinned to 4290ea7539)
Solutions
- Use deriveKey(password) which can only emit alphabet characters.
- If you build a key manually, restrict it to the exact alphabet string and validate with a regex like /^[_a-z0-9.,\-+*/:?!'()]+$/.
- Report the upstream bug: the for-loop should iterate `key`, not `letters`, so this guard actually fires.
Example fix
// Bug in src/core/lib/LS47.mjs: the loop iterates `letters` not `key`.
// before
for (const elem of letters) {
if (letters.indexOf(elem) === -1) throw ...; // always false
}
// after (intended)
for (const elem of key) {
if (letters.indexOf(elem) === -1)
throw new OperationError("Letter " + elem + " not in LS47");
} Defensive patterns
Strategy: validation
Validate before calling
const LS47_ALPHABET = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
function keyUsesOnlyAlphabet(key) {
return [...key].every(c => LS47_ALPHABET.includes(c));
} Type guard
function isAllLS47Chars(x): x is string {
const A = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
return typeof x === 'string' && [...x].every(c => A.includes(c));
} Prevention
- Use deriveKey() which only emits alphabet characters.
- Validate manually with a regex/charset check before calling encrypt/decrypt since the in-library guard is currently a no-op (upstream bug).
- Lowercase and substitute unsupported characters before constructing a key by hand.
- Report the upstream bug so the library check actually iterates the key.
When it happens
Trigger: Intent: calling checkKey (via encrypt/decrypt/encryptPad/decryptPad) with a 49-char key that includes a character outside the LS47 alphabet (e.g. '@', uppercase letters, '\n'). In practice, with the current code the check never fires because the loop target is wrong.
Common situations: Hand-built key containing uppercase letters or symbols outside the alphabet; key encoded with characters from a related cipher (e.g. LS47+ which has a different alphabet); copy-paste introduced a stray character. The bug also masks these cases from users.
Related errors
- Letter duplicated in the key
- Wrong key size
- Letter ${letter} is not in the key
- Letter ${letter} is not included in LS47
- No padding requested but input is not a ${blockSize}-byte mu
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/56bb36da5ff11838.
Report an issue: GitHub.