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

  1. Always derive keys via deriveKey(password), which produces a correct permutation.
  2. Validate manually with new Set(key).size === 49 && key.length === 49 before calling encrypt/decrypt.
  3. 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

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


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