mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-11-24 22:45:48 +00:00
Drop asmcrypto.js for AES_CFB in favor of noble-ciphers
Asm.js has now been deprecated for many years, and no performance gain is recorded for AES.
This commit is contained in:
parent
efb0324330
commit
e8de7d76db
16
package-lock.json
generated
16
package-lock.json
generated
@ -9,6 +9,7 @@
|
|||||||
"version": "6.0.0-beta.2",
|
"version": "6.0.0-beta.2",
|
||||||
"license": "LGPL-3.0+",
|
"license": "LGPL-3.0+",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@noble/ciphers": "^0.6.0",
|
||||||
"@noble/curves": "^1.4.0",
|
"@noble/curves": "^1.4.0",
|
||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@openpgp/asmcrypto.js": "^3.1.0",
|
"@openpgp/asmcrypto.js": "^3.1.0",
|
||||||
@ -801,6 +802,15 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noble/ciphers": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@noble/curves": {
|
"node_modules/@noble/curves": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz",
|
||||||
@ -9041,6 +9051,12 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@noble/ciphers": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@noble/curves": {
|
"@noble/curves": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz",
|
||||||
|
|||||||
@ -62,6 +62,7 @@
|
|||||||
"postversion": "git push && git push --tags && npm publish"
|
"postversion": "git push && git push --tags && npm publish"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@noble/ciphers": "^0.6.0",
|
||||||
"@noble/curves": "^1.4.0",
|
"@noble/curves": "^1.4.0",
|
||||||
"@noble/hashes": "^1.4.0",
|
"@noble/hashes": "^1.4.0",
|
||||||
"@openpgp/asmcrypto.js": "^3.1.0",
|
"@openpgp/asmcrypto.js": "^3.1.0",
|
||||||
|
|||||||
@ -21,7 +21,8 @@
|
|||||||
* @module crypto/mode/cfb
|
* @module crypto/mode/cfb
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AES_CFB } from '@openpgp/asmcrypto.js/aes/cfb.js';
|
import { cfb as nobleAesCfb, unsafe as nobleAesHelpers } from '@noble/ciphers/aes';
|
||||||
|
|
||||||
import * as stream from '@openpgp/web-stream-tools';
|
import * as stream from '@openpgp/web-stream-tools';
|
||||||
import util from '../../util';
|
import util from '../../util';
|
||||||
import enums from '../../enums';
|
import enums from '../../enums';
|
||||||
@ -174,17 +175,17 @@ class WebCryptoEncryptor {
|
|||||||
|
|
||||||
const encryptedBlocks = await this._runCBC(toEncrypt);
|
const encryptedBlocks = await this._runCBC(toEncrypt);
|
||||||
xorMut(encryptedBlocks, plaintext);
|
xorMut(encryptedBlocks, plaintext);
|
||||||
this.prevBlock = encryptedBlocks.subarray(-this.blockSize).slice();
|
this.prevBlock = encryptedBlocks.slice(-this.blockSize);
|
||||||
|
|
||||||
// take care of leftover data
|
// take care of leftover data
|
||||||
if (leftover > 0) this.nextBlock.set(value.subarray(-leftover).slice());
|
if (leftover > 0) this.nextBlock.set(value.subarray(-leftover));
|
||||||
this.i = leftover;
|
this.i = leftover;
|
||||||
|
|
||||||
return encryptedBlocks;
|
return encryptedBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.i += added.length;
|
this.i += added.length;
|
||||||
let encryptedBlock = new Uint8Array();
|
let encryptedBlock;
|
||||||
if (this.i === this.nextBlock.length) { // block ready to be encrypted
|
if (this.i === this.nextBlock.length) { // block ready to be encrypted
|
||||||
const curBlock = this.nextBlock;
|
const curBlock = this.nextBlock;
|
||||||
encryptedBlock = await this._runCBC(this.prevBlock);
|
encryptedBlock = await this._runCBC(this.prevBlock);
|
||||||
@ -195,6 +196,8 @@ class WebCryptoEncryptor {
|
|||||||
const remaining = value.subarray(added.length);
|
const remaining = value.subarray(added.length);
|
||||||
this.nextBlock.set(remaining, this.i);
|
this.nextBlock.set(remaining, this.i);
|
||||||
this.i += remaining.length;
|
this.i += remaining.length;
|
||||||
|
} else {
|
||||||
|
encryptedBlock = new Uint8Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
return encryptedBlock;
|
return encryptedBlock;
|
||||||
@ -237,22 +240,111 @@ class WebCryptoEncryptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NobleStreamProcessor {
|
||||||
|
constructor(forEncryption, algo, key, iv) {
|
||||||
|
this.forEncryption = forEncryption;
|
||||||
|
const { blockSize } = getCipherParams(algo);
|
||||||
|
this.key = nobleAesHelpers.expandKeyLE(key);
|
||||||
|
|
||||||
|
if (iv.byteOffset % 4 !== 0) iv = iv.slice(); // aligned arrays required by noble-ciphers
|
||||||
|
this.prevBlock = getUint32Array(iv);
|
||||||
|
this.nextBlock = new Uint8Array(blockSize);
|
||||||
|
this.i = 0; // pointer inside next block
|
||||||
|
this.blockSize = blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
_runCFB(src) {
|
||||||
|
const src32 = getUint32Array(src);
|
||||||
|
const dst = new Uint8Array(src.length);
|
||||||
|
const dst32 = getUint32Array(dst);
|
||||||
|
for (let i = 0; i + 4 <= dst32.length; i += 4) {
|
||||||
|
const { s0: e0, s1: e1, s2: e2, s3: e3 } = nobleAesHelpers.encrypt(this.key, this.prevBlock[0], this.prevBlock[1], this.prevBlock[2], this.prevBlock[3]);
|
||||||
|
dst32[i + 0] = src32[i + 0] ^ e0;
|
||||||
|
dst32[i + 1] = src32[i + 1] ^ e1;
|
||||||
|
dst32[i + 2] = src32[i + 2] ^ e2;
|
||||||
|
dst32[i + 3] = src32[i + 3] ^ e3;
|
||||||
|
this.prevBlock = (this.forEncryption ? dst32 : src32).slice(i, i + 4);
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
async processChunk(value) {
|
||||||
|
const missing = this.nextBlock.length - this.i;
|
||||||
|
const added = value.subarray(0, missing);
|
||||||
|
this.nextBlock.set(added, this.i);
|
||||||
|
|
||||||
|
if ((this.i + value.length) >= (2 * this.blockSize)) {
|
||||||
|
const leftover = (value.length - missing) % this.blockSize;
|
||||||
|
const toProcess = util.concatUint8Array([
|
||||||
|
this.nextBlock,
|
||||||
|
value.subarray(missing, value.length - leftover)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const processedBlocks = this._runCFB(toProcess);
|
||||||
|
|
||||||
|
// take care of leftover data
|
||||||
|
if (leftover > 0) this.nextBlock.set(value.subarray(-leftover));
|
||||||
|
this.i = leftover;
|
||||||
|
|
||||||
|
return processedBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.i += added.length;
|
||||||
|
|
||||||
|
let processedBlock;
|
||||||
|
if (this.i === this.nextBlock.length) { // block ready to be encrypted
|
||||||
|
processedBlock = this._runCFB(this.nextBlock);
|
||||||
|
this.i = 0;
|
||||||
|
|
||||||
|
const remaining = value.subarray(added.length);
|
||||||
|
this.nextBlock.set(remaining, this.i);
|
||||||
|
this.i += remaining.length;
|
||||||
|
} else {
|
||||||
|
processedBlock = new Uint8Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
async finish() {
|
||||||
|
let result;
|
||||||
|
if (this.i === 0) { // nothing more to encrypt
|
||||||
|
result = new Uint8Array();
|
||||||
|
} else {
|
||||||
|
const processedBlock = this._runCFB(this.nextBlock);
|
||||||
|
|
||||||
|
result = processedBlock.subarray(0, this.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearSensitiveData();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSensitiveData() {
|
||||||
|
this.nextBlock.fill(0);
|
||||||
|
this.prevBlock.fill(0);
|
||||||
|
this.key.fill(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function aesEncrypt(algo, key, pt, iv) {
|
async function aesEncrypt(algo, key, pt, iv) {
|
||||||
if (webCrypto && await WebCryptoEncryptor.isSupported(algo)) { // Chromium does not implement AES with 192-bit keys
|
if (webCrypto && await WebCryptoEncryptor.isSupported(algo)) { // Chromium does not implement AES with 192-bit keys
|
||||||
const cfb = new WebCryptoEncryptor(algo, key, iv);
|
const cfb = new WebCryptoEncryptor(algo, key, iv);
|
||||||
return util.isStream(pt) ? stream.transform(pt, value => cfb.encryptChunk(value), () => cfb.finish()) : cfb.encrypt(pt);
|
return util.isStream(pt) ? stream.transform(pt, value => cfb.encryptChunk(value), () => cfb.finish()) : cfb.encrypt(pt);
|
||||||
} else {
|
} else if (util.isStream(pt)) { // async callbacks are not accepted by stream.transform unless the input is a stream
|
||||||
const cfb = new AES_CFB(key, iv);
|
const cfb = new NobleStreamProcessor(true, algo, key, iv);
|
||||||
return stream.transform(pt, value => cfb.aes.AES_Encrypt_process(value), () => cfb.aes.AES_Encrypt_finish());
|
return stream.transform(pt, value => cfb.processChunk(value), () => cfb.finish());
|
||||||
}
|
}
|
||||||
|
return nobleAesCfb(key, iv).encrypt(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function aesDecrypt(algo, key, ct, iv) {
|
async function aesDecrypt(algo, key, ct, iv) {
|
||||||
if (util.isStream(ct)) {
|
if (util.isStream(ct)) {
|
||||||
const cfb = new AES_CFB(key, iv);
|
const cfb = new NobleStreamProcessor(false, algo, key, iv);
|
||||||
return stream.transform(ct, value => cfb.aes.AES_Decrypt_process(value), () => cfb.aes.AES_Decrypt_finish());
|
return stream.transform(ct, value => cfb.processChunk(value), () => cfb.finish());
|
||||||
}
|
}
|
||||||
return AES_CFB.decrypt(ct, key, iv);
|
return nobleAesCfb(key, iv).decrypt(ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
function xorMut(a, b) {
|
function xorMut(a, b) {
|
||||||
@ -262,6 +354,8 @@ function xorMut(a, b) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getUint32Array = arr => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
||||||
|
|
||||||
function nodeEncrypt(algo, key, pt, iv) {
|
function nodeEncrypt(algo, key, pt, iv) {
|
||||||
const algoName = enums.read(enums.symmetric, algo);
|
const algoName = enums.read(enums.symmetric, algo);
|
||||||
const cipherObj = new nodeCrypto.createCipheriv(nodeAlgos[algoName], key, iv);
|
const cipherObj = new nodeCrypto.createCipheriv(nodeAlgos[algoName], key, iv);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user