Refactor to factor out random and string scrambling functions.
rodič
d615c5f93e
revize
c14dd920b4
@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
/* Utilities for random numbers. */
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
}
|
||||
function getRandomLetter() {
|
||||
return (10 + getRandomInt(26)).toString(36);
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/a/2450976
|
||||
function shuffle(array) {
|
||||
let currentIndex = array.length, temporaryValue, randomIndex;
|
||||
|
||||
// While there remain elements to shuffle...
|
||||
while (0 !== currentIndex) {
|
||||
|
||||
// Pick a remaining element...
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex -= 1;
|
||||
|
||||
// And swap it with the current element.
|
||||
temporaryValue = array[currentIndex];
|
||||
array[currentIndex] = array[randomIndex];
|
||||
array[randomIndex] = temporaryValue;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
'use strict';
|
||||
|
||||
/* Utilities for scrambling strings to obscure puzzle answers. Not
|
||||
* intended to be secure. */
|
||||
|
||||
// From https://stackoverflow.com/a/34310051
|
||||
function toHexString(buffer) {
|
||||
let byteArray = new Uint8Array(buffer);
|
||||
return Array.from(byteArray, function(byte) {
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
function fromHexString(str) {
|
||||
let buffer = new ArrayBuffer(str.length / 2);
|
||||
let byteArray = new Uint8Array(buffer);
|
||||
let i = 0;
|
||||
for (let idx = 0; idx < str.length; idx += 2) {
|
||||
let hex = str.substring(idx, idx+2);
|
||||
byteArray[i++] = parseInt(hex, 16);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// "Encrypt" a string and include the key just as a way of scrambling it
|
||||
// to make it hard to read for a human.
|
||||
async function getDefaultKeyAndIv() {
|
||||
let key = await window.crypto.subtle.importKey('raw', new ArrayBuffer(32),
|
||||
{ name: "AES-GCM" }, false, ['encrypt', 'decrypt']);
|
||||
let iv = new Uint8Array(12);
|
||||
return [key, iv];
|
||||
}
|
||||
async function encryptStringNoKey(plaintext) {
|
||||
let encoded = new TextEncoder().encode(plaintext);
|
||||
let [key, iv] = await getDefaultKeyAndIv();
|
||||
let ciphertext = await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv
|
||||
},
|
||||
key,
|
||||
encoded
|
||||
);
|
||||
return toHexString(ciphertext);
|
||||
}
|
||||
async function decryptStringNoKey(ciphertextStr) {
|
||||
let [key, iv] = await getDefaultKeyAndIv();
|
||||
let ciphertext = fromHexString(ciphertextStr);
|
||||
|
||||
let decrypted = await window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv
|
||||
},
|
||||
key,
|
||||
ciphertext
|
||||
);
|
||||
let plaintext = new TextDecoder().decode(decrypted);
|
||||
return plaintext;
|
||||
}
|
||||
async function encryptString(plaintext) {
|
||||
let encoded = new TextEncoder().encode(plaintext);
|
||||
let key = await window.crypto.subtle.generateKey(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
length: 256,
|
||||
},
|
||||
true,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
let iv = await window.crypto.getRandomValues(new Uint8Array(12));
|
||||
let ciphertext = await window.crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv
|
||||
},
|
||||
key,
|
||||
encoded
|
||||
);
|
||||
let keyBuffer = await window.crypto.subtle.exportKey('raw', key);
|
||||
return toHexString(keyBuffer) + ','
|
||||
+ toHexString(iv) + ','
|
||||
+ toHexString(ciphertext);
|
||||
}
|
||||
async function decryptString(str) {
|
||||
let [keyStr, ivStr, ciphertextStr] = str.split(',');
|
||||
let keyBuffer = fromHexString(keyStr);
|
||||
let iv = fromHexString(ivStr);
|
||||
let ciphertext = fromHexString(ciphertextStr);
|
||||
|
||||
let key = await window.crypto.subtle.importKey('raw', keyBuffer,
|
||||
{ name: "AES-GCM" }, false, ['decrypt']);
|
||||
let decrypted = await window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv
|
||||
},
|
||||
key,
|
||||
ciphertext
|
||||
);
|
||||
let plaintext = new TextDecoder().decode(decrypted);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
async function _scrambleString(str, kind) {
|
||||
if (kind[0] == 'B') {
|
||||
return 'B' + btoa(await _scrambleString(str, kind.substring(1)));
|
||||
} else if (kind[0] == 'E') {
|
||||
return 'E' + await encryptString(
|
||||
await _scrambleString(str, kind.substring(1)));
|
||||
} else if (kind[0] == 'A') {
|
||||
return 'A' + await encryptStringNoKey(
|
||||
await _scrambleString(str, kind.substring(1)));
|
||||
} else if(!kind) {
|
||||
return str;
|
||||
} else {
|
||||
throw new Exception("Unknown scramble kind: " + kind);
|
||||
}
|
||||
}
|
||||
function scrambleString(str, scrambleKinds) {
|
||||
let res = null;
|
||||
for (let i in scrambleKinds) {
|
||||
let kind = scrambleKinds[i];
|
||||
if (res === null) {
|
||||
res = _scrambleString(str, kind);
|
||||
} else {
|
||||
res = res.catch(_ => _scrambleString(str, kind));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
async function unscrambleString(str) {
|
||||
try {
|
||||
if (str[0] == 'B') {
|
||||
return await unscrambleString(atob(str.substring(1)));
|
||||
} else if (str[0] == 'E') {
|
||||
return await unscrambleString(
|
||||
await decryptString(str.substring(1)));
|
||||
} else if (str[0] == 'A') {
|
||||
return await unscrambleString(
|
||||
await decryptStringNoKey(str.substring(1)));
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
return null;
|
||||
}
|
||||
}
|
Načítá se…
Odkázat v novém úkolu