Je suis auteur d’énigmes chez Zupple, team building et énigmes à Lyon, et je trouve que les codes secrets sont géniaux pour faire apparaître un message qui était devant vous depuis le début ! Dans cet article, enfin, après 7 articles d’introduction, je vais vous expliquer comment encoder et décoder des messages à l’aide du chiffre Vigenere.
Le code Vigenere est une technique de chiffrement qui est la suite logique du code César. Celui-ci avait pour défaut d’être facilement détectable : une lettre encodée donnera toujours le même résultat. Un code César peut aussi être facilement brut-forcé, car il n’y a que 25 clés possibles.
Avec un chiffrement Vigenere, on utilise un mot ou une phrase comme clé. Chaque lettre de cette clé va être utilisée, chacune à son tour, pour définir le décalage pour encoder le message original. Ainsi, l’encodage d’une lettre va dépendre de sa position dans le message. En plus, il existe une infinité de clés possibles.
Par exemple, un code César Progressif est un code Vigenere avec la clé « BCDEFGH… ».
/**
* Returns the message encoded with the Vigenere cipher
*
* @param {string} message
* @param {string} key
* @param {boolean} isDecoding - true to decode instead of encode
* @return {string} The encoded message
*/
function encodeVigenere(message, key, isDecoding) {
let stepIndex = -1;
// Clean key to only have letters from the alphabet
const safeKey = key
.split("")
.filter(isLetterInAlphabet)
.join("");
return message
.split("")
.map(function(letter, index) {
// We do not encode a letter that is not in the alphabet
if (!isLetterInAlphabet(letter)) {
return letter;
}
// Once you have used all the letters in the key, we restart from the beginning
stepIndex = (stepIndex + 1) % safeKey.length;
// The current offset is the position in the alphabet of the current letter of the key
const offset = letterToNumber(safeKey[stepIndex]) - 1;
return shiftLetter(letter, isDecoding ? -offset : offset);
})
.join("");
}
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
// See https://tzi.fr/decaler-lettres/
function shiftLetter(letter, offset) {
if (!isLetterInAlphabet(letter)) {
return letter;
}
return numberToLetter(letterToNumber(letter) + offset);
}
// See https://tzi.fr/js/convertir-lettres-en-nombres/
function isLetterInAlphabet(letter) {
return alphabet.includes(letter.toUpperCase());
}
function letterToNumber(letter) {
if (!isLetterInAlphabet(letter)) {
return false;
}
return alphabet.indexOf(letter.toUpperCase()) + 1;
}
// See https://tzi.fr/js/convertir-nombres-en-lettres/
function numberToLetter(position) {
return getLetterByAlphabeticPosition(
moduloClamp(position, 1, alphabet.length)
);
}
function getLetterByAlphabeticPosition(position) {
if (position < 1 || position > alphabet.length) {
return false;
}
return alphabet[position - 1];
}
// See https://tzi.fr/js/modulos-min-max
function moduloClamp(number, min, max) {
return positiveModulo(number - min, max + 1 - min) + min;
}
// See https://tzi.fr/js/modulos-positifs
function positiveModulo(number, divisor) {
return ((number % divisor) + divisor) % divisor;
}