Cryptanalysis with a Known-Plaintext Attack
Cryptanalysis with a Known-Plaintext Attack
In order to make this demonstration a bit more easy, the encryption algorithm is known and very simple.
- In order to make this attack more difficult...
- Unkown Key Length
- Key is longer than known-plaintext sample
- Location of plaintext unkown
- Ciphertext will be relatively long in length
So we know that somewhere in the plaintext is the word "radical" - that's what makes this a known plaintext attack.
function encrypt(key, msg) {
var ciphertext = '';
for (var i = 0; i < msg.length; i++) {
ciphertext += toHex(msg.charCodeAt(i) ^ key.charCodeAt(i % key.length));
}
return ciphertext;
}
Cipher Text:
Hex representation of "radical" = 7261646963616C Cipher-text: 212F4C1122566D | 00100001 00101111 01001100 00010001 00100010 01010110 01101101 Plain-text: 7261646963616C | 01110010 01100001 01100100 01101001 01100011 01100001 01101100 XOR: 534E2878413701 | 01010011 01001110 00101000 01111000 01000001 00110111 00000001 Hex representation of "radical" = 7261646963616C Cipher-text: 2F4C1122566D19 | 00101111 01001100 00010001 00100010 01010110 01101101 00011001 Plain-text: 7261646963616C | 01110010 01100001 01100100 01101001 01100011 01100001 01101100 XOR: 5D2D754B350C75 | 01011101 00101101 01110101 01001011 00110101 00001100 01110101
Decryption Algorithm
function decrypt(hexkey, msg) {
var plaintext = '';
for (var i = 0; i < msg.length; i+=2) {
plaintext += String.fromCharCode(parseInt(msg.slice(i, i+2), 16) ^ parseInt(hexkey.slice(i%hexkey.length, (i%hexkey.length)+2), 16));
}
return plaintext;
}
function genKey(knowntext, ciphertext, index, padding) {
var key = '';
var len = knowntext.length;
var ciphersample = ciphertext.slice(index, len+index);
for (var i = 0; i < len; i+=2) {
key += toHex(parseInt(ciphersample.slice(i, i+2), 16) ^ parseInt(knowntext.slice(i, i+2), 16));
}
for (var i = 0; i < padding; i++) {
key = key.concat('00');
}
// The key is now a hex string that represents what it takes to turn the ciphertext to the plaintext
// We need to shuffle it around according to what the index is
len += (padding*2);
if (index % len != 0) {
key = key.slice(-(index % len)) + key.slice(0, len - (index % len));
}
return key;
}
Index:
Padding:
At this point, our best key is 68416C654A334D000000 at index 180 and padding 3. Because we're still using padding, the decrypted message is only partially clear. Our hex key is 20 characters long with padding, meaning the key used was 10 characters. We were able to determine 7 of them because our known-plaintext "radical" was 7 characters long.
So what we try to do next is to infer what the remaining "bad" characters are. If we can guess three from a word we can complete our key. Unfortunately because our output was generated by javascript and displayed in html, unprintable characters were excluded from the output. Normally you'd know what value these unprintable characters had. The first "clearest" output I want to look more closely at is Sub�"@uently,N3Ye websi�"�for theN(Cganizat�(_ was ha�,Td, temp�5Prily re. Looking specifically at the words theN(Cganizat�(_ was I think it might be "the organization was."
So we need to figure out what value turns N(C into or
N(C: 4E2843 | 01001110 00101000 01000011 or: 206F72 | 00100000 01101111 01110010 key: 6E4731 | 01101110 01000111 00110001
So our complete key is 68416C654A334D6E4731.
decrypt('68416C654A334D6E4731', ciphertext);
And if you care, the ascii representation of the key is hAleJ3MnG1