Created
December 5, 2024 12:44
-
-
Save LenweSaralonde/4e05f253577d5e4c97f770c707c2acee to your computer and use it in GitHub Desktop.
Bruteforce vigenère cracking Node.js script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Usage: node resolve.js | |
| // You can change the parameters below | |
| const puzzle = 'JYPFFQVY'; | |
| const errorThreshold = 1; | |
| const dictionaryFile = 'All-English-and-WoW-words.txt'; // Get this file at https://gist.github.com/LenweSaralonde/4f9a98c6d7d2569cff881ad8a13a7c4a | |
| /// /////////////////////////////////// | |
| const fs = require('fs'); | |
| const charA = 'A'.charCodeAt(0); | |
| function getLetterNumAt(str, pos) { | |
| return str.charCodeAt(pos) - charA; | |
| } | |
| function getLetterFromNum(num) { | |
| return String.fromCharCode(((26 + num) % 26) + charA); | |
| } | |
| function vigenereDecode(word, key, sign = -1) { | |
| let decoded = ''; | |
| let keyPos = 0; | |
| for (let i = 0; i < word.length; i++) { | |
| if (word[i] === ' ') { | |
| decoded += ' '; | |
| } else { | |
| decoded += getLetterFromNum( | |
| getLetterNumAt(word, i) + | |
| sign * getLetterNumAt(key, keyPos % key.length), | |
| ); | |
| keyPos++; | |
| } | |
| } | |
| return decoded; | |
| } | |
| function vigenereEncode(word, key) { | |
| return vigenereDecode(word, key, 1); | |
| } | |
| /// /////////////////////////////////// | |
| fs.readFile(dictionaryFile, 'utf8', (err, data) => { | |
| console.log('Loading and sorting list...'); | |
| // Extract rows from text file, uppercase, no accents, remove spaces | |
| const rows = data | |
| .split('\n') | |
| .map((word) => | |
| word | |
| // Strip accents | |
| .normalize('NFD') | |
| .replace(/[\u0300-\u036f]/g, '') | |
| // UPPERCASE | |
| .toUpperCase() | |
| // Keep letters only | |
| .replace(/[^A-Z]+/g, '') | |
| .trim(), | |
| ) | |
| // Extract words having at least the length of the puzzle string | |
| .filter((word) => word.length >= puzzle.length); | |
| // Sort words alphabetically | |
| const wordsMap = new Map(); | |
| for (const row of rows) { | |
| const rowWords = row.split(' '); | |
| for (const word of rowWords) { | |
| if (!wordsMap.has(word)) { | |
| wordsMap.set(word, word); | |
| } | |
| } | |
| } | |
| const words = []; | |
| wordsMap.forEach((word) => word.length >= puzzle.length && words.push(word)); | |
| words.sort(); | |
| // Group words by length | |
| const wordsByLength = new Map(); | |
| for (const word of words) { | |
| if (!wordsByLength.has(word.length)) { | |
| wordsByLength.set(word.length, []); | |
| } | |
| wordsByLength.get(word.length).push(word); | |
| } | |
| // Bruteforce | |
| const checkWord = (algorithm, word, decodedWord) => { | |
| const threshold = Math.ceil( | |
| (errorThreshold * decodedWord.length) / puzzle.length, | |
| ); | |
| for (const otherWord of wordsByLength.get(decodedWord.length)) { | |
| let errors = 0; | |
| for (let i = 0; i < decodedWord.length; i++) { | |
| if (otherWord[i] !== decodedWord[i]) { | |
| errors++; | |
| } | |
| } | |
| if (errors <= threshold) { | |
| console.log( | |
| word, | |
| '->', | |
| otherWord, | |
| `(${decodedWord} with ${errors} error(s))`, | |
| `(${algorithm})`, | |
| ); | |
| } | |
| } | |
| }; | |
| console.log(`${words.length} words found of ${puzzle.length}+ letters.`); | |
| console.log('Using puzzle key', puzzle); | |
| console.log('Error tolerance', errorThreshold); | |
| console.log('Bruteforcing...'); | |
| words.forEach((word, index) => { | |
| // Display progression | |
| process.stdout.write( | |
| `${Math.floor((10000 * index) / words.length) / 100} % \r`, | |
| ); | |
| // Try to crack some stuff | |
| checkWord( | |
| 'vigenereDecode(word, puzzle)', | |
| word, | |
| vigenereDecode(word, puzzle), | |
| ); | |
| checkWord( | |
| 'vigenereDecode(puzzle, word)', | |
| word, | |
| vigenereDecode(puzzle, word), | |
| ); | |
| checkWord( | |
| 'vigenereEncode(word, puzzle)', | |
| word, | |
| vigenereEncode(word, puzzle), | |
| ); | |
| checkWord( | |
| 'vigenereEncode(puzzle, word)', | |
| word, | |
| vigenereEncode(puzzle, word), | |
| ); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment