crypto_pals/basics.ts

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;
};