1'use strict'; 2 3const { 4 ObjectSetPrototypeOf, 5} = primordials; 6 7const { 8 RSA_PKCS1_OAEP_PADDING, 9 RSA_PKCS1_PADDING 10} = internalBinding('constants').crypto; 11 12const { 13 ERR_CRYPTO_INVALID_STATE, 14 ERR_INVALID_ARG_TYPE, 15 ERR_INVALID_OPT_VALUE 16} = require('internal/errors').codes; 17const { validateString } = require('internal/validators'); 18 19const { 20 preparePrivateKey, 21 preparePublicOrPrivateKey, 22 prepareSecretKey 23} = require('internal/crypto/keys'); 24const { 25 getDefaultEncoding, 26 kHandle, 27 getArrayBufferView 28} = require('internal/crypto/util'); 29 30const { isArrayBufferView } = require('internal/util/types'); 31 32const { 33 CipherBase, 34 privateDecrypt: _privateDecrypt, 35 privateEncrypt: _privateEncrypt, 36 publicDecrypt: _publicDecrypt, 37 publicEncrypt: _publicEncrypt 38} = internalBinding('crypto'); 39 40const assert = require('internal/assert'); 41const LazyTransform = require('internal/streams/lazy_transform'); 42 43const { normalizeEncoding } = require('internal/util'); 44 45// Lazy loaded for startup performance. 46let StringDecoder; 47 48function rsaFunctionFor(method, defaultPadding, keyType) { 49 return (options, buffer) => { 50 const { format, type, data, passphrase } = 51 keyType === 'private' ? 52 preparePrivateKey(options) : 53 preparePublicOrPrivateKey(options); 54 const padding = options.padding || defaultPadding; 55 const { oaepHash, oaepLabel } = options; 56 if (oaepHash !== undefined && typeof oaepHash !== 'string') 57 throw new ERR_INVALID_ARG_TYPE('options.oaepHash', 'string', oaepHash); 58 if (oaepLabel !== undefined && !isArrayBufferView(oaepLabel)) { 59 throw new ERR_INVALID_ARG_TYPE('options.oaepLabel', 60 ['Buffer', 'TypedArray', 'DataView'], 61 oaepLabel); 62 } 63 return method(data, format, type, passphrase, buffer, padding, oaepHash, 64 oaepLabel); 65 }; 66} 67 68const publicEncrypt = rsaFunctionFor(_publicEncrypt, RSA_PKCS1_OAEP_PADDING, 69 'public'); 70const publicDecrypt = rsaFunctionFor(_publicDecrypt, RSA_PKCS1_PADDING, 71 'public'); 72const privateEncrypt = rsaFunctionFor(_privateEncrypt, RSA_PKCS1_PADDING, 73 'private'); 74const privateDecrypt = rsaFunctionFor(_privateDecrypt, RSA_PKCS1_OAEP_PADDING, 75 'private'); 76 77function getDecoder(decoder, encoding) { 78 encoding = normalizeEncoding(encoding); 79 if (StringDecoder === undefined) 80 StringDecoder = require('string_decoder').StringDecoder; 81 decoder = decoder || new StringDecoder(encoding); 82 assert(decoder.encoding === encoding, 'Cannot change encoding'); 83 return decoder; 84} 85 86function getUIntOption(options, key) { 87 let value; 88 if (options && (value = options[key]) != null) { 89 if (value >>> 0 !== value) 90 throw new ERR_INVALID_OPT_VALUE(key, value); 91 return value; 92 } 93 return -1; 94} 95 96function createCipherBase(cipher, credential, options, decipher, iv) { 97 const authTagLength = getUIntOption(options, 'authTagLength'); 98 99 this[kHandle] = new CipherBase(decipher); 100 if (iv === undefined) { 101 this[kHandle].init(cipher, credential, authTagLength); 102 } else { 103 this[kHandle].initiv(cipher, credential, iv, authTagLength); 104 } 105 this._decoder = null; 106 107 LazyTransform.call(this, options); 108} 109 110function createCipher(cipher, password, options, decipher) { 111 validateString(cipher, 'cipher'); 112 password = getArrayBufferView(password, 'password'); 113 114 createCipherBase.call(this, cipher, password, options, decipher); 115} 116 117function createCipherWithIV(cipher, key, options, decipher, iv) { 118 validateString(cipher, 'cipher'); 119 key = prepareSecretKey(key); 120 iv = iv === null ? null : getArrayBufferView(iv, 'iv'); 121 createCipherBase.call(this, cipher, key, options, decipher, iv); 122} 123 124function Cipher(cipher, password, options) { 125 if (!(this instanceof Cipher)) 126 return new Cipher(cipher, password, options); 127 128 createCipher.call(this, cipher, password, options, true); 129} 130 131ObjectSetPrototypeOf(Cipher.prototype, LazyTransform.prototype); 132ObjectSetPrototypeOf(Cipher, LazyTransform); 133 134Cipher.prototype._transform = function _transform(chunk, encoding, callback) { 135 this.push(this[kHandle].update(chunk, encoding)); 136 callback(); 137}; 138 139Cipher.prototype._flush = function _flush(callback) { 140 try { 141 this.push(this[kHandle].final()); 142 } catch (e) { 143 callback(e); 144 return; 145 } 146 callback(); 147}; 148 149Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) { 150 const encoding = getDefaultEncoding(); 151 inputEncoding = inputEncoding || encoding; 152 outputEncoding = outputEncoding || encoding; 153 154 if (typeof data !== 'string' && !isArrayBufferView(data)) { 155 throw new ERR_INVALID_ARG_TYPE( 156 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); 157 } 158 159 const ret = this[kHandle].update(data, inputEncoding); 160 161 if (outputEncoding && outputEncoding !== 'buffer') { 162 this._decoder = getDecoder(this._decoder, outputEncoding); 163 return this._decoder.write(ret); 164 } 165 166 return ret; 167}; 168 169 170Cipher.prototype.final = function final(outputEncoding) { 171 outputEncoding = outputEncoding || getDefaultEncoding(); 172 const ret = this[kHandle].final(); 173 174 if (outputEncoding && outputEncoding !== 'buffer') { 175 this._decoder = getDecoder(this._decoder, outputEncoding); 176 return this._decoder.end(ret); 177 } 178 179 return ret; 180}; 181 182 183Cipher.prototype.setAutoPadding = function setAutoPadding(ap) { 184 if (!this[kHandle].setAutoPadding(!!ap)) 185 throw new ERR_CRYPTO_INVALID_STATE('setAutoPadding'); 186 return this; 187}; 188 189Cipher.prototype.getAuthTag = function getAuthTag() { 190 const ret = this[kHandle].getAuthTag(); 191 if (ret === undefined) 192 throw new ERR_CRYPTO_INVALID_STATE('getAuthTag'); 193 return ret; 194}; 195 196 197function setAuthTag(tagbuf) { 198 if (!isArrayBufferView(tagbuf)) { 199 throw new ERR_INVALID_ARG_TYPE('buffer', 200 ['Buffer', 'TypedArray', 'DataView'], 201 tagbuf); 202 } 203 if (!this[kHandle].setAuthTag(tagbuf)) 204 throw new ERR_CRYPTO_INVALID_STATE('setAuthTag'); 205 return this; 206} 207 208Cipher.prototype.setAAD = function setAAD(aadbuf, options) { 209 if (!isArrayBufferView(aadbuf)) { 210 throw new ERR_INVALID_ARG_TYPE('buffer', 211 ['Buffer', 'TypedArray', 'DataView'], 212 aadbuf); 213 } 214 215 const plaintextLength = getUIntOption(options, 'plaintextLength'); 216 if (!this[kHandle].setAAD(aadbuf, plaintextLength)) 217 throw new ERR_CRYPTO_INVALID_STATE('setAAD'); 218 return this; 219}; 220 221function Cipheriv(cipher, key, iv, options) { 222 if (!(this instanceof Cipheriv)) 223 return new Cipheriv(cipher, key, iv, options); 224 225 createCipherWithIV.call(this, cipher, key, options, true, iv); 226} 227 228function addCipherPrototypeFunctions(constructor) { 229 constructor.prototype._transform = Cipher.prototype._transform; 230 constructor.prototype._flush = Cipher.prototype._flush; 231 constructor.prototype.update = Cipher.prototype.update; 232 constructor.prototype.final = Cipher.prototype.final; 233 constructor.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; 234 if (constructor === Cipheriv) { 235 constructor.prototype.getAuthTag = Cipher.prototype.getAuthTag; 236 } else { 237 constructor.prototype.setAuthTag = setAuthTag; 238 } 239 constructor.prototype.setAAD = Cipher.prototype.setAAD; 240} 241 242ObjectSetPrototypeOf(Cipheriv.prototype, LazyTransform.prototype); 243ObjectSetPrototypeOf(Cipheriv, LazyTransform); 244addCipherPrototypeFunctions(Cipheriv); 245 246function Decipher(cipher, password, options) { 247 if (!(this instanceof Decipher)) 248 return new Decipher(cipher, password, options); 249 250 createCipher.call(this, cipher, password, options, false); 251} 252 253ObjectSetPrototypeOf(Decipher.prototype, LazyTransform.prototype); 254ObjectSetPrototypeOf(Decipher, LazyTransform); 255addCipherPrototypeFunctions(Decipher); 256 257 258function Decipheriv(cipher, key, iv, options) { 259 if (!(this instanceof Decipheriv)) 260 return new Decipheriv(cipher, key, iv, options); 261 262 createCipherWithIV.call(this, cipher, key, options, false, iv); 263} 264 265ObjectSetPrototypeOf(Decipheriv.prototype, LazyTransform.prototype); 266ObjectSetPrototypeOf(Decipheriv, LazyTransform); 267addCipherPrototypeFunctions(Decipheriv); 268 269module.exports = { 270 Cipher, 271 Cipheriv, 272 Decipher, 273 Decipheriv, 274 privateDecrypt, 275 privateEncrypt, 276 publicDecrypt, 277 publicEncrypt, 278}; 279