move keccak related functions to front end

This commit is contained in:
Sam Hadow 2025-03-09 15:48:54 +01:00
parent edeb1b7c73
commit 35ff9cb240
8 changed files with 51 additions and 46 deletions

View File

@ -1,27 +1,17 @@
const kdf = require('./kdf'); import { concatUint8Arrays, splitIntoChunks } from './arrayutils.js';
const keccak = require('./keccak'); import { keccak } from './keccak.js';
function splitIntoChunks(data) {
const chunks = [];
const chunkSize = 16;
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
chunks.push(chunk);
}
return chunks;
}
class keccakAEAD { class keccakAEAD {
constructor(iv, key, nonce) { constructor(iv, key, nonce) {
const input = kdf.concatUint8Arrays(iv, key, nonce); const input = concatUint8Arrays(iv, key, nonce);
this.state = keccak.SHAKE256(input, 40); this.state = keccak.SHAKE256(input, 40);
let r = this.state.slice(0, 16); let r = this.state.slice(0, 16);
let c = this.state.slice(16, 40); let c = this.state.slice(16, 40);
let padded_key = kdf.concatUint8Arrays(new Uint8Array(24-key.length), key); let padded_key = concatUint8Arrays(new Uint8Array(24-key.length), key);
for (let i = 0; i < padded_key.length; i++) { for (let i = 0; i < padded_key.length; i++) {
c[i] ^= key[i]; c[i] ^= key[i];
} }
this.state = kdf.concatUint8Arrays(r, c); this.state = concatUint8Arrays(r, c);
} }
associated_data_processing(associated_data) { associated_data_processing(associated_data) {
@ -37,7 +27,7 @@ class keccakAEAD {
for (let i = 0; i < chunk.length; i++) { for (let i = 0; i < chunk.length; i++) {
r[i] = chunk[i] ^ to_xor[i]; r[i] = chunk[i] ^ to_xor[i];
} }
input = kdf.concatUint8Arrays(r, c); input = concatUint8Arrays(r, c);
this.state = keccak.SHAKE256(input, 40); this.state = keccak.SHAKE256(input, 40);
}); });
r = this.state.slice(0, 16); r = this.state.slice(0, 16);
@ -47,7 +37,7 @@ class keccakAEAD {
for (let i = 0; i < c.length; i++) { for (let i = 0; i < c.length; i++) {
c[i] ^= to_xor[i]; c[i] ^= to_xor[i];
} }
this.state = kdf.concatUint8Arrays(r, c); this.state = concatUint8Arrays(r, c);
} }
plaintext_processing(plaintext) { plaintext_processing(plaintext) {
@ -65,7 +55,7 @@ class keccakAEAD {
r[i] = chunk[i] ^ to_xor[i]; r[i] = chunk[i] ^ to_xor[i];
} }
cipherchunks.push(r); cipherchunks.push(r);
input = kdf.concatUint8Arrays(r, c); input = concatUint8Arrays(r, c);
this.state = keccak.SHAKE256(input, 40); this.state = keccak.SHAKE256(input, 40);
}); });
return cipherchunks; return cipherchunks;
@ -86,7 +76,7 @@ class keccakAEAD {
r[i] = chunk[i] ^ to_xor[i]; r[i] = chunk[i] ^ to_xor[i];
} }
plaintextchunks.push(r); plaintextchunks.push(r);
input = kdf.concatUint8Arrays(chunk, c); input = concatUint8Arrays(chunk, c);
this.state = keccak.SHAKE256(input, 40); this.state = keccak.SHAKE256(input, 40);
}); });
return plaintextchunks; return plaintextchunks;
@ -95,11 +85,11 @@ class keccakAEAD {
finalize(key) { finalize(key) {
let r = this.state.slice(0, 16); let r = this.state.slice(0, 16);
let c = this.state.slice(16, 40); let c = this.state.slice(16, 40);
let padded_key = kdf.concatUint8Arrays(new Uint8Array(24-key.length), key); let padded_key = concatUint8Arrays(new Uint8Array(24-key.length), key);
for (let i = 0; i < padded_key.length; i++) { for (let i = 0; i < padded_key.length; i++) {
c[i] ^= padded_key[i]; c[i] ^= padded_key[i];
} }
this.state = kdf.concatUint8Arrays(r, c); this.state = concatUint8Arrays(r, c);
const output = keccak.SHAKE256(this.state, 40); const output = keccak.SHAKE256(this.state, 40);
let to_xor = output.slice(40-key.length, 40); let to_xor = output.slice(40-key.length, 40);
let tag = new Uint8Array(key.length); let tag = new Uint8Array(key.length);
@ -113,7 +103,7 @@ class keccakAEAD {
let sponge = new keccakAEAD(iv, key, nonce); let sponge = new keccakAEAD(iv, key, nonce);
sponge.associated_data_processing(associated_data); sponge.associated_data_processing(associated_data);
let cipherChunks = sponge.plaintext_processing(plaintext); let cipherChunks = sponge.plaintext_processing(plaintext);
let ciphertext = kdf.concatUint8Arrays(...cipherChunks); let ciphertext = concatUint8Arrays(...cipherChunks);
let tag = sponge.finalize(key); let tag = sponge.finalize(key);
return { return {
cipher: ciphertext, cipher: ciphertext,
@ -125,7 +115,7 @@ class keccakAEAD {
let sponge = new keccakAEAD(iv, key, nonce); let sponge = new keccakAEAD(iv, key, nonce);
sponge.associated_data_processing(associated_data); sponge.associated_data_processing(associated_data);
let plaintextChunks = sponge.ciphertext_processing(ciphertext); let plaintextChunks = sponge.ciphertext_processing(ciphertext);
let plaintext = kdf.concatUint8Arrays(...plaintextChunks); let plaintext = concatUint8Arrays(...plaintextChunks);
let tag = sponge.finalize(key); let tag = sponge.finalize(key);
return { return {
plaintext: plaintext, plaintext: plaintext,
@ -134,4 +124,4 @@ class keccakAEAD {
} }
} }
module.exports = { keccakAEAD }; export { keccakAEAD };

22
src/public/arrayutils.js Normal file
View File

@ -0,0 +1,22 @@
function concatUint8Arrays(...arrays) {
let totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0);
let result = new Uint8Array(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
function splitIntoChunks(data) {
const chunks = [];
const chunkSize = 16;
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
chunks.push(chunk);
}
return chunks;
}
export { concatUint8Arrays, splitIntoChunks };

View File

@ -1,4 +1,6 @@
import { genKeys, sharedKey } from "./ecdh.js"; import { genKeys, sharedKey } from "./ecdh.js";
import { keccakAEAD } from "./aead.js";
import { keccakKDF } from "./kdf.js";
const socket = io(); const socket = io();
let secret = null; let secret = null;
@ -47,7 +49,7 @@ export function create_listener(form, input) {
} }
const fromHexString = (hexString) => const fromHexString = (hexString) =>
Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const toHexString = (bytes) => const toHexString = (bytes) =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');

View File

@ -1,16 +1,5 @@
const keccak = require('./keccak'); import { concatUint8Arrays } from './arrayutils.js';
import { keccak } from './keccak.js';
function concatUint8Arrays(...arrays) {
let totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0);
let result = new Uint8Array(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
class keccakKDF { class keccakKDF {
constructor(encryptionKeyLength = 32, privateKeyLength = 32) { constructor(encryptionKeyLength = 32, privateKeyLength = 32) {
@ -36,5 +25,4 @@ class keccakKDF {
} }
} }
export { keccakKDF };
module.exports = { keccakKDF, concatUint8Arrays };

View File

@ -168,4 +168,4 @@ const keccak = {
} }
} }
module.exports = keccak; export { keccak };

View File

@ -15,6 +15,9 @@ html(lang="en-US")
script(type="module", src="/rooms.js", defer) script(type="module", src="/rooms.js", defer)
script(type="module", src="/ecdh.js", defer) script(type="module", src="/ecdh.js", defer)
script(type="module", src="/popups-logged.js", defer) script(type="module", src="/popups-logged.js", defer)
script(type="module", src="/keccak.js", defer)
script(type="module", src="/aead.js", defer)
script(type="module", src="/kdf.js", defer)
else else
script(type="module", src="/popups.js", defer) script(type="module", src="/popups.js", defer)
script(type="module", src="/register.js", defer) script(type="module", src="/register.js", defer)

View File

@ -1,4 +1,4 @@
const aead = require('../src/aead'); import { keccakAEAD } from '../src/public/aead';
const crypto = require('crypto'); const crypto = require('crypto');
const stringutils = require('../src/stringutils'); const stringutils = require('../src/stringutils');
@ -17,10 +17,10 @@ describe('aead.js functions', () => {
let iv = generateRandomUint8Array(); let iv = generateRandomUint8Array();
let nonce = generateRandomUint8Array(); let nonce = generateRandomUint8Array();
let key = generateRandomUint8Array(); let key = generateRandomUint8Array();
let result = aead.keccakAEAD.encrypt(key, msg, iv, ad, nonce); let result = keccakAEAD.encrypt(key, msg, iv, ad, nonce);
let tag_encrypt_hex = stringutils.arrayToHex(result.tag); let tag_encrypt_hex = stringutils.arrayToHex(result.tag);
let cipher_hex = stringutils.arrayToHex(result.cipher); let cipher_hex = stringutils.arrayToHex(result.cipher);
let result2 = aead.keccakAEAD.decrypt(key, result.cipher, iv, ad, nonce); let result2 = keccakAEAD.decrypt(key, result.cipher, iv, ad, nonce);
let tag_decrypt_hex = stringutils.arrayToHex(result2.tag); let tag_decrypt_hex = stringutils.arrayToHex(result2.tag);
let decrypted_hex = stringutils.arrayToHex(result2.plaintext); let decrypted_hex = stringutils.arrayToHex(result2.plaintext);
expect(decrypted_hex).toBe(msg_hex); expect(decrypted_hex).toBe(msg_hex);

View File

@ -1,4 +1,4 @@
const keccak = require('../src/keccak'); import { keccak } from '../src/public/keccak';
const stringutils = require('../src/stringutils'); const stringutils = require('../src/stringutils');
describe('keccak.js functions', () => { describe('keccak.js functions', () => {