Files
mrs/client/public/encryption.js
2026-04-06 15:17:25 -04:00

102 lines
2.8 KiB
JavaScript

const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const PHONETIC = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel',
'India', 'Juliet', 'Kilo', 'Lima', 'Mike', 'November', 'Oscar', 'Papa', 'Quebec', 'Romeo',
'Sierra', 'Tango', 'Uniform', 'Victor', 'Whiskey', 'Xray', 'Yankee', 'Zulu'];
class SeededRandom {
constructor(seed) {
this.seed = seed;
this.counter = 0;
}
next() {
const hash = CryptoJS.SHA256(this.seed + ':' + this.counter);
this.counter++;
const hex = hash.toString(CryptoJS.enc.Hex);
return parseInt(hex.substring(0, 8), 16) / 0xFFFFFFFF;
}
nextInt(min, max) {
return Math.floor(this.next() * (max - min)) + min;
}
choice(arr) {
return arr[this.nextInt(0, arr.length)];
}
}
function generateHOTP(secret, nonce) {
const rng = new SeededRandom(secret + ':hotp:' + nonce);
let code = '';
for (let i = 0; i < 2; i++) {
code += rng.choice(CHARS.split(''));
}
return code;
}
function generateAuthTable(seed, tableIndex) {
const rng = new SeededRandom(seed + ':table:' + tableIndex);
const rows = 'NOPQRSTUVWXYZ'.split('');
const cols = 'ABCDEFGHIJKLM'.split('');
const table = [];
for (let row of rows) {
const rowData = [];
for (let col of cols) {
rowData.push(rng.choice(CHARS.split('')));
}
table.push(rowData);
}
return table;
}
function generatePad(seed, padKey, length = 60) {
const rng = new SeededRandom(seed + ':pad:' + padKey);
const pad = [];
for (let i = 0; i < length; i++) {
pad.push(rng.nextInt(0, 1000));
}
return pad;
}
function generatePadDisplay(seed, padKey) {
const rng = new SeededRandom(seed + ':pad:' + padKey);
const groups = [];
for (let i = 0; i < 24; i++) {
const num = rng.nextInt(0, 1000).toString().padStart(3, '0');
groups.push(num);
}
return groups.join(' ');
}
function encodeMessage(message, key, padKey, useStream = true) {
const messageBytes = new TextEncoder().encode(message);
const padLength = useStream ? messageBytes.length : 60;
const pad = generatePad(key, padKey, padLength);
const encoded = [];
for (let i = 0; i < messageBytes.length; i++) {
const padValue = pad[i % pad.length];
const result = (messageBytes[i] + padValue) % 1000;
encoded.push(result.toString().padStart(3, '0'));
}
return encoded.join('');
}
function decodeMessage(encoded, key, padKey, useStream = true) {
const values = [];
for (let i = 0; i < encoded.length; i += 3) {
values.push(parseInt(encoded.substr(i, 3), 10));
}
const padLength = useStream ? values.length : 60;
const pad = generatePad(key, padKey, padLength);
const decoded = [];
for (let i = 0; i < values.length; i++) {
const padValue = pad[i % pad.length];
let result = (values[i] - padValue) % 1000;
if (result < 0) result += 1000;
decoded.push(result);
}
return new TextDecoder().decode(new Uint8Array(decoded));
}