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 { validateEncoding, 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') { 155 validateEncoding(data, inputEncoding); 156 } else if (!isArrayBufferView(data)) { 157 throw new ERR_INVALID_ARG_TYPE( 158 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); 159 } 160 161 const ret = this[kHandle].update(data, inputEncoding); 162 163 if (outputEncoding && outputEncoding !== 'buffer') { 164 this._decoder = getDecoder(this._decoder, outputEncoding); 165 return this._decoder.write(ret); 166 } 167 168 return ret; 169}; 170 171 172Cipher.prototype.final = function final(outputEncoding) { 173 outputEncoding = outputEncoding || getDefaultEncoding(); 174 const ret = this[kHandle].final(); 175 176 if (outputEncoding && outputEncoding !== 'buffer') { 177 this._decoder = getDecoder(this._decoder, outputEncoding); 178 return this._decoder.end(ret); 179 } 180 181 return ret; 182}; 183 184 185Cipher.prototype.setAutoPadding = function setAutoPadding(ap) { 186 if (!this[kHandle].setAutoPadding(!!ap)) 187 throw new ERR_CRYPTO_INVALID_STATE('setAutoPadding'); 188 return this; 189}; 190 191Cipher.prototype.getAuthTag = function getAuthTag() { 192 const ret = this[kHandle].getAuthTag(); 193 if (ret === undefined) 194 throw new ERR_CRYPTO_INVALID_STATE('getAuthTag'); 195 return ret; 196}; 197 198 199function setAuthTag(tagbuf) { 200 if (!isArrayBufferView(tagbuf)) { 201 throw new ERR_INVALID_ARG_TYPE('buffer', 202 ['Buffer', 'TypedArray', 'DataView'], 203 tagbuf); 204 } 205 if (!this[kHandle].setAuthTag(tagbuf)) 206 throw new ERR_CRYPTO_INVALID_STATE('setAuthTag'); 207 return this; 208} 209 210Cipher.prototype.setAAD = function setAAD(aadbuf, options) { 211 if (!isArrayBufferView(aadbuf)) { 212 throw new ERR_INVALID_ARG_TYPE('buffer', 213 ['Buffer', 'TypedArray', 'DataView'], 214 aadbuf); 215 } 216 217 const plaintextLength = getUIntOption(options, 'plaintextLength'); 218 if (!this[kHandle].setAAD(aadbuf, plaintextLength)) 219 throw new ERR_CRYPTO_INVALID_STATE('setAAD'); 220 return this; 221}; 222 223function Cipheriv(cipher, key, iv, options) { 224 if (!(this instanceof Cipheriv)) 225 return new Cipheriv(cipher, key, iv, options); 226 227 createCipherWithIV.call(this, cipher, key, options, true, iv); 228} 229 230function addCipherPrototypeFunctions(constructor) { 231 constructor.prototype._transform = Cipher.prototype._transform; 232 constructor.prototype._flush = Cipher.prototype._flush; 233 constructor.prototype.update = Cipher.prototype.update; 234 constructor.prototype.final = Cipher.prototype.final; 235 constructor.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; 236 if (constructor === Cipheriv) { 237 constructor.prototype.getAuthTag = Cipher.prototype.getAuthTag; 238 } else { 239 constructor.prototype.setAuthTag = setAuthTag; 240 } 241 constructor.prototype.setAAD = Cipher.prototype.setAAD; 242} 243 244ObjectSetPrototypeOf(Cipheriv.prototype, LazyTransform.prototype); 245ObjectSetPrototypeOf(Cipheriv, LazyTransform); 246addCipherPrototypeFunctions(Cipheriv); 247 248function Decipher(cipher, password, options) { 249 if (!(this instanceof Decipher)) 250 return new Decipher(cipher, password, options); 251 252 createCipher.call(this, cipher, password, options, false); 253} 254 255ObjectSetPrototypeOf(Decipher.prototype, LazyTransform.prototype); 256ObjectSetPrototypeOf(Decipher, LazyTransform); 257addCipherPrototypeFunctions(Decipher); 258 259 260function Decipheriv(cipher, key, iv, options) { 261 if (!(this instanceof Decipheriv)) 262 return new Decipheriv(cipher, key, iv, options); 263 264 createCipherWithIV.call(this, cipher, key, options, false, iv); 265} 266 267ObjectSetPrototypeOf(Decipheriv.prototype, LazyTransform.prototype); 268ObjectSetPrototypeOf(Decipheriv, LazyTransform); 269addCipherPrototypeFunctions(Decipheriv); 270 271module.exports = { 272 Cipher, 273 Cipheriv, 274 Decipher, 275 Decipheriv, 276 privateDecrypt, 277 privateEncrypt, 278 publicDecrypt, 279 publicEncrypt, 280}; 281