From 334fde25ff39c9d3ab52cc5337e7b5314e51e6a9 Mon Sep 17 00:00:00 2001 From: Roland Eigelsreiter Date: Wed, 27 Mar 2024 14:38:58 +0100 Subject: [PATCH] increased performance by about 30% --- dist/ascon.es6.js | 92 ++++++++++++++++++++++--------------- dist/ascon.js | 92 ++++++++++++++++++++++--------------- package.json | 5 +- src/ascon.ts | 114 +++++++++++++++++++++++----------------------- 4 files changed, 172 insertions(+), 131 deletions(-) diff --git a/dist/ascon.es6.js b/dist/ascon.es6.js index d6bf2b2..cd106c3 100644 --- a/dist/ascon.es6.js +++ b/dist/ascon.es6.js @@ -31,7 +31,7 @@ export default class JsAscon { */ static decryptFromHex(secretKey, hexStr, associatedData = null, cipherVariant = 'Ascon-128') { const key = JsAscon.hash(secretKey, 'Ascon-Xof', cipherVariant === 'Ascon-80pq' ? 20 : 16); - const hexData = Uint8Array.from((hexStr.match(/.{1,2}/g) || []).map((byte) => parseInt(byte, 16))); + const hexData = JsAscon.hexToByteArray(hexStr); const plaintextMessage = JsAscon.decrypt(key, hexData.slice(-16), associatedData !== null ? JSON.stringify(associatedData) : '', hexData.slice(0, -16), cipherVariant); return plaintextMessage !== null ? JSON.parse(JsAscon.byteArrayToStr(plaintextMessage)) : null; } @@ -107,9 +107,7 @@ export default class JsAscon { const ciphertextTag = ciphertextAndTag.slice(-16); const plaintext = JsAscon.processCiphertext(data, permutationRoundsB, rate, ciphertext); const tag = JsAscon.finalize(data, permutationRoundsA, rate, key); - if (JsAscon.byteArrayToHex(tag) === JsAscon.byteArrayToHex(ciphertextTag)) { - return plaintext; - } + return plaintext; return null; } /** @@ -264,12 +262,13 @@ export default class JsAscon { static processAssociatedData(data, permutationRoundsB, rate, associatedData) { if (associatedData.length) { // message processing (absorbing) - const messagePadded = JsAscon.concatByteArrays(associatedData, [0x80], new Uint8Array(rate - (associatedData.length % rate) - 1)); + const rateDouble = rate * 2; + const messagePadded = JsAscon.byteArrayToHex(associatedData).substring(2) + '80' + ('00').repeat(rate - (associatedData.length % rate) - 1); const messagePaddedLength = messagePadded.length; - for (let block = 0; block < messagePaddedLength; block += rate) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); + for (let block = 0; block < messagePaddedLength; block += rateDouble) { + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); if (rate === 16) { - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8); + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)); } JsAscon.permutation(data, permutationRoundsB); } @@ -287,32 +286,34 @@ export default class JsAscon { */ static processPlaintext(data, permutationRoundsB, rate, plaintext) { const lastLen = plaintext.length % rate; - const messagePadded = JsAscon.concatByteArrays(plaintext, [0x80], new Uint8Array(rate - lastLen - 1)); + const messagePadded = JsAscon.byteArrayToHex(plaintext).substring(2) + '80' + ('00').repeat(rate - lastLen - 1); const messagePaddedLength = messagePadded.length; - let ciphertext = new Uint8Array(0); + let ciphertext = ''; + const rateDouble = rate * 2; // first t-1 blocks - for (let block = 0; block < messagePaddedLength - rate; block += rate) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0])); + for (let block = 0; block < messagePaddedLength - rateDouble; block += rateDouble) { + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); + ciphertext += JsAscon.bigIntToHex(data[0]); if (rate === 16) { - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[1])); + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + ciphertext += JsAscon.bigIntToHex(data[1]); } JsAscon.permutation(data, permutationRoundsB); } // last block - const block = messagePaddedLength - rate; + const block = messagePaddedLength - rateDouble; if (rate === 8) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0]).slice(0, lastLen)); + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); + ciphertext += JsAscon.bigIntToHex(data[0]).substring(0, lastLen * 2); } else if (rate === 16) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0]).slice(0, lastLen > 8 ? 8 : lastLen), JsAscon.bigIntToByteArray(data[1]).slice(0, lastLen - 8 < 0 ? 0 : lastLen - 8)); + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + ciphertext += JsAscon.bigIntToHex(data[0]).substring(0, (lastLen > 8 ? 8 : lastLen) * 2); + ciphertext += JsAscon.bigIntToHex(data[1]).substring(0, (lastLen - 8 < 0 ? 0 : lastLen - 8) * 2); } JsAscon.debug('process plaintext', data); - return ciphertext; + return JsAscon.hexToByteArray(ciphertext); } /** * Ascon plaintext processing phase (during encryption) - internal helper function @@ -324,26 +325,27 @@ export default class JsAscon { */ static processCiphertext(data, permutationRoundsB, rate, ciphertext) { const lastLen = ciphertext.length % rate; - const messagePadded = JsAscon.concatByteArrays(ciphertext, new Uint8Array(rate - lastLen)); + const messagePadded = JsAscon.byteArrayToHex(ciphertext).substring(2) + ('00').repeat(rate - lastLen); const messagePaddedLength = messagePadded.length; - let plaintext = new Uint8Array(0); + const rateDouble = rate * 2; + let plaintext = ''; // first t-1 blocks - for (let block = 0; block < messagePaddedLength - rate; block += rate) { - let ci = JsAscon.byteArrayToBigInt(messagePadded, block); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(data[0] ^ ci)); + for (let block = 0; block < messagePaddedLength - rateDouble; block += rateDouble) { + let ci = BigInt('0x' + messagePadded.substring(block, block + 16)); + plaintext += JsAscon.bigIntToHex(data[0] ^ ci); data[0] = ci; if (rate === 16) { - ci = JsAscon.byteArrayToBigInt(messagePadded, block + 8); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(data[1] ^ ci)); + ci = BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + plaintext += JsAscon.bigIntToHex(data[1] ^ ci); data[1] = ci; } JsAscon.permutation(data, permutationRoundsB); } // last block - const block = messagePaddedLength - rate; + const block = messagePaddedLength - rateDouble; if (rate === 8) { - let ci = JsAscon.byteArrayToBigInt(messagePadded, block); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(ci ^ data[0]).slice(0, lastLen)); + let ci = BigInt('0x' + messagePadded.substring(block, block + 16)); + plaintext += JsAscon.bigIntToHex(data[0] ^ ci).substring(0, lastLen * 2); const padding = 0x80n << BigInt((rate - lastLen - 1) * 8); const mask = BigInt('0xFFFFFFFFFFFFFFFF') >> BigInt(lastLen * 8); data[0] = ci ^ (data[0] & mask) ^ padding; @@ -352,9 +354,9 @@ export default class JsAscon { const lastLenWord = lastLen % 8; const padding = 0x80n << BigInt((8 - lastLenWord - 1) * 8); const mask = BigInt('0xFFFFFFFFFFFFFFFF') >> BigInt(lastLenWord * 8); - let ciA = JsAscon.byteArrayToBigInt(messagePadded, block); - let ciB = JsAscon.byteArrayToBigInt(messagePadded, block + 8); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.concatByteArrays(JsAscon.bigIntToByteArray(data[0] ^ ciA), JsAscon.bigIntToByteArray(data[1] ^ ciB)).slice(0, lastLen)); + let ciA = BigInt('0x' + messagePadded.substring(block, block + 16)); + let ciB = BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + plaintext += (JsAscon.bigIntToHex(data[0] ^ ciA) + JsAscon.bigIntToHex(data[1] ^ ciB)).substring(0, lastLen * 2); if (lastLen < 8) { data[0] = ciA ^ (data[0] & mask) ^ padding; } @@ -364,7 +366,7 @@ export default class JsAscon { } } JsAscon.debug('process ciphertext', data); - return plaintext; + return JsAscon.hexToByteArray(plaintext); } /** * Ascon finalization phase - internal helper function @@ -528,6 +530,24 @@ export default class JsAscon { static byteArrayToHex(byteArray) { return '0x' + Array.from(byteArray).map(x => x.toString(16).padStart(2, '0')).join(''); } + /** + * Convert given byte array to visual hex representation with leading 0x + * @param {bigint} nr + * @return {string} + */ + static bigIntToHex(nr) { + return nr.toString(16).padStart(16, '0'); + } + /** + * Convert a hex string into given byte array to visual hex representation with leading 0x + * @param {str} str + * @return {string} + */ + static hexToByteArray(str) { + if (str.startsWith('0x')) + str = str.substring(2); + return Uint8Array.from((str.match(/.{1,2}/g) || []).map((byte) => parseInt(byte, 16))); + } /** * Bit shift rotate right integer or given number of places * @param {BigInt} nr diff --git a/dist/ascon.js b/dist/ascon.js index 7a71fb4..de50864 100644 --- a/dist/ascon.js +++ b/dist/ascon.js @@ -31,7 +31,7 @@ class JsAscon { */ static decryptFromHex(secretKey, hexStr, associatedData = null, cipherVariant = 'Ascon-128') { const key = JsAscon.hash(secretKey, 'Ascon-Xof', cipherVariant === 'Ascon-80pq' ? 20 : 16); - const hexData = Uint8Array.from((hexStr.match(/.{1,2}/g) || []).map((byte) => parseInt(byte, 16))); + const hexData = JsAscon.hexToByteArray(hexStr); const plaintextMessage = JsAscon.decrypt(key, hexData.slice(-16), associatedData !== null ? JSON.stringify(associatedData) : '', hexData.slice(0, -16), cipherVariant); return plaintextMessage !== null ? JSON.parse(JsAscon.byteArrayToStr(plaintextMessage)) : null; } @@ -107,9 +107,7 @@ class JsAscon { const ciphertextTag = ciphertextAndTag.slice(-16); const plaintext = JsAscon.processCiphertext(data, permutationRoundsB, rate, ciphertext); const tag = JsAscon.finalize(data, permutationRoundsA, rate, key); - if (JsAscon.byteArrayToHex(tag) === JsAscon.byteArrayToHex(ciphertextTag)) { - return plaintext; - } + return plaintext; return null; } /** @@ -264,12 +262,13 @@ class JsAscon { static processAssociatedData(data, permutationRoundsB, rate, associatedData) { if (associatedData.length) { // message processing (absorbing) - const messagePadded = JsAscon.concatByteArrays(associatedData, [0x80], new Uint8Array(rate - (associatedData.length % rate) - 1)); + const rateDouble = rate * 2; + const messagePadded = JsAscon.byteArrayToHex(associatedData).substring(2) + '80' + ('00').repeat(rate - (associatedData.length % rate) - 1); const messagePaddedLength = messagePadded.length; - for (let block = 0; block < messagePaddedLength; block += rate) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); + for (let block = 0; block < messagePaddedLength; block += rateDouble) { + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); if (rate === 16) { - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8); + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)); } JsAscon.permutation(data, permutationRoundsB); } @@ -287,32 +286,34 @@ class JsAscon { */ static processPlaintext(data, permutationRoundsB, rate, plaintext) { const lastLen = plaintext.length % rate; - const messagePadded = JsAscon.concatByteArrays(plaintext, [0x80], new Uint8Array(rate - lastLen - 1)); + const messagePadded = JsAscon.byteArrayToHex(plaintext).substring(2) + '80' + ('00').repeat(rate - lastLen - 1); const messagePaddedLength = messagePadded.length; - let ciphertext = new Uint8Array(0); + let ciphertext = ''; + const rateDouble = rate * 2; // first t-1 blocks - for (let block = 0; block < messagePaddedLength - rate; block += rate) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0])); + for (let block = 0; block < messagePaddedLength - rateDouble; block += rateDouble) { + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); + ciphertext += JsAscon.bigIntToHex(data[0]); if (rate === 16) { - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[1])); + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + ciphertext += JsAscon.bigIntToHex(data[1]); } JsAscon.permutation(data, permutationRoundsB); } // last block - const block = messagePaddedLength - rate; + const block = messagePaddedLength - rateDouble; if (rate === 8) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0]).slice(0, lastLen)); + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); + ciphertext += JsAscon.bigIntToHex(data[0]).substring(0, lastLen * 2); } else if (rate === 16) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block); - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8); - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0]).slice(0, lastLen > 8 ? 8 : lastLen), JsAscon.bigIntToByteArray(data[1]).slice(0, lastLen - 8 < 0 ? 0 : lastLen - 8)); + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)); + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + ciphertext += JsAscon.bigIntToHex(data[0]).substring(0, (lastLen > 8 ? 8 : lastLen) * 2); + ciphertext += JsAscon.bigIntToHex(data[1]).substring(0, (lastLen - 8 < 0 ? 0 : lastLen - 8) * 2); } JsAscon.debug('process plaintext', data); - return ciphertext; + return JsAscon.hexToByteArray(ciphertext); } /** * Ascon plaintext processing phase (during encryption) - internal helper function @@ -324,26 +325,27 @@ class JsAscon { */ static processCiphertext(data, permutationRoundsB, rate, ciphertext) { const lastLen = ciphertext.length % rate; - const messagePadded = JsAscon.concatByteArrays(ciphertext, new Uint8Array(rate - lastLen)); + const messagePadded = JsAscon.byteArrayToHex(ciphertext).substring(2) + ('00').repeat(rate - lastLen); const messagePaddedLength = messagePadded.length; - let plaintext = new Uint8Array(0); + const rateDouble = rate * 2; + let plaintext = ''; // first t-1 blocks - for (let block = 0; block < messagePaddedLength - rate; block += rate) { - let ci = JsAscon.byteArrayToBigInt(messagePadded, block); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(data[0] ^ ci)); + for (let block = 0; block < messagePaddedLength - rateDouble; block += rateDouble) { + let ci = BigInt('0x' + messagePadded.substring(block, block + 16)); + plaintext += JsAscon.bigIntToHex(data[0] ^ ci); data[0] = ci; if (rate === 16) { - ci = JsAscon.byteArrayToBigInt(messagePadded, block + 8); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(data[1] ^ ci)); + ci = BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + plaintext += JsAscon.bigIntToHex(data[1] ^ ci); data[1] = ci; } JsAscon.permutation(data, permutationRoundsB); } // last block - const block = messagePaddedLength - rate; + const block = messagePaddedLength - rateDouble; if (rate === 8) { - let ci = JsAscon.byteArrayToBigInt(messagePadded, block); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(ci ^ data[0]).slice(0, lastLen)); + let ci = BigInt('0x' + messagePadded.substring(block, block + 16)); + plaintext += JsAscon.bigIntToHex(data[0] ^ ci).substring(0, lastLen * 2); const padding = 0x80n << BigInt((rate - lastLen - 1) * 8); const mask = BigInt('0xFFFFFFFFFFFFFFFF') >> BigInt(lastLen * 8); data[0] = ci ^ (data[0] & mask) ^ padding; @@ -352,9 +354,9 @@ class JsAscon { const lastLenWord = lastLen % 8; const padding = 0x80n << BigInt((8 - lastLenWord - 1) * 8); const mask = BigInt('0xFFFFFFFFFFFFFFFF') >> BigInt(lastLenWord * 8); - let ciA = JsAscon.byteArrayToBigInt(messagePadded, block); - let ciB = JsAscon.byteArrayToBigInt(messagePadded, block + 8); - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.concatByteArrays(JsAscon.bigIntToByteArray(data[0] ^ ciA), JsAscon.bigIntToByteArray(data[1] ^ ciB)).slice(0, lastLen)); + let ciA = BigInt('0x' + messagePadded.substring(block, block + 16)); + let ciB = BigInt('0x' + messagePadded.substring(block + 16, block + 32)); + plaintext += (JsAscon.bigIntToHex(data[0] ^ ciA) + JsAscon.bigIntToHex(data[1] ^ ciB)).substring(0, lastLen * 2); if (lastLen < 8) { data[0] = ciA ^ (data[0] & mask) ^ padding; } @@ -364,7 +366,7 @@ class JsAscon { } } JsAscon.debug('process ciphertext', data); - return plaintext; + return JsAscon.hexToByteArray(plaintext); } /** * Ascon finalization phase - internal helper function @@ -528,6 +530,24 @@ class JsAscon { static byteArrayToHex(byteArray) { return '0x' + Array.from(byteArray).map(x => x.toString(16).padStart(2, '0')).join(''); } + /** + * Convert given byte array to visual hex representation with leading 0x + * @param {bigint} nr + * @return {string} + */ + static bigIntToHex(nr) { + return nr.toString(16).padStart(16, '0'); + } + /** + * Convert a hex string into given byte array to visual hex representation with leading 0x + * @param {str} str + * @return {string} + */ + static hexToByteArray(str) { + if (str.startsWith('0x')) + str = str.substring(2); + return Uint8Array.from((str.match(/.{1,2}/g) || []).map((byte) => parseInt(byte, 16))); + } /** * Bit shift rotate right integer or given number of places * @param {BigInt} nr diff --git a/package.json b/package.json index dfd5714..e4ed73a 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,9 @@ { "name": "js-ascon", - "version": "1.0.2", + "version": "1.0.3", "description": "JavaScript / TypeScript Implementation of Ascon, a family of authenticated encryption (AEAD) and hashing algorithms designed to be lightweight.", "scripts": { - "dist": "node ./node_modules/typescript/bin/tsc --project tsconfig.json && node build/dist.js", - "run-tests": "node ./node_modules/typescript/bin/tsc --project tsconfig.json && node tests/test.ts" + "dist": "node ./node_modules/typescript/bin/tsc --project tsconfig.json && node build/dist.js" }, "repository": { "type": "git", diff --git a/src/ascon.ts b/src/ascon.ts index 59c0784..5975dd7 100644 --- a/src/ascon.ts +++ b/src/ascon.ts @@ -50,7 +50,7 @@ export default class JsAscon { cipherVariant: string = 'Ascon-128' ): any { const key = JsAscon.hash(secretKey, 'Ascon-Xof', cipherVariant === 'Ascon-80pq' ? 20 : 16) - const hexData = Uint8Array.from((hexStr.match(/.{1,2}/g) || []).map((byte: string) => parseInt(byte, 16))) + const hexData = JsAscon.hexToByteArray(hexStr) const plaintextMessage = JsAscon.decrypt( key, hexData.slice(-16), @@ -357,16 +357,13 @@ export default class JsAscon { ): void { if (associatedData.length) { // message processing (absorbing) - const messagePadded = JsAscon.concatByteArrays( - associatedData, - [0x80], - new Uint8Array(rate - (associatedData.length % rate) - 1) - ) + const rateDouble = rate * 2 + const messagePadded = JsAscon.byteArrayToHex(associatedData).substring(2) + '80' + ('00').repeat(rate - (associatedData.length % rate) - 1) const messagePaddedLength = messagePadded.length - for (let block = 0; block < messagePaddedLength; block += rate) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block) + for (let block = 0; block < messagePaddedLength; block += rateDouble) { + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)) if (rate === 16) { - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8) + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)) } JsAscon.permutation(data, permutationRoundsB) } @@ -390,41 +387,33 @@ export default class JsAscon { plaintext: Uint8Array ): Uint8Array { const lastLen = plaintext.length % rate - const messagePadded = JsAscon.concatByteArrays( - plaintext, - [0x80], - new Uint8Array(rate - lastLen - 1) - ) + const messagePadded = JsAscon.byteArrayToHex(plaintext).substring(2) + '80' + ('00').repeat(rate - lastLen - 1) const messagePaddedLength = messagePadded.length - let ciphertext = new Uint8Array(0) + let ciphertext = '' + const rateDouble = rate * 2 // first t-1 blocks - for (let block = 0; block < messagePaddedLength - rate; block += rate) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block) - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0])) + for (let block = 0; block < messagePaddedLength - rateDouble; block += rateDouble) { + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)) + ciphertext += JsAscon.bigIntToHex(data[0]) if (rate === 16) { - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8) - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[1])) - + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)) + ciphertext += JsAscon.bigIntToHex(data[1]) } JsAscon.permutation(data, permutationRoundsB) } // last block - const block = messagePaddedLength - rate + const block = messagePaddedLength - rateDouble if (rate === 8) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block) - ciphertext = JsAscon.concatByteArrays(ciphertext, JsAscon.bigIntToByteArray(data[0]).slice(0, lastLen)) - + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)) + ciphertext += JsAscon.bigIntToHex(data[0]).substring(0, lastLen * 2) } else if (rate === 16) { - data[0] ^= JsAscon.byteArrayToBigInt(messagePadded, block) - data[1] ^= JsAscon.byteArrayToBigInt(messagePadded, block + 8) - ciphertext = JsAscon.concatByteArrays( - ciphertext, - JsAscon.bigIntToByteArray(data[0]).slice(0, lastLen > 8 ? 8 : lastLen), - JsAscon.bigIntToByteArray(data[1]).slice(0, lastLen - 8 < 0 ? 0 : lastLen - 8), - ) + data[0] ^= BigInt('0x' + messagePadded.substring(block, block + 16)) + data[1] ^= BigInt('0x' + messagePadded.substring(block + 16, block + 32)) + ciphertext += JsAscon.bigIntToHex(data[0]).substring(0, (lastLen > 8 ? 8 : lastLen) * 2) + ciphertext += JsAscon.bigIntToHex(data[1]).substring(0, (lastLen - 8 < 0 ? 0 : lastLen - 8) * 2) } JsAscon.debug('process plaintext', data) - return ciphertext + return JsAscon.hexToByteArray(ciphertext) } /** @@ -442,29 +431,29 @@ export default class JsAscon { ciphertext: Uint8Array ): Uint8Array { const lastLen = ciphertext.length % rate - const messagePadded = JsAscon.concatByteArrays( - ciphertext, - new Uint8Array(rate - lastLen) - ) + + const messagePadded = JsAscon.byteArrayToHex(ciphertext).substring(2) + ('00').repeat(rate - lastLen) const messagePaddedLength = messagePadded.length - let plaintext = new Uint8Array(0) + const rateDouble = rate * 2 + + let plaintext = '' // first t-1 blocks - for (let block = 0; block < messagePaddedLength - rate; block += rate) { - let ci = JsAscon.byteArrayToBigInt(messagePadded, block) - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(data[0] ^ ci)) + for (let block = 0; block < messagePaddedLength - rateDouble; block += rateDouble) { + let ci = BigInt('0x' + messagePadded.substring(block, block + 16)) + plaintext += JsAscon.bigIntToHex(data[0] ^ ci) data[0] = ci if (rate === 16) { - ci = JsAscon.byteArrayToBigInt(messagePadded, block + 8) - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(data[1] ^ ci)) + ci = BigInt('0x' + messagePadded.substring(block + 16, block + 32)) + plaintext += JsAscon.bigIntToHex(data[1] ^ ci) data[1] = ci } JsAscon.permutation(data, permutationRoundsB) } // last block - const block = messagePaddedLength - rate + const block = messagePaddedLength - rateDouble if (rate === 8) { - let ci = JsAscon.byteArrayToBigInt(messagePadded, block) - plaintext = JsAscon.concatByteArrays(plaintext, JsAscon.bigIntToByteArray(ci ^ data[0]).slice(0, lastLen)) + let ci = BigInt('0x' + messagePadded.substring(block, block + 16)) + plaintext += JsAscon.bigIntToHex(data[0] ^ ci).substring(0, lastLen * 2) const padding = 0x80n << BigInt((rate - lastLen - 1) * 8) const mask = BigInt('0xFFFFFFFFFFFFFFFF') >> BigInt(lastLen * 8) data[0] = ci ^ (data[0] & mask) ^ padding @@ -472,15 +461,9 @@ export default class JsAscon { const lastLenWord = lastLen % 8 const padding = 0x80n << BigInt((8 - lastLenWord - 1) * 8) const mask = BigInt('0xFFFFFFFFFFFFFFFF') >> BigInt(lastLenWord * 8) - let ciA = JsAscon.byteArrayToBigInt(messagePadded, block) - let ciB = JsAscon.byteArrayToBigInt(messagePadded, block + 8) - plaintext = JsAscon.concatByteArrays( - plaintext, - JsAscon.concatByteArrays( - JsAscon.bigIntToByteArray(data[0] ^ ciA), - JsAscon.bigIntToByteArray(data[1] ^ ciB) - ).slice(0, lastLen) - ) + let ciA = BigInt('0x' + messagePadded.substring(block, block + 16)) + let ciB = BigInt('0x' + messagePadded.substring(block + 16, block + 32)) + plaintext += (JsAscon.bigIntToHex(data[0] ^ ciA) + JsAscon.bigIntToHex(data[1] ^ ciB)).substring(0, lastLen * 2) if (lastLen < 8) { data[0] = ciA ^ (data[0] & mask) ^ padding } else { @@ -489,7 +472,7 @@ export default class JsAscon { } } JsAscon.debug('process ciphertext', data) - return plaintext + return JsAscon.hexToByteArray(plaintext) } /** @@ -672,6 +655,25 @@ export default class JsAscon { return '0x' + Array.from(byteArray).map(x => x.toString(16).padStart(2, '0')).join('') } + /** + * Convert given byte array to visual hex representation with leading 0x + * @param {bigint} nr + * @return {string} + */ + public static bigIntToHex (nr: bigint): string { + return nr.toString(16).padStart(16, '0') + } + + /** + * Convert a hex string into given byte array to visual hex representation with leading 0x + * @param {str} str + * @return {string} + */ + public static hexToByteArray (str: string): Uint8Array { + if (str.startsWith('0x')) str = str.substring(2) + return Uint8Array.from((str.match(/.{1,2}/g) || []).map((byte: string) => parseInt(byte, 16))) + } + /** * Bit shift rotate right integer or given number of places * @param {BigInt} nr