124 lines
3.9 KiB
TypeScript
124 lines
3.9 KiB
TypeScript
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];
|
|
};
|
|
|
|
type EncryptRepeatingXOR = (buff: Buffer, key: Buffer) => Buffer;
|
|
export const encryptRepeatingXOR: EncryptRepeatingXOR = (buff, key) => {
|
|
const resBuff = Buffer.alloc(buff.length);
|
|
range(0, buff.length).forEach(i => {
|
|
resBuff[i] = buff[i] ^ key[i % key.length];
|
|
});
|
|
return resBuff;
|
|
};
|
|
|
|
type DecryptRepeatingXOR = (buff: Buffer, key: Buffer) => Buffer;
|
|
export const decryptRepeatingXOR: DecryptRepeatingXOR = (buff, key) => {
|
|
const resBuff = Buffer.alloc(buff.length);
|
|
range(0, buff.length).forEach(i => {
|
|
resBuff[i] = buff[i] ^ key[i % key.length];
|
|
});
|
|
return resBuff;
|
|
};
|