gchq/CyberChef · error · OperationError
Letter duplicated in the key
Error message
Letter duplicated in the key
What it means
LS47 keys must be permutations: each of the 49 alphabet characters must appear exactly once. checkKey() intends to detect duplicates via a per-character count map. NOTE: like error 101, the loop iterates `letters` rather than `key`, so counts[elem] is incremented exactly once per alphabet character and never exceeds 1; this branch is effectively unreachable in the current code.
Source
Thrown at src/core/lib/LS47.mjs:108
}
/**
* 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
- Always derive keys via deriveKey(password), which produces a correct permutation.
- Validate manually with new Set(key).size === 49 && key.length === 49 before calling encrypt/decrypt.
- Report the upstream bug: change `for (const elem of letters)` to `for (const elem of key)` so duplicates are detected.
Example fix
// before (buggy - iterates `letters`, never detects duplicates in key)
for (const elem of letters) {
counts[elem]++;
if (counts[elem] > 1) throw new OperationError("Letter duplicated in the key");
}
// after
for (const elem of key) {
counts[elem]++;
if (counts[elem] > 1) throw new OperationError("Letter duplicated in the key");
} Defensive patterns
Strategy: validation
Validate before calling
function isLS47Permutation(key) {
return key.length === 49 && new Set(key).size === 49;
} Type guard
function isLS47PermutationKey(x): x is string {
return typeof x === 'string' && x.length === 49 && new Set(x).size === 49;
} Prevention
- Always use deriveKey() to obtain a valid permutation.
- Verify new Set(key).size === 49 yourself because the in-library guard is currently a no-op (upstream bug).
- When editing a key manually, replace characters rather than inserting duplicates.
- Persist keys as opaque blobs rather than re-typing them.
When it happens
Trigger: Intent: calling encrypt/decrypt with a 49-char key that repeats a character (e.g. two 'a's) and therefore omits another. In practice the current loop never trips this guard.
Common situations: Hand-crafted key with a typo duplicating a letter; key derived from a faulty source; key edited in a text editor without unique-character verification. The masking bug lets invalid keys through to produce wrong output instead of an error.
Related errors
- Letter ${elem} not in LS47
- 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/d5245fd0647b9750.
Report an issue: GitHub.