diff --git a/src/aead.js b/src/aead.js new file mode 100644 index 0000000..8a6d8b1 --- /dev/null +++ b/src/aead.js @@ -0,0 +1,115 @@ +const kdf = require('./kdf'); +const keccak = require('./keccak'); + +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 { + constructor(iv, key) { + const input = kdf.concatUint8Arrays(iv, key); + this.state = keccak.SHAKE256(input, 32); + } + + associated_data_processing(associated_data) { + const chunks = splitIntoChunks(associated_data); + let to_xor = null; + let c = null; + let r = null; + let input = null; + chunks.forEach((chunk) => { + to_xor = this.state.slice(0, 16); + c = this.state.slice(16, 32); + r = new Uint8Array(chunk.length); + for (let i = 0; i < chunk.length; i++) { + r[i] = chunk[i] ^ to_xor[i]; + } + input = kdf.concatUint8Arrays(c, r); + this.state = keccak.SHAKE256(input, 32); + }); + } + + plaintext_processing(plaintext) { + const chunks = splitIntoChunks(plaintext); + let to_xor = null; + let c = null; + let r = null; + let input = null; + let cipherchunks = []; + chunks.forEach((chunk) => { + to_xor = this.state.slice(0, 16); + c = this.state.slice(16, 32); + r = new Uint8Array(chunk.length); + for (let i = 0; i < chunk.length; i++) { + r[i] = chunk[i] ^ to_xor[i]; + } + cipherchunks.push(r); + input = kdf.concatUint8Arrays(c, r); + this.state = keccak.SHAKE256(input, 32); + }); + return cipherchunks; + } + + ciphertext_processing(ciphertext) { + const chunks = splitIntoChunks(ciphertext); + let to_xor = null; + let c = null; + let r = null; + let input = null; + let plaintextchunks = []; + chunks.forEach((chunk) => { + to_xor = this.state.slice(0, 16); + c = this.state.slice(16, 32); + r = new Uint8Array(chunk.length); + for (let i = 0; i < chunk.length; i++) { + r[i] = chunk[i] ^ to_xor[i]; + } + plaintextchunks.push(r); + input = kdf.concatUint8Arrays(c, chunk); + this.state = keccak.SHAKE256(input, 32); + }); + return plaintextchunks; + } + + finalize(key) { + const output = keccak.SHAKE256(this.state, 32); + let to_xor = output.slice(16, 32); + let tag = new Uint8Array(key.length); + for (let i = 0; i < key.length; i++) { + tag[i] = key[i] ^ to_xor[i]; + } + return tag; + } + + static encrypt(key, plaintext, iv, associated_data) { + let sponge = new keccakAEAD(iv, key); + sponge.associated_data_processing(associated_data); + let cipherChunks = sponge.plaintext_processing(plaintext); + let ciphertext = kdf.concatUint8Arrays(...cipherChunks); + let tag = sponge.finalize(key); + return { + cipher: ciphertext, + tag: tag + }; + } + + static decrypt(key, ciphertext, iv, associated_data) { + let sponge = new keccakAEAD(iv, key); + sponge.associated_data_processing(associated_data); + let plaintextChunks = sponge.ciphertext_processing(ciphertext); + let plaintext = kdf.concatUint8Arrays(...plaintextChunks); + let tag = sponge.finalize(key); + return { + plaintext: plaintext, + tag: tag + }; + } +} + +module.exports = { keccakAEAD }; diff --git a/src/kdf.js b/src/kdf.js index 41f9c91..24e381b 100644 --- a/src/kdf.js +++ b/src/kdf.js @@ -37,4 +37,4 @@ class keccakKDF { } -module.exports = { keccakKDF }; +module.exports = { keccakKDF, concatUint8Arrays }; diff --git a/tests/aead.test.js b/tests/aead.test.js new file mode 100644 index 0000000..9df5af7 --- /dev/null +++ b/tests/aead.test.js @@ -0,0 +1,25 @@ +const aead = require('../src/aead'); +const crypto = require('crypto'); + +function generateRandomUint8Array(length = 16) { + const randomArray = new Uint8Array(length); + crypto.getRandomValues(randomArray); + return randomArray; +} + +describe('aead.js functions', () => { + + it('encrypt and decrypt', () => { + let msg = generateRandomUint8Array(28); + console.log(msg); + let ad = generateRandomUint8Array(12); + let iv = generateRandomUint8Array(); + let key = generateRandomUint8Array(); + let result = aead.keccakAEAD.encrypt(key, msg, iv, ad); + console.log(result.tag); + console.log(result.cipher); + let result2 = aead.keccakAEAD.decrypt(key, result.cipher, iv, ad); + console.log(result2.tag); + console.log(result2.plaintext); + }); +});