'use strict'; const { ObjectSetPrototypeOf, ReflectApply, StringPrototypeToLowerCase, } = primordials; const { CipherBase, privateDecrypt: _privateDecrypt, privateEncrypt: _privateEncrypt, publicDecrypt: _publicDecrypt, publicEncrypt: _publicEncrypt, getCipherInfo: _getCipherInfo, } = internalBinding('crypto'); const { crypto: { RSA_PKCS1_OAEP_PADDING, RSA_PKCS1_PADDING, } } = internalBinding('constants'); const { codes: { ERR_CRYPTO_INVALID_STATE, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, } } = require('internal/errors'); const { validateEncoding, validateInt32, validateObject, validateString, } = require('internal/validators'); const { preparePrivateKey, preparePublicOrPrivateKey, prepareSecretKey, } = require('internal/crypto/keys'); const { getDefaultEncoding, getArrayBufferOrView, getStringOption, kHandle, } = require('internal/crypto/util'); const { isArrayBufferView, } = require('internal/util/types'); const assert = require('internal/assert'); const LazyTransform = require('internal/streams/lazy_transform'); const { normalizeEncoding } = require('internal/util'); const { StringDecoder } = require('string_decoder'); function rsaFunctionFor(method, defaultPadding, keyType) { return (options, buffer) => { const { format, type, data, passphrase } = keyType === 'private' ? preparePrivateKey(options) : preparePublicOrPrivateKey(options); const padding = options.padding || defaultPadding; const { oaepHash, encoding } = options; let { oaepLabel } = options; if (oaepHash !== undefined) validateString(oaepHash, 'key.oaepHash'); if (oaepLabel !== undefined) oaepLabel = getArrayBufferOrView(oaepLabel, 'key.oaepLabel', encoding); buffer = getArrayBufferOrView(buffer, 'buffer', encoding); return method(data, format, type, passphrase, buffer, padding, oaepHash, oaepLabel); }; } const publicEncrypt = rsaFunctionFor(_publicEncrypt, RSA_PKCS1_OAEP_PADDING, 'public'); const publicDecrypt = rsaFunctionFor(_publicDecrypt, RSA_PKCS1_PADDING, 'public'); const privateEncrypt = rsaFunctionFor(_privateEncrypt, RSA_PKCS1_PADDING, 'private'); const privateDecrypt = rsaFunctionFor(_privateDecrypt, RSA_PKCS1_OAEP_PADDING, 'private'); function getDecoder(decoder, encoding) { encoding = normalizeEncoding(encoding); decoder = decoder || new StringDecoder(encoding); assert(decoder.encoding === encoding, 'Cannot change encoding'); return decoder; } function getUIntOption(options, key) { let value; if (options && (value = options[key]) != null) { if (value >>> 0 !== value) throw new ERR_INVALID_ARG_VALUE(`options.${key}`, value); return value; } return -1; } function createCipherBase(cipher, credential, options, decipher, iv) { const authTagLength = getUIntOption(options, 'authTagLength'); this[kHandle] = new CipherBase(decipher); if (iv === undefined) { this[kHandle].init(cipher, credential, authTagLength); } else { this[kHandle].initiv(cipher, credential, iv, authTagLength); } this._decoder = null; ReflectApply(LazyTransform, this, [options]); } function createCipher(cipher, password, options, decipher) { validateString(cipher, 'cipher'); password = getArrayBufferOrView(password, 'password'); ReflectApply(createCipherBase, this, [cipher, password, options, decipher]); } function createCipherWithIV(cipher, key, options, decipher, iv) { validateString(cipher, 'cipher'); const encoding = getStringOption(options, 'encoding'); key = prepareSecretKey(key, encoding); iv = iv === null ? null : getArrayBufferOrView(iv, 'iv'); ReflectApply(createCipherBase, this, [cipher, key, options, decipher, iv]); } // The Cipher class is part of the legacy Node.js crypto API. It exposes // a stream-based encryption/decryption model. For backwards compatibility // the Cipher class is defined using the legacy function syntax rather than // ES6 classes. function Cipher(cipher, password, options) { if (!(this instanceof Cipher)) return new Cipher(cipher, password, options); ReflectApply(createCipher, this, [cipher, password, options, true]); } ObjectSetPrototypeOf(Cipher.prototype, LazyTransform.prototype); ObjectSetPrototypeOf(Cipher, LazyTransform); Cipher.prototype._transform = function _transform(chunk, encoding, callback) { this.push(this[kHandle].update(chunk, encoding)); callback(); }; Cipher.prototype._flush = function _flush(callback) { try { this.push(this[kHandle].final()); } catch (e) { callback(e); return; } callback(); }; Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) { const encoding = getDefaultEncoding(); inputEncoding = inputEncoding || encoding; outputEncoding = outputEncoding || encoding; if (typeof data === 'string') { validateEncoding(data, inputEncoding); } else if (!isArrayBufferView(data)) { throw new ERR_INVALID_ARG_TYPE( 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); } const ret = this[kHandle].update(data, inputEncoding); if (outputEncoding && outputEncoding !== 'buffer') { this._decoder = getDecoder(this._decoder, outputEncoding); return this._decoder.write(ret); } return ret; }; Cipher.prototype.final = function final(outputEncoding) { outputEncoding = outputEncoding || getDefaultEncoding(); const ret = this[kHandle].final(); if (outputEncoding && outputEncoding !== 'buffer') { this._decoder = getDecoder(this._decoder, outputEncoding); return this._decoder.end(ret); } return ret; }; Cipher.prototype.setAutoPadding = function setAutoPadding(ap) { if (!this[kHandle].setAutoPadding(!!ap)) throw new ERR_CRYPTO_INVALID_STATE('setAutoPadding'); return this; }; Cipher.prototype.getAuthTag = function getAuthTag() { const ret = this[kHandle].getAuthTag(); if (ret === undefined) throw new ERR_CRYPTO_INVALID_STATE('getAuthTag'); return ret; }; function setAuthTag(tagbuf, encoding) { tagbuf = getArrayBufferOrView(tagbuf, 'buffer', encoding); if (!this[kHandle].setAuthTag(tagbuf)) throw new ERR_CRYPTO_INVALID_STATE('setAuthTag'); return this; } Cipher.prototype.setAAD = function setAAD(aadbuf, options) { const encoding = getStringOption(options, 'encoding'); const plaintextLength = getUIntOption(options, 'plaintextLength'); aadbuf = getArrayBufferOrView(aadbuf, 'aadbuf', encoding); if (!this[kHandle].setAAD(aadbuf, plaintextLength)) throw new ERR_CRYPTO_INVALID_STATE('setAAD'); return this; }; // The Cipheriv class is part of the legacy Node.js crypto API. It exposes // a stream-based encryption/decryption model. For backwards compatibility // the Cipheriv class is defined using the legacy function syntax rather than // ES6 classes. function Cipheriv(cipher, key, iv, options) { if (!(this instanceof Cipheriv)) return new Cipheriv(cipher, key, iv, options); ReflectApply(createCipherWithIV, this, [cipher, key, options, true, iv]); } function addCipherPrototypeFunctions(constructor) { constructor.prototype._transform = Cipher.prototype._transform; constructor.prototype._flush = Cipher.prototype._flush; constructor.prototype.update = Cipher.prototype.update; constructor.prototype.final = Cipher.prototype.final; constructor.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; if (constructor === Cipheriv) { constructor.prototype.getAuthTag = Cipher.prototype.getAuthTag; } else { constructor.prototype.setAuthTag = setAuthTag; } constructor.prototype.setAAD = Cipher.prototype.setAAD; } ObjectSetPrototypeOf(Cipheriv.prototype, LazyTransform.prototype); ObjectSetPrototypeOf(Cipheriv, LazyTransform); addCipherPrototypeFunctions(Cipheriv); // The Decipher class is part of the legacy Node.js crypto API. It exposes // a stream-based encryption/decryption model. For backwards compatibility // the Decipher class is defined using the legacy function syntax rather than // ES6 classes. function Decipher(cipher, password, options) { if (!(this instanceof Decipher)) return new Decipher(cipher, password, options); ReflectApply(createCipher, this, [cipher, password, options, false]); } ObjectSetPrototypeOf(Decipher.prototype, LazyTransform.prototype); ObjectSetPrototypeOf(Decipher, LazyTransform); addCipherPrototypeFunctions(Decipher); // The Decipheriv class is part of the legacy Node.js crypto API. It exposes // a stream-based encryption/decryption model. For backwards compatibility // the Decipheriv class is defined using the legacy function syntax rather than // ES6 classes. function Decipheriv(cipher, key, iv, options) { if (!(this instanceof Decipheriv)) return new Decipheriv(cipher, key, iv, options); ReflectApply(createCipherWithIV, this, [cipher, key, options, false, iv]); } ObjectSetPrototypeOf(Decipheriv.prototype, LazyTransform.prototype); ObjectSetPrototypeOf(Decipheriv, LazyTransform); addCipherPrototypeFunctions(Decipheriv); function getCipherInfo(nameOrNid, options) { if (typeof nameOrNid !== 'string' && typeof nameOrNid !== 'number') { throw new ERR_INVALID_ARG_TYPE( 'nameOrNid', ['string', 'number'], nameOrNid); } if (typeof nameOrNid === 'number') validateInt32(nameOrNid, 'nameOrNid'); let keyLength, ivLength; if (options !== undefined) { validateObject(options, 'options'); ({ keyLength, ivLength } = options); if (keyLength !== undefined) validateInt32(keyLength, 'options.keyLength'); if (ivLength !== undefined) validateInt32(ivLength, 'options.ivLength'); } const ret = _getCipherInfo({}, nameOrNid, keyLength, ivLength); if (ret !== undefined) { if (ret.name) ret.name = StringPrototypeToLowerCase(ret.name); if (ret.type) ret.type = StringPrototypeToLowerCase(ret.type); } return ret; } module.exports = { Cipher, Cipheriv, Decipher, Decipheriv, privateDecrypt, privateEncrypt, publicDecrypt, publicEncrypt, getCipherInfo, };