Last active
July 29, 2017 14:34
-
-
Save rajasharan/efc547583174eed2c1f9c86cec5bbabd to your computer and use it in GitHub Desktop.
bit level manipulation in javascript
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
const R = require('ramda'); | |
/* | |
* https://cryptopals.com/sets/1/challenges/1 | |
*/ | |
const HEX_DIGITS = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]; | |
const HEX_TO_BINARY_MAP = { | |
"0": "0000", | |
"1": "0001", | |
"2": "0010", | |
"3": "0011", | |
"4": "0100", | |
"5": "0101", | |
"6": "0110", | |
"7": "0111", | |
"8": "1000", | |
"9": "1001", | |
"A": "1010", | |
"B": "1011", | |
"C": "1100", | |
"D": "1101", | |
"E": "1110", | |
"F": "1111", | |
"a": "1010", | |
"b": "1011", | |
"c": "1100", | |
"d": "1101", | |
"e": "1110", | |
"f": "1111" | |
}; | |
const BASE64_DIGITS = [ | |
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", | |
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", | |
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/" | |
]; | |
var binary = hex => HEX_TO_BINARY_MAP[hex]; | |
var base64 = decimal => BASE64_DIGITS[decimal]; | |
var hex = decimal => HEX_DIGITS[decimal]; | |
var decimal = binary => { | |
let binaryDigits = R.compose( | |
R.map(parseInt), | |
R.split('') | |
)(binary); | |
let binaryMultiplier = R.compose( | |
R.reverse, | |
R.map(x => Math.pow(2, x)), | |
R.range(0), | |
R.length | |
)(binaryDigits); | |
let zippedArray = R.zipWith(R.multiply)(binaryDigits)(binaryMultiplier); | |
return R.sum(zippedArray); | |
}; | |
var hexToBinary = R.compose( | |
R.join(''), | |
R.map(binary), | |
R.split('') | |
); | |
var binaryToBase64 = R.compose( | |
R.join(''), | |
R.map(base64), | |
R.map(decimal), | |
R.splitEvery(6) | |
); | |
var hexToBase64 = R.compose( | |
binaryToBase64, | |
hexToBinary | |
); | |
console.log(hexToBase64('49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d')); | |
//-> SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t | |
/* | |
* https://cryptopals.com/sets/1/challenges/2 | |
*/ | |
var binaryToHex = R.compose( | |
R.join(''), | |
R.map(hex), | |
R.map(decimal), | |
R.splitEvery(4) | |
); | |
var xor = (hex1, hex2) => R.compose( | |
binaryToHex, | |
R.join(''), | |
R.map(x => x? 0 : 1), | |
R.zipWith(R.equals, R.split('', hexToBinary(hex1))), | |
R.split('') | |
)(hexToBinary(hex2)); | |
console.log(xor('1c0111001f010100061a024b53535009181c', '686974207468652062756c6c277320657965')); | |
//-> 746865206b696420646f6e277420706c6179 | |
/* | |
* https://cryptopals.com/sets/1/challenges/3 | |
*/ | |
/* 32 to 126 */ | |
const ASCII_CHARS = [ | |
" ", "!", '"', "#", "$", "%", "%", "'", "(", ")", "*", "+", ",", "-", ".", "/", | |
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", | |
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", | |
"[", "\\", "]", "^", "_", "`", | |
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", | |
"{", "|", "}", "~" | |
]; | |
var asciiDecimal = char => R.ifElse(R.equals('\n'), () => 10, () => R.indexOf(char, ASCII_CHARS) + 32)(char); | |
var binary10 = decimal => { | |
let _binary10 = (decimal, result) => { | |
let quotient = Math.floor(decimal/2); | |
let reminder = R.modulo(decimal, 2); | |
result = R.append(reminder, result); | |
if (R.equals(0, quotient)) { | |
return result; | |
} | |
else { | |
return _binary10(quotient, result); | |
} | |
}; | |
let binList = _binary10(decimal, []); | |
let binLen = R.length(binList); | |
let zeroList = R.repeat(0, 8 - binLen); | |
return R.pipe(R.concat, R.reverse, R.join(''))(binList, zeroList); | |
}; | |
var ascii = binary => R.pipe(R.splitEvery(8), R.map(decimal), R.map(dec => ASCII_CHARS[dec - 32]), R.join(''))(binary); | |
var xor2 = (bin1, bin2) => R.compose( | |
R.join(''), | |
R.map(x => x? 0 : 1), | |
R.zipWith(R.equals, R.split('', bin1)), | |
R.split('') | |
)(bin2); | |
var repeatingKeyXor = (binaryStr, chars) => { | |
let byteCount = R.length(binaryStr) / 8; | |
let decimals = R.map(asciiDecimal)(chars); | |
let bytes = R.pipe(R.map(binary10), R.join(''))(decimals); | |
let byteStr = R.compose(R.join(''), R.flatten, R.repeat)(bytes, byteCount); | |
let xorBinary = xor2(binaryStr, byteStr); | |
return xorBinary; | |
}; | |
var frequencyDistribution = str => R.pipe(R.split(''), R.groupBy(a => a), R.map(v => R.length(v)))(str); | |
const BASE_SCORES = { | |
"E": 20, "T": 19, "A": 18, "O": 17, "I": 16, "N": 15, " ": 14, "S": 13, "H": 12, "R": 11, "D": 10, "L": 7, "U": 8, | |
"e": 20, "t": 19, "a": 18, "o": 17, "i": 16, "n": 15, " ": 14, "s": 13, "h": 12, "r": 11, "d": 10, "l": 7, "u": 8 | |
}; | |
var _score = (val, key) => { | |
let baseScore = BASE_SCORES[key]; | |
let score = R.isNil(baseScore)? 1 : val * baseScore; | |
return score; | |
}; | |
var score = str => R.pipe(frequencyDistribution, R.mapObjIndexed(_score), R.values, R.sum)(str); | |
const ENCRYPTED_TEXT = '1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736'; | |
var decryptObjs = encrptedText => R.map(char => { | |
let obj = {}; | |
obj[char] = R.compose(ascii, repeatingKeyXor)(hexToBinary(encrptedText), [char]); | |
obj.score = R.compose(score, ascii, repeatingKeyXor)(hexToBinary(encrptedText), [char]); | |
return obj; | |
})(ASCII_CHARS); | |
var decryptObj = encrptedText => R.pipe(decryptObjs, R.sortBy(R.prop('score')), R.reverse)(encrptedText)[0]; | |
console.log(decryptObj(ENCRYPTED_TEXT)); | |
//-> { X: "Cooking MC's like a pound of bacon", score: 349 } | |
/* | |
* https://cryptopals.com/sets/1/challenges/4 | |
* | |
const fs = require('fs'); | |
fs.readFile('4.txt', { encoding:'utf8' }, (err, data) => { | |
let lines = R.split('\n', data); | |
let result = R.pipe(R.map(decryptObj), R.sortBy(R.prop('score')), R.reverse)(lines)[0]; | |
console.log(result); | |
//-> { '5': 'Now that the party is jumping', score: 343 } | |
}); | |
* | |
*/ | |
/* | |
* https://cryptopals.com/sets/1/challenges/5 | |
*/ | |
const REPEATING_KEY = "ICE"; | |
const ENG_MSG = "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"; | |
var binaryStr = ascii => R.pipe(R.split(''), R.map(asciiDecimal), R.map(binary10), R.join(''))(ascii); | |
var result = R.compose( | |
binaryToHex, | |
repeatingKeyXor | |
)(binaryStr(ENG_MSG), R.split('', REPEATING_KEY)); | |
console.log(R.pipe(R.splitEvery(74), R.join('\n'))(result)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment