diff options
Diffstat (limited to 'ext/node/polyfills/internal/crypto/cipher.ts')
-rw-r--r-- | ext/node/polyfills/internal/crypto/cipher.ts | 87 |
1 files changed, 70 insertions, 17 deletions
diff --git a/ext/node/polyfills/internal/crypto/cipher.ts b/ext/node/polyfills/internal/crypto/cipher.ts index 5622576cd..cf1641326 100644 --- a/ext/node/polyfills/internal/crypto/cipher.ts +++ b/ext/node/polyfills/internal/crypto/cipher.ts @@ -36,6 +36,8 @@ function isStringOrBuffer(val) { const { ops, encode } = globalThis.__bootstrap.core; +const NO_TAG = new Uint8Array(); + export type CipherCCMTypes = | "aes-128-ccm" | "aes-192-ccm" @@ -143,6 +145,10 @@ export class Cipheriv extends Transform implements Cipher { /** plaintext data cache */ #cache: BlockModeCache; + #needsBlockCache: boolean; + + #authTag?: Buffer; + constructor( cipher: string, key: CipherKey, @@ -162,6 +168,8 @@ export class Cipheriv extends Transform implements Cipher { }); this.#cache = new BlockModeCache(false); this.#context = ops.op_node_create_cipheriv(cipher, toU8(key), toU8(iv)); + this.#needsBlockCache = + !(cipher == "aes-128-gcm" || cipher == "aes-256-gcm"); if (this.#context == 0) { throw new TypeError("Unknown cipher"); } @@ -169,21 +177,29 @@ export class Cipheriv extends Transform implements Cipher { final(encoding: string = getDefaultEncoding()): Buffer | string { const buf = new Buffer(16); - ops.op_node_cipheriv_final(this.#context, this.#cache.cache, buf); + const maybeTag = ops.op_node_cipheriv_final( + this.#context, + this.#cache.cache, + buf, + ); + if (maybeTag) { + this.#authTag = Buffer.from(maybeTag); + return encoding === "buffer" ? Buffer.from([]) : ""; + } return encoding === "buffer" ? buf : buf.toString(encoding); } getAuthTag(): Buffer { - notImplemented("crypto.Cipheriv.prototype.getAuthTag"); + return this.#authTag!; } setAAD( - _buffer: ArrayBufferView, + buffer: ArrayBufferView, _options?: { plaintextLength: number; }, ): this { - notImplemented("crypto.Cipheriv.prototype.setAAD"); + ops.op_node_cipheriv_set_aad(this.#context, buffer); return this; } @@ -198,13 +214,23 @@ export class Cipheriv extends Transform implements Cipher { outputEncoding: Encoding = getDefaultEncoding(), ): Buffer | string { // TODO(kt3k): throw ERR_INVALID_ARG_TYPE if data is not string, Buffer, or ArrayBufferView + let buf = data; if (typeof data === "string" && typeof inputEncoding === "string") { - this.#cache.add(Buffer.from(data, inputEncoding)); - } else { - this.#cache.add(data); + buf = Buffer.from(data, inputEncoding); } - const input = this.#cache.get(); + let output; + if (!this.#needsBlockCache) { + output = Buffer.allocUnsafe(buf.length); + ops.op_node_cipheriv_encrypt(this.#context, buf, output); + return outputEncoding === "buffer" + ? output + : output.toString(outputEncoding); + } + + this.#cache.add(buf); + const input = this.#cache.get(); + if (input === null) { output = Buffer.alloc(0); } else { @@ -262,6 +288,10 @@ export class Decipheriv extends Transform implements Cipher { /** ciphertext data cache */ #cache: BlockModeCache; + #needsBlockCache: boolean; + + #authTag?: BinaryLike; + constructor( cipher: string, key: CipherKey, @@ -281,6 +311,8 @@ export class Decipheriv extends Transform implements Cipher { }); this.#cache = new BlockModeCache(true); this.#context = ops.op_node_create_decipheriv(cipher, toU8(key), toU8(iv)); + this.#needsBlockCache = + !(cipher == "aes-128-gcm" || cipher == "aes-256-gcm"); if (this.#context == 0) { throw new TypeError("Unknown cipher"); } @@ -288,22 +320,34 @@ export class Decipheriv extends Transform implements Cipher { final(encoding: string = getDefaultEncoding()): Buffer | string { let buf = new Buffer(16); - ops.op_node_decipheriv_final(this.#context, this.#cache.cache, buf); + ops.op_node_decipheriv_final( + this.#context, + this.#cache.cache, + buf, + this.#authTag || NO_TAG, + ); + + if (!this.#needsBlockCache) { + return encoding === "buffer" ? Buffer.from([]) : ""; + } + buf = buf.subarray(0, 16 - buf.at(-1)); // Padded in Pkcs7 mode return encoding === "buffer" ? buf : buf.toString(encoding); } setAAD( - _buffer: ArrayBufferView, + buffer: ArrayBufferView, _options?: { plaintextLength: number; }, ): this { - notImplemented("crypto.Decipheriv.prototype.setAAD"); + ops.op_node_decipheriv_set_aad(this.#context, buffer); + return this; } - setAuthTag(_buffer: BinaryLike, _encoding?: string): this { - notImplemented("crypto.Decipheriv.prototype.setAuthTag"); + setAuthTag(buffer: BinaryLike, _encoding?: string): this { + this.#authTag = buffer; + return this; } setAutoPadding(_autoPadding?: boolean): this { @@ -316,13 +360,22 @@ export class Decipheriv extends Transform implements Cipher { outputEncoding: Encoding = getDefaultEncoding(), ): Buffer | string { // TODO(kt3k): throw ERR_INVALID_ARG_TYPE if data is not string, Buffer, or ArrayBufferView + let buf = data; if (typeof data === "string" && typeof inputEncoding === "string") { - this.#cache.add(Buffer.from(data, inputEncoding)); - } else { - this.#cache.add(data); + buf = Buffer.from(data, inputEncoding); } - const input = this.#cache.get(); + let output; + if (!this.#needsBlockCache) { + output = Buffer.allocUnsafe(buf.length); + ops.op_node_decipheriv_decrypt(this.#context, buf, output); + return outputEncoding === "buffer" + ? output + : output.toString(outputEncoding); + } + + this.#cache.add(buf); + const input = this.#cache.get(); if (input === null) { output = Buffer.alloc(0); } else { |