Skip to content

Commit

Permalink
tmp test stream.cancel with slower cfb
Browse files Browse the repository at this point in the history
  • Loading branch information
larabr committed Jun 23, 2023
1 parent 785d24d commit fcdbf8a
Showing 1 changed file with 230 additions and 124 deletions.
354 changes: 230 additions & 124 deletions src/crypto/mode/cfb.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,139 +43,245 @@ const nodeAlgos = {
/* twofish is not implemented in OpenSSL */
};

/**
* CFB encryption
* @param {enums.symmetric} algo - block cipher algorithm
* @param {Uint8Array} key
* @param {MaybeStream<Uint8Array>} plaintext
* @param {Uint8Array} iv
* @param {Object} config - full configuration, defaults to openpgp.config
* @returns MaybeStream<Uint8Array>
*/
export async function encrypt(algo, key, plaintext, iv, config) {
const algoName = enums.read(enums.symmetric, algo);
if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.
return nodeEncrypt(algo, key, plaintext, iv);
/**

Check failure on line 46 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 0 spaces but found 2
* CFB encryption
* @param {enums.symmetric} algo - block cipher algorithm
* @param {Uint8Array} key
* @param {MaybeStream<Uint8Array>} plaintext
* @param {Uint8Array} iv
* @param {Object} config - full configuration, defaults to openpgp.config
* @returns MaybeStream<Uint8Array>
*/
export async function encrypt(algo, key, plaintext, iv, config) {

Check failure on line 55 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 0 spaces but found 2
const algoName = enums.read(enums.symmetric, algo);

Check failure on line 56 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 2 spaces but found 4
if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.

Check failure on line 57 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 2 spaces but found 4
return nodeEncrypt(algo, key, plaintext, iv);

Check failure on line 58 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 4 spaces but found 6
}

Check failure on line 59 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 2 spaces but found 4
if (algoName.substr(0, 3) === 'aes') {

Check failure on line 60 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 2 spaces but found 4
return aesEncrypt(algo, key, plaintext, iv, config);

Check failure on line 61 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 4 spaces but found 6
}

Check failure on line 62 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Expected indentation of 2 spaces but found 4

Check failure on line 63 in src/crypto/mode/cfb.js

View workflow job for this annotation

GitHub Actions / ESLint

Trailing spaces not allowed
const Cipher = getCipher(algo);
const cipherfn = new Cipher(key);
const block_size = cipherfn.blockSize;

const blockc = iv.slice();
let pt = new Uint8Array();
const process = chunk => {
if (chunk) {
pt = util.concatUint8Array([pt, chunk]);
}
const ciphertext = new Uint8Array(pt.length);
let i;
let j = 0;
while (chunk ? pt.length >= block_size : pt.length) {
const encblock = cipherfn.encrypt(blockc);
for (i = 0; i < block_size; i++) {
blockc[i] = pt[i] ^ encblock[i];
ciphertext[j++] = blockc[i];
}
pt = pt.subarray(block_size);
}
return ciphertext.subarray(0, j);
};
return stream.transform(plaintext, process, process);
}
if (algoName.substr(0, 3) === 'aes') {
return aesEncrypt(algo, key, plaintext, iv, config);

/**
* CFB decryption
* @param {enums.symmetric} algo - block cipher algorithm
* @param {Uint8Array} key
* @param {MaybeStream<Uint8Array>} ciphertext
* @param {Uint8Array} iv
* @returns MaybeStream<Uint8Array>
*/
export async function decrypt(algo, key, ciphertext, iv) {
const algoName = enums.read(enums.symmetric, algo);
if (nodeCrypto && nodeAlgos[algoName]) { // Node crypto library.
return nodeDecrypt(algo, key, ciphertext, iv);
}
if (algoName.substr(0, 3) === 'aes') {
return aesDecrypt(algo, key, ciphertext, iv);
}

const Cipher = getCipher(algo);
const cipherfn = new Cipher(key);
const block_size = cipherfn.blockSize;

let blockp = iv;
let ct = new Uint8Array();
const process = chunk => {
if (chunk) {
ct = util.concatUint8Array([ct, chunk]);
}
const plaintext = new Uint8Array(ct.length);
let i;
let j = 0;
while (chunk ? ct.length >= block_size : ct.length) {
const decblock = cipherfn.encrypt(blockp);
blockp = ct;
for (i = 0; i < block_size; i++) {
plaintext[j++] = blockp[i] ^ decblock[i];
}
ct = ct.subarray(block_size);
}
return plaintext.subarray(0, j);
};
return stream.transform(ciphertext, process, process);
}

const Cipher = getCipher(algo);
const cipherfn = new Cipher(key);
const block_size = cipherfn.blockSize;

const blockc = iv.slice();
let pt = new Uint8Array();
const process = chunk => {
if (chunk) {
pt = util.concatUint8Array([pt, chunk]);

class WebCryptoEncryptor {
constructor(algo, key, iv) {
const { blockSize } = getCipher(algo);
this.key = key;
this.prevBlock = iv;
this.nextBlock = new Uint8Array(blockSize);
this.i = 0;// pointer inside next block
this.blockSize = blockSize;
this.zeroBlock = new Uint8Array(this.blockSize);
}

static async isSupported(algo) {
const { keySize } = getCipher(algo);
return webCrypto.importKey('raw', new Uint8Array(keySize), 'aes-cbc', false, ['encrypt'])
.then(() => true)
.catch(() => false);
}
const ciphertext = new Uint8Array(pt.length);
let i;
let j = 0;
while (chunk ? pt.length >= block_size : pt.length) {
const encblock = cipherfn.encrypt(blockc);
for (i = 0; i < block_size; i++) {
blockc[i] = pt[i] ^ encblock[i];
ciphertext[j++] = blockc[i];

async _runCBC(plaintext, nonZeroIV) {
const mode = 'AES-CBC';
this.keyRef = this.keyRef || await webCrypto.importKey('raw', this.key, mode, false, ['encrypt']);
const ciphertext = await webCrypto.encrypt(
{ name: mode, iv: nonZeroIV || this.zeroBlock },
this.keyRef,
plaintext
);
return new Uint8Array(ciphertext).subarray(0, plaintext.length);
}

async encryptChunk(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 plaintext = util.concatUint8Array([
this.nextBlock,
value.subarray(missing, value.length - leftover)
]);
const toEncrypt = util.concatUint8Array([
this.prevBlock,
plaintext.subarray(0, plaintext.length - this.blockSize) // stop one block "early", since we only need to xor the plaintext and pass it over as prevBlock
]);

const encryptedBlocks = await this._runCBC(toEncrypt);
xorMut(encryptedBlocks, plaintext);
this.prevBlock = encryptedBlocks.subarray(-this.blockSize).slice();

// take care of leftover data
if (leftover > 0) this.nextBlock.set(value.subarray(-leftover).slice());
this.i = leftover;

return encryptedBlocks;
}

this.i += added.length;
let encryptedBlock = new Uint8Array();
if (this.i === this.nextBlock.length) { // block ready to be encrypted
const curBlock = this.nextBlock;
encryptedBlock = await this._runCBC(this.prevBlock);
xorMut(encryptedBlock, curBlock);
this.prevBlock = encryptedBlock.slice();
this.i = 0;

const remaining = value.subarray(added.length);
this.nextBlock.set(remaining, this.i);
this.i += remaining.length;
}
pt = pt.subarray(block_size);

return encryptedBlock;
}

async finish() {
let result;
if (this.i === 0) { // nothing more to encrypt
result = new Uint8Array();
} else {
this.nextBlock = this.nextBlock.subarray(0, this.i);
const curBlock = this.nextBlock;
const encryptedBlock = await this._runCBC(this.prevBlock);
xorMut(encryptedBlock, curBlock);
result = encryptedBlock.subarray(0, curBlock.length);
}

this.clearSensitiveData();
return result;
}

clearSensitiveData() {
this.nextBlock.fill(0);
this.prevBlock.fill(0);
this.keyRef = null;
this.key = null;
}

async encrypt(plaintext) {
const ct = await this._runCBC(
util.concatUint8Array([new Uint8Array(this.blockSize), plaintext]),
this.iv
).then(result => result.subarray(0, plaintext.length));
xorMut(ct, plaintext);
this.clearSensitiveData();
return ct;
}
return ciphertext.subarray(0, j);
};
return stream.transform(plaintext, process, process);
}

/**
* CFB decryption
* @param {enums.symmetric} algo - block cipher algorithm
* @param {Uint8Array} key
* @param {MaybeStream<Uint8Array>} ciphertext
* @param {Uint8Array} iv
* @returns MaybeStream<Uint8Array>
*/
export async function decrypt(algo, key, ciphertext, iv) {
const algoName = enums.read(enums.symmetric, algo);
if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.
return nodeDecrypt(algo, key, ciphertext, iv);
}
if (algoName.substr(0, 3) === 'aes') {
return aesDecrypt(algo, key, ciphertext, iv);

async function aesEncrypt(algo, key, pt, iv) {
if (webCrypto && await WebCryptoEncryptor.isSupported(algo)) { // Chromium does not implement AES with 192-bit keys
const cfb = new WebCryptoEncryptor(algo, key, iv);
return util.isStream(pt) ? stream.transform(pt, value => cfb.encryptChunk(value), () => cfb.finish()) : cfb.encrypt(pt);
} else {
const cfb = new AES_CFB(key, iv);
return stream.transform(pt, value => cfb.aes.AES_Encrypt_process(value), () => cfb.aes.AES_Encrypt_finish());
}



}

const Cipher = getCipher(algo);
const cipherfn = new Cipher(key);
const block_size = cipherfn.blockSize;

let blockp = iv;
let ct = new Uint8Array();
const process = chunk => {
if (chunk) {
ct = util.concatUint8Array([ct, chunk]);

function aesDecrypt(algo, key, ct, iv) {
if (util.isStream(ct)) {
const cfb = new AES_CFB(key, iv);
return stream.transform(ct, value => cfb.aes.AES_Decrypt_process(value), () => cfb.aes.AES_Decrypt_finish());
}
const plaintext = new Uint8Array(ct.length);
let i;
let j = 0;
while (chunk ? ct.length >= block_size : ct.length) {
const decblock = cipherfn.encrypt(blockp);
blockp = ct;
for (i = 0; i < block_size; i++) {
plaintext[j++] = blockp[i] ^ decblock[i];
}
ct = ct.subarray(block_size);
return AES_CFB.decrypt(ct, key, iv);
}

function xorMut(a, b) {
for (let i = 0; i < Math.min(a.length, b.length); i++) {
a[i] = a[i] ^ b[i];
}
return plaintext.subarray(0, j);
};
return stream.transform(ciphertext, process, process);
}

function aesEncrypt(algo, key, pt, iv, config) {
if (
util.getWebCrypto() &&
key.length !== 24 && // Chrome doesn't support 192 bit keys, see https://www.chromium.org/blink/webcrypto#TOC-AES-support
!util.isStream(pt) &&
pt.length >= 3000 * config.minBytesForWebCrypto // Default to a 3MB minimum. Chrome is pretty slow for small messages, see: https://bugs.chromium.org/p/chromium/issues/detail?id=701188#c2
) { // Web Crypto
return webEncrypt(algo, key, pt, iv);
}
// asm.js fallback
const cfb = new AES_CFB(key, iv);
return stream.transform(pt, value => cfb.aes.AES_Encrypt_process(value), () => cfb.aes.AES_Encrypt_finish());
}

function aesDecrypt(algo, key, ct, iv) {
if (util.isStream(ct)) {
const cfb = new AES_CFB(key, iv);
return stream.transform(ct, value => cfb.aes.AES_Decrypt_process(value), () => cfb.aes.AES_Decrypt_finish());











function nodeEncrypt(algo, key, pt, iv) {
const algoName = enums.read(enums.symmetric, algo);
const cipherObj = new nodeCrypto.createCipheriv(nodeAlgos[algoName], key, iv);
return stream.transform(pt, value => new Uint8Array(cipherObj.update(value)));
}
return AES_CFB.decrypt(ct, key, iv);
}

function xorMut(a, b) {
for (let i = 0; i < a.length; i++) {
a[i] = a[i] ^ b[i];

function nodeDecrypt(algo, key, ct, iv) {
const algoName = enums.read(enums.symmetric, algo);
const decipherObj = new nodeCrypto.createDecipheriv(nodeAlgos[algoName], key, iv);
return stream.transform(ct, value => new Uint8Array(decipherObj.update(value)));
}
}

async function webEncrypt(algo, key, pt, iv) {
const ALGO = 'AES-CBC';
const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt']);
const { blockSize } = getCipher(algo);
const cbc_pt = util.concatUint8Array([new Uint8Array(blockSize), pt]);
const ct = new Uint8Array(await webCrypto.encrypt({ name: ALGO, iv }, _key, cbc_pt)).subarray(0, pt.length);
xorMut(ct, pt);
return ct;
}

function nodeEncrypt(algo, key, pt, iv) {
const algoName = enums.read(enums.symmetric, algo);
const cipherObj = new nodeCrypto.createCipheriv(nodeAlgos[algoName], key, iv);
return stream.transform(pt, value => new Uint8Array(cipherObj.update(value)));
}

function nodeDecrypt(algo, key, ct, iv) {
const algoName = enums.read(enums.symmetric, algo);
const decipherObj = new nodeCrypto.createDecipheriv(nodeAlgos[algoName], key, iv);
return stream.transform(ct, value => new Uint8Array(decipherObj.update(value)));
}

0 comments on commit fcdbf8a

Please sign in to comment.