Skip to content

Instantly share code, notes, and snippets.

@ebrandel
Last active October 9, 2016 03:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ebrandel/bd206ec0282c338c82750b660919a0dc to your computer and use it in GitHub Desktop.
Save ebrandel/bd206ec0282c338c82750b660919a0dc to your computer and use it in GitHub Desktop.
AMSCO is an incomplete columnar transposition cipher
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