Files
inzerosight/dash.js

129 lines
4.9 KiB
JavaScript

import * as chunked from './chunked.js';
import * as speck48_96ctr from './speck48_96ctr.js';
import * as speck32_64ecb from './speck32_64ecb.js';
import { makeSig, parseSig } from './sig.js';
const textarea = document.getElementById('textarea');
const encoderDropdown = document.getElementById('encoder');
const cipherDropdown = document.getElementById('cipher');
const signBtn = document.getElementById('sign');
const sigDetect = document.getElementById('sigDetect');
const notice = document.getElementById('notice');
const buttons = ['encodeButton', 'decodeButton'].map(id => document.getElementById(id));
const controls = [...buttons, encoderDropdown, cipherDropdown, signBtn];
document.getElementById('encodeButton').addEventListener('click', ACT);
document.getElementById('decodeButton').addEventListener('click', ACT);
signBtn.addEventListener('click', e =>
e.target.classList.toggle('on')
);
let fadeTimer, busy = false;
async function ACT(event) {
if (busy) return;
clearTimeout(fadeTimer);
sigDetect.className = '';
if (textarea.value === '') {
textarea.value = 'The text box is empty.';
return;
}
const op = event.target.id === 'encodeButton' ? 'NO' : 'YES';
let cipher = getCipherKey();
let base = encoderDropdown.value.split('-')[1];
let text = textarea.value;
if (op === 'YES') {
const parsed = parseSig(text);
if (parsed) {
if (parsed.base !== base || (parsed.cipher && parsed.cipher !== cipher)) {
const desc = parsed.cipher && parsed.cipher !== 'PLAIN' ? ` (${parsed.cipher})` : '';
sigDetect.textContent = `ZWUS-${parsed.base}${desc} signature detected`;
sigDetect.className = 'show';
fadeTimer = setTimeout(() =>
sigDetect.className = '', 2000
);
}
base = parsed.base;
encoderDropdown.value = 'ZWUS-' + base;
if (parsed.cipher) {
cipher = parsed.cipher;
cipherDropdown.value = cipher;
}
text = text.slice(0, parsed.sigIdx) + parsed.payload;
}
}
const needsKey = cipher !== 'PLAIN';
const kStr = needsKey && prompt('enter password.');
if (needsKey && !kStr) return;
busy = true;
controls.forEach(control => control.disabled = true);
notice.textContent = 'Processing…';
try {
let val = await DESCRY[op][cipher](text, base, kStr);
if (op === 'NO' && signBtn.classList.contains('on'))
val = makeSig(base, cipher) + val;
textarea.value = val;
if (op === 'NO') {
notice.textContent = 'Copying…';
const copied = await copyText(val);
if (copied && val.length <= 65536)
textarea.value = 'Copied to your clipboard.\n A copy has been placed between these brackets [' + val + ']';
notice.textContent = copied ? `Copied ${val.length.toLocaleString()} characters.` :
'Copy failed. The encoded text is in the box; select and copy it manually.';
} else notice.textContent = `Decoded ${val.length.toLocaleString()} characters.`;
} catch (e) {
console.error(e);
notice.textContent = `Could not ${op === 'NO' ? 'encode' : 'decode'}: ${e.message}`;
} finally {
busy = false;
controls.forEach(control => control.disabled = false);
}
}
async function copyText(val) {
if (navigator.clipboard?.writeText) {
let timer;
try {
await Promise.race([
navigator.clipboard.writeText(val),
new Promise((_, reject) => timer = setTimeout(() => reject(new Error('Copy timed out')), 5000))
]);
return true;
} catch (e) { console.warn('Clipboard copy failed', e); }
finally { clearTimeout(timer); }
}
if (val.length > 65536) return false;
textarea.select();
try { return document.execCommand('copy'); }
catch (e) { console.warn('Clipboard copy failed', e); return false; }
}
function getCipherKey() {
return cipherDropdown.value;
}
const DESCRY = {
NO: {
PLAIN: (ptStr, base) =>
chunked.encodeString(ptStr, base),
SPECK48_96CTR: (ptStr, base, kStr) =>
chunked.encodeNumberArray(speck48_96ctr.encrypt(ptStr, speck48_96ctr.getKey(kStr)), base),
'SPECK32_64ECB (insecure)': (ptStr, base, kStr) =>
chunked.encodeNumberArray(speck32_64ecb.encrypt(ptStr, speck32_64ecb.getKey(kStr)), base),
},
YES: {
PLAIN: (ptStr, base) =>
chunked.decodeToString(ptStr, base),
SPECK48_96CTR: async (ptStr, base, kStr) =>
speck48_96ctr.decrypt(await chunked.decodeToNumberArray(ptStr, base), speck48_96ctr.getKey(kStr)),
'SPECK32_64ECB (insecure)': async (ptStr, base, kStr) =>
speck32_64ecb.decrypt(await chunked.decodeToNumberArray(ptStr, base), speck32_64ecb.getKey(kStr)),
}
};