import { range, englishFreqs } from './utils'; type HexToBuff = (hex: string) => Buffer; export const hexToBuff: HexToBuff = (hex) => Buffer.from(hex, 'hex'); type BuffTo64 = (buff: Buffer) => string; export const buffTo64: BuffTo64 = (buff) => buff.toString('base64'); type XorBuffers = (buff1: Buffer, buff2: Buffer) => Buffer; export const xorBuffers: XorBuffers = (buff1, buff2) => { const resBuff = Buffer.alloc(buff1.length); range(0, buff1.length).forEach(i => { resBuff[i] = buff1[i] ^ buff2[i]; }); return resBuff; }; type EncryptSingleByte = (msg: Buffer, key: number) => Buffer; export const encryptSingleByte: EncryptSingleByte = (msg, key) => { const resBuffer = Buffer.alloc(msg.length); range(0, msg.length).forEach(i => { resBuffer[i] = msg[i] ^ key; }); return resBuffer; }; type DecryptSingleByte = (msg: Buffer, key: number) => Buffer; export const decryptSingleByte: DecryptSingleByte = (msg, key) => { const resBuffer = Buffer.alloc(msg.length); range(0, msg.length).forEach(i => { resBuffer[i] = msg[i] ^ key; }); return resBuffer; }; type EnglishByteFreqs = () => { [id: number]: number }; const englishByteFreqs: EnglishByteFreqs = () => { const byteFreqs = {} as { [id: number]: number }; range(0, 255).forEach(i => byteFreqs[i] = 0); Object.keys(englishFreqs).forEach(k => byteFreqs[k] = englishFreqs[k]); return byteFreqs; }; type ByteFreqs = (buff: Buffer) => { [id: number]: number }; const byteFreqs: ByteFreqs = (buff) => { const freqs = {} as { [id: number]: number }; let n = 0; range(0, 255).forEach(i => freqs[i] = 0); buff.forEach(b => { freqs[b]++; n++; }); Object.keys(freqs).forEach(b => freqs[b] = freqs[b] / n); return freqs; }; type ScoreBuffer = (buff: Buffer) => number; export const scoreBuffer: ScoreBuffer = (buff) => { const msgLower = Buffer.from(buff.toString('utf-8').toLowerCase(), 'utf-8'); const engBFreqs = englishByteFreqs(); const msgFreq = byteFreqs(msgLower); let [cosSimilarity, normMsgFreq, normEngFreq] = [0, 0, 0]; range(0, 255).forEach(i => { cosSimilarity += msgFreq[i] * engBFreqs[i]; normMsgFreq += msgFreq[i] ** 2; normEngFreq += engBFreqs[i] ** 2; }); normMsgFreq = Math.sqrt(normMsgFreq); normEngFreq = Math.sqrt(normEngFreq); cosSimilarity /= normMsgFreq * normEngFreq; return cosSimilarity; }; type CrackSingleByteKeyMsg = (msg: string) => string; export const crackSingleByteKeyMsg: CrackSingleByteKeyMsg = (msg) => { const candidates = [] as [number, string][]; range(0, 255).forEach(i => { const decrypted = decryptSingleByte(Buffer.from(msg, 'hex'), i); candidates.push([scoreBuffer(decrypted), decrypted.toString('utf-8')]); }); candidates.sort((a, b) => b[0] - a[0]); return candidates[0][1]; }; type DetectSingleByteXor = (buffs: Buffer[]) => string; export const detectSingleByteXor: DetectSingleByteXor = (buffs) => { const candidates = [] as [number, string][]; buffs.slice(0, -1).forEach(buff => { range(0, 255).forEach(i => { const decrypted = decryptSingleByte(buff, i); candidates.push([scoreBuffer(decrypted), decrypted.toString('utf-8').trim()]); }); }); candidates.sort((a, b) => b[0] - a[0]); return candidates[0][1]; };