diff --git a/package-lock.json b/package-lock.json index 73d5a0b..247131d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3886,9 +3886,9 @@ "dev": true }, "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -7819,9 +7819,9 @@ "dev": true }, "nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, "natural-compare": { "version": "1.4.0", diff --git a/src/transform/block.ts b/src/transform/block.ts index 29ec368..3048884 100644 --- a/src/transform/block.ts +++ b/src/transform/block.ts @@ -20,6 +20,10 @@ export class Block extends Transform { } + calculateRemain(length: number, blockSize: number): number { + return length % blockSize; + } + _transform(data: Buffer, encoding: BufferEncoding, callback: TransformCallback) { const blockSize = this._cipher.getBlockSize(); @@ -30,26 +34,38 @@ export class Block extends Transform { data = Buffer.concat([this._tail, data]); - const remain = blockSize + ((data.length % blockSize) || blockSize); - const align = data.length > remain ? data.length - remain : 0; - - this._tail = data.slice(align); + if (data.length < blockSize) { + // The chunk is smaller than the block size, and the algorithm can't handle this case. + // We should pass the current chunk to the next transformation operation to concatenate it + // and attempt processing in the next step. + this._tail = data.subarray(); + return callback(); + } + + const remain = this.calculateRemain(data.length, blockSize); + + if (remain === 0) { + // Perfectly fitting the data size to the block size eliminates the need to pass + // the remaining data to the next transformation. + this._tail = Buffer.alloc(0); + } else { + this._tail = data.subarray(-remain); + data = data.subarray(0, -remain); + } try { - return callback(null, this._cipher.transform(data.slice(0, align))); + return callback(null, this._cipher.transform(data)); } catch (err) { return callback(err as Error | null | undefined); } } - - } export class BlockEncrypt extends Block { _flush(callback: TransformCallback) { try { - this._tail.length > 0 && this.push(this._cipher.transform(this._pad(this._tail))); + this.push(this._cipher.transform(this._pad(this._tail))); return callback(null); } catch (err) { return callback(err as Error | null | undefined); @@ -72,12 +88,27 @@ export class BlockEncrypt extends Block { export class BlockDecrypt extends Block { + calculateRemain(length: number, blockSize: number): number { + // This is necessary because the stream data length is indefinite, + // and we must ensure that the stream finishes in time. + // We also need to process the last bytes of data separately to remove padding from the decrypted data. + return (length % blockSize) || blockSize; + } + _flush(callback: TransformCallback) { - + + if (this._tail.length === 0) { + return callback(new Error('Finishing data cannot be empty')); + } + + if ((this._tail.length % this._cipher.getBlockSize()) !== 0) { + return callback(new Error('Finishing data not matches the block size')); + } + const target = this._cipher.transform(this._tail); try { - this._tail.length > 0 && this.push(this._unpad(target)); + this.push(this._unpad(target)); return callback(null); } catch (err) { return callback(err as Error | null | undefined);