Last active
October 9, 2016 03:41
-
-
Save ebrandel/bd206ec0282c338c82750b660919a0dc to your computer and use it in GitHub Desktop.
AMSCO is an incomplete columnar transposition cipher
This file contains 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
var _ = require("lodash"); | |
var plainText = "On the other side of the screen it all looks so easy"; | |
var key = "4123"; | |
var cipherText = ""; | |
var cols = []; | |
_.each(key.split(""), function(k) { | |
cols.push({ | |
id: Number(k), | |
chunks: [] | |
}); | |
}); | |
// remove any spaces and convert to upper case. Normallizing the text isn't requierd, | |
// but not doing so would leak information about the text being output | |
var compressedText = plainText.replace(/\s+/g, "").toLocaleUpperCase(); | |
var a = 0; // track position in compressedText | |
var i = 0; // column index | |
var row = 0; | |
var chunkSize = 1; | |
var chunk = ""; | |
var cellMap = [[]]; | |
for (var z=0; z<key.length; z++) { | |
cellMap[z] = []; | |
} | |
// build the columns from the plainText | |
while (a < compressedText.length) { | |
i = i % cols.length; | |
// determine chunk size | |
chunkSize = ((row + i + 1) % 2) + 1; | |
// get a chunk from the plainText | |
chunk = compressedText.substr(a, chunkSize); | |
// add the new chunk to the bottom of the column | |
cols[i].chunks.push(chunk); | |
// move the position tracker | |
a += chunkSize; | |
// add the size of the chunk to our cellMap, which is used later | |
// typically this wouldn't be created when you're building the | |
// plaintext columns, but the data is readily available | |
cellMap[i][row] = chunkSize; | |
i++; | |
if (i === cols.length) { | |
// end of the row, increase row count | |
row++; | |
} | |
} | |
// build the cipherText | |
// go through all of the columns and join them together | |
i = 0; | |
var column = {}; | |
var cipherText = ""; | |
while (i < key.length) { | |
column = _.find(cols, {id : i + 1}); | |
cipherText = cipherText + column.chunks.join(""); | |
i++; | |
} | |
// build some columns | |
// first get the total number of rows | |
// for even key length, this is more straight forward | |
var rowCharCount = 0; | |
var colCount = 0; | |
var rowCount = 0; | |
if (key.length % 2 === 0) { | |
// even number of columns | |
// each row has the same number of letters, except (possible) the last | |
rowCharCount = key.length * 1.5; | |
rowCount = Math.floor(cipherText.length / rowCharCount); | |
if (cipherText.length % rowCharCount > 0) { | |
rowCount++; | |
} | |
} else { | |
rowCount = 0; | |
a = 0; | |
// rowCharCount varies by row | |
// even rows, which start with 2 chars: | |
while (a < cipherText.length) { | |
if (rowCount % 2 === 0) { | |
rowCharCount = key.length + Math.ceil(key.length / 2); | |
} else { | |
rowCharCount = key.length + Math.floor(key.length / 2); | |
} | |
a += rowCharCount; | |
rowCount++; | |
} | |
} | |
// now that the ciphertext has been created | |
// let's attempt to decrypt it using the key | |
// the first step is to create a place to put the values | |
var newCols = []; | |
_.each(key.split(""), function(k) { | |
newCols.push({ | |
id: Number(k), | |
chunks: [] | |
}) | |
}); | |
a = 0; | |
i = 1; | |
row = 0; | |
var colKey = 0; | |
while (a < cipherText.length) { | |
colKey = key.indexOf(i); | |
// utilizing the cellMap we built up earlier. This is | |
// just a table of 2,1,2,1,... values that | |
// represent the chunk size structure | |
rowCount = cellMap[colKey].length; | |
chunkSize = cellMap[colKey][row]; | |
// grab the new column | |
column = _.find(newCols, {id : i}); | |
// get the chunk | |
chunk = cipherText.substr(a, chunkSize); | |
// push the chunk | |
column.chunks.push(chunk); | |
a += chunkSize; | |
row++; | |
if (row === rowCount) { | |
row = 0; | |
i++; | |
} | |
} | |
// build the message back up | |
// this will just go through the columns | |
// in proper order, from left to right and then down | |
// and rebuild the original message | |
a = 0; | |
var finalMessage = ""; | |
i = 0; | |
while (finalMessage.length < compressedText.length) { | |
column = newCols[i]; | |
finalMessage += column.chunks.shift(); | |
i++; | |
// move back to the first column | |
if (i === key.length) { | |
i = 0; | |
} | |
} | |
// let's see how we did | |
console.log(compressedText); | |
console.log(cipherText); | |
console.log(finalMessage); | |
if (compressedText === finalMessage) { | |
console.log("Congrats"); | |
} else { | |
console.log("Failed") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment