UNPKG

js-ktc

Version:

Javascript Keyword Transposition Cipher

222 lines (192 loc) 7.57 kB
'use strict'; class Cipher { constructor(secret, props = { alpha: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', toUpper: true, stripCharacters: true, addSpaces: true, stripSpaces: true }) { this.props = props; // set class properties this.alpha = props.alpha; this.trans = []; // Prepare the keyword based on the provided string in the secret variable // // Example: if the keyword "mythology" was used, it would become "MYTHOLOGY" // after running this._parse this.key = this._parse(secret) // Remove any duplicate characters from the keyword // // Example: the keyword "MYTHOLOGY" would become "MYTHOLG" this.key = this._distinct(this.key); // Prepare the cipher based on the key by splitting the key into an array of letters // // Example: they key "MYTHOLG" is split into the array // ["M", "Y", "T", "H", "O", "L", "G"] this.cipher = this.key.split(''); // Sort the cipher by alphabetical order // // now the array is sorted into // ["G", "H", "L", "M", "O", "T", "Y"] this.cipher = this.cipher.sort(); // Set the "width" of the cipher. // // The cipher is a string with a set number of unique characters. // In our example, the width is 7 this.width = this.cipher.length; // Set the "height" of the cipher. // // Divide the length of the alphabet by the length of the cipher // to find its width. In our example, the height is 4 this.height = Math.floor(this.alpha.length / this.cipher.length)+1; // Create a "keyword" alphabet based on the original alphabet and the keyword // // in our example, kAlpha will equal: /* MYTHOLGABCDEFGHIJKLMNOPQRSTUVWXYZ */ var kAlpha = this.key + this.alpha; // Remove any repeated letters // // in our example, kAlpha becomes: /* MYTHOLGABCDEFIJKNPQRSUVWXZ */ kAlpha = this._distinct(kAlpha); // Store keyword alpha as a class property and add padding to the end of it. // // In our example: // 7 * 4 = 28 - 26 + 1 = 3 // An array of length 3 is created, then joined together on a " " blank space character. // This is added to the kAlpha and stored as a class property. const padding = Array(Math.abs(this.width * this.height - kAlpha.length) + 1).join(" "); this.kAlpha = kAlpha + padding; // ARRANGE THE GRID // // Iterate over each letter in the cipher array. this.cipher.forEach( (_, i) => { // get a letter from the keyword alphabet let letter = this.kAlpha[i]; // get the index of the chosen letter from the cipher let cipherIndex = this.cipher.indexOf(letter); // store the character in the transform alpha using the cipher index // // In our example, a transform of the following shape is created // this is used to create an enciphered alphabet // [-6, -2, -3, 3, 0, 3, 5 ] this.trans[cipherIndex] = cipherIndex - i; } ); // Build the encrypted alphabet // // In our example, GISHDPXLFRMAJUOEQZTCNWYBKV this.encryptedAlpha = this._parse( // Split the keyword into an array this.kAlpha.split('').map( (_, i) => { /* set keyword alphabet index _flatIndex transforms the given keyword alpha index into an index which can be used with a transform. The max between that number and the kAlpha length becomes the kAlphaIndex. This operation is performed for each letter in the kAlpha string. In our example, the index => flat => kAlpha => kAlphaIndex transformation for each letter is: 0 => 0 => 6 => 6 1 => 7 => 13 => 13 2 => 14 => 20 => 20 3 => 21 => 27 => 27 4 => 1 => 3 => 3 5 => 8 => 10 => 10 6 => 15 => 17 => 17 7 => 22 => 24 => 24 8 => 2 => 5 => 5 9 => 9 => 12 => 12 10 => 16 => 19 => 19 11 => 23 => 26 => 26 12 => 3 => 0 => 0 13 => 10 => 7 => 7 14 => 17 => 14 => 14 15 => 24 => 21 => 21 16 => 4 => 4 => 4 17 => 11 => 11 => 11 18 => 18 => 18 => 18 19 => 25 => 25 => 25 20 => 5 => 2 => 2 21 => 12 => 9 => 9 22 => 19 => 16 => 16 23 => 26 => 23 => 23 24 => 6 => 1 => 1 25 => 13 => 8 => 8 26 => 20 => 15 => 15 27 => 27 => 22 => 22 */ let kAlphaIndex = this._max( this._flatIndex(i) - this.trans[this._flatIndex(i) % this.width], this.kAlpha.length ) return this.kAlpha[kAlphaIndex] } ).join('') ); } /* * Remove any repeated letters from the given string * */ _distinct = (val) => val.split('').filter((item, i, self) => self.indexOf(item) === i).join('') /* * Compare two integers and return max if i is greater * */ _max = (i, max) => i > max ? max : i /* * Get an index based on the "height" and "width" of the cipher * */ _flatIndex = (i) => Math.floor(i / this.height) + ((i % this.height) * this.width) /* * Adds spaces to the given string * */ _addSpaces = (s) => this.props.addSpaces ? s.replace(/.{5}/g, '$& ') : s /* * Encrypts the given input string * */ _crypt = (s, alphaA, alphaB) => this._addSpaces(s.split('').map(lttr => alphaA[alphaB.indexOf(lttr)]).join('')) /* * Converts the given string to upper-case * */ _convertCase = (s) => this.props.toUpper ? s.toUpperCase() : s; /* * Strip any non-alpha characters from the input string * */ _stripCharacters = (s) => this.props.stripCharacters ? s.replace(/[^a-zA-Z]/g, '') : s /* * Strip any spaces from the input string * */ _stripSpaces = (s) => this.props.stripSpaces ? s.replace(/ /g, '') : s /* * Convert the given string to uppercase, replace non-alpha characters. * */ _parse = (s) => this._stripCharacters( this._stripSpaces( this._convertCase(s) ) ) /* * encrypt(string: s): string * * Given a plain text string, this function will return an encrypted version of it * using the _crypt function. * */ encrypt = (s) => this._crypt(this._parse(s), this.encryptedAlpha, this.alpha) /* * decrypt(string: s): string * * Given an encrypted string, this function will return a decrypt it * using the _crypt function. * */ decrypt = (s) => this._crypt(s, this.alpha, this.encryptedAlpha) } module.exports = Cipher;