Skip to content

Instantly share code, notes, and snippets.

@ccckmit
Last active August 29, 2015 14:16
Show Gist options
  • Save ccckmit/b0a90a7bd9d3d3e5baa8 to your computer and use it in GitHub Desktop.
Save ccckmit/b0a90a7bd9d3d3e5baa8 to your computer and use it in GitHub Desktop.
javascript version of enigma machine (in world war II)
// 原始程式來源: http://practicalcryptography.com/ciphers/enigma-cipher/ 網頁內的 javascript 程式碼
var c = console;
var plaintext = 'ABCDEF';
c.log('>> plaintext2 : '+plaintext);
var ciphertext = Encrypt(plaintext, 'AAA', 'AAA', '123', 'POMLIUKJNHYTGBVFREDC');
c.log('>> ciphertext : '+ciphertext);
var plaintext2 = Encrypt(ciphertext, 'AAA', 'AAA', '123', 'POMLIUKJNHYTGBVFREDC');
c.log('>> plaintext2 : '+plaintext);
function Encrypt(plaintext, keysettings, ringsettings, rotorsettings, plugboardsettings) {
c.log('plaintext='+plaintext);
c.log('keysetting='+keysettings);
c.log('ringsetting='+ringsettings);
c.log('plugboardsettings='+plugboardsettings+' len='+plugboardsettings.length);
c.log('rotorsettings='+rotorsettings);
// do some error checking
if(plaintext.length < 1){ c.log("please enter some plaintext (letters and numbers only)"); return; }
if(keysettings.length != 3){ c.log("Key settings must consist of 3 uppercase characters."); return; }
if(ringsettings.length != 3){ c.log("Ring settings must consist of 3 uppercase characters."); return; }
if(plugboardsettings.length > 26){ c.log("There cannot be more than 13 pairs in the plugboard settings."); return; }
if(plugboardsettings.length % 2 != 0){ c.log("There must be an even number of characters in the plugboard settings."); return; }
if(rotorsettings.length != 3){ c.log("Rotor settings must consist of 3 numbers 1-9."); return; }
// interpret the rotor settings (strings 1-8 to int 0-7)
var rotors = rotorsettings.split("");
rotors[0]=rotors[0].valueOf()-1;rotors[1]=rotors[1].valueOf()-1;rotors[2]=rotors[2].valueOf()-1;
// parse plugboard settings, store as a simple substitution key
var plugboard = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var parr = plugboard.split("");
for(i=0,j=1;i<plugboardsettings.length;i+=2,j+=2){
ichar = plugboard.indexOf(plugboardsettings.charAt(i));
jchar = plugboard.indexOf(plugboardsettings.charAt(j));
temp = parr[jchar]; parr[jchar]=parr[ichar]; parr[ichar]=temp;
}
plugboard = parr.join("");
// interpret key and ring settings (convert from letters to numbers 0-25)
key=keysettings.split("");
key[0]=code(key[0]); key[1]=code(key[1]); key[2]=code(key[2]);
ring=ringsettings.split("");
ring[0]=code(ring[0]); ring[1]=code(ring[1]); ring[2]=code(ring[2]);
// do the actual enigma enciphering
ciphertext = "";
for(i=0; i < plaintext.length; i++){
ch=plaintext.charAt(i);
// if the current character is not a letter, pass it through unchanged
if(!ch.match(/[A-Z]/)){
ciphertext = ciphertext + ch;
}else{
key=increment_settings(key,rotors);
ciphertext = ciphertext + enigma(ch,key,rotors,ring,plugboard);
}
}
c.log('ciphertext='+ciphertext);
return ciphertext;
}
function enigma(ch,key,rotors,ring,plugboard){
// apply plugboard transformation
ch = simplesub(ch,plugboard);
// apply rotor transformations from right to left
ch = rotor(ch,rotors[2],key[2]-ring[2]);
ch = rotor(ch,rotors[1],key[1]-ring[1]);
ch = rotor(ch,rotors[0],key[0]-ring[0]);
// use reflector B
ch = simplesub(ch,"YRUHQSLDPXNGOKMIEBFZCWVJAT");
// apply inverse rotor transformations from left to right
ch = rotor(ch,rotors[0]+8,key[0]-ring[0]);
ch = rotor(ch,rotors[1]+8,key[1]-ring[1]);
ch = rotor(ch,rotors[2]+8,key[2]-ring[2]);
// apply plugboard transformation again
ch = simplesub(ch,plugboard);
return ch;
}
function increment_settings(key,r){
//notch = [['Q','Q'],['E','E'],['V','V'],['J','J'],['Z','Z'],['Z','M'],['Z','M'],['Z','M']];
// The notch array stores the positions at which each rotor kicks over the rotor to its left
var notch = [[16,16],[4,4],[21,21],[9,9],[25,25],[25,12],[25,12],[25,12]];
if(key[1] == notch[r[1]][0] || key[1] == notch[r[1]][1]){
key[0] = (key[0]+1)%26;
key[1] = (key[1]+1)%26;
}
if(key[2] == notch[r[2]][0] || key[2] == notch[r[2]][1]){
key[1] = (key[1]+1)%26;
}
key[2] = (key[2]+1)%26;
return key;
}
// perform a simple substitution cipher
function simplesub(ch,key){
return key.charAt(code(ch));
}
function rotor(ch,r,offset){
// The first eight strings represent the rotor substitutions I through VIII. The second 8 are the
// inverse transformations
var key = ["EKMFLGDQVZNTOWYHXUSPAIBRCJ","AJDKSIRUXBLHWTMCQGZNPYFVOE","BDFHJLCPRTXVZNYEIWGAKMUSQO",
"ESOVPZJAYQUIRHXLNFTGKDCMWB","VZBRGITYUPSDNHLXAWMJQOFECK","JPGVOUMFYQBENHZRDKASXLICTW",
"NZJHGRCXMYSWBOUFAIVLPEKQDT","FKQHTLXOCBJSPDZRAMEWNIUYGV",
// inverses
"UWYGADFPVZBECKMTHXSLRINQOJ","AJPCZWRLFBDKOTYUQGENHXMIVS","TAGBPCSDQEUFVNZHYIXJWLRKOM",
"HZWVARTNLGUPXQCEJMBSKDYOIF","QCYLXWENFTZOSMVJUDKGIARPHB","SKXQLHCNWARVGMEBJPTYFDZUIO",
"QMGYVPEDRCWTIANUXFKZOSLHJB","QJINSAYDVKBFRUHMCPLEWZTGXO"];
// the following code looks a bit horrible, but it is essentially just doing a simple substitution
// taking into account 16 possible keys (8 rotors and their inverses) and the offset (which is calculated
// from the indicator and ring settings). The offset essentially shifts the rotor key to the left or right
var chcode = (code(ch)+26+offset)%26;
var mapch = (code( key[r].charAt(chcode) ) +26-offset)%26 +65;
return String.fromCharCode(mapch);
}
// return the number 0-25 given a letter [A-Za-z]
function code(ch){return ch.toUpperCase().charCodeAt(0) - 65;}
@ccckmit
Copy link
Author

ccckmit commented Mar 2, 2015

$ node enigma.js

plaintext2 : ABCDEF

plaintext=ABCDEF
keysetting=AAA
ringsetting=AAA
plugboardsettings=POMLIUKJNHYTGBVFREDC len=20
rotorsettings=123
ciphertext=GWKGRC

ciphertext : GWKGRC

plaintext=GWKGRC
keysetting=AAA
ringsetting=AAA
plugboardsettings=POMLIUKJNHYTGBVFREDC len=20
rotorsettings=123
ciphertext=ABCDEF

plaintext2 : ABCDEF

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment