1'use strict'; 2 3const { 4 ObjectSetPrototypeOf, 5 ReflectApply, 6 StringPrototypeToLowerCase, 7} = primordials; 8 9const { 10 CipherBase, 11 privateDecrypt: _privateDecrypt, 12 privateEncrypt: _privateEncrypt, 13 publicDecrypt: _publicDecrypt, 14 publicEncrypt: _publicEncrypt, 15 getCipherInfo: _getCipherInfo, 16} = internalBinding('crypto'); 17 18const { 19 crypto: { 20 RSA_PKCS1_OAEP_PADDING, 21 RSA_PKCS1_PADDING, 22 }, 23} = internalBinding('constants'); 24 25const { 26 codes: { 27 ERR_CRYPTO_INVALID_STATE, 28 ERR_INVALID_ARG_TYPE, 29 ERR_INVALID_ARG_VALUE, 30 ERR_UNKNOWN_ENCODING, 31 }, 32} = require('internal/errors'); 33 34const { 35 validateEncoding, 36 validateInt32, 37 validateObject, 38 validateString, 39} = require('internal/validators'); 40 41const { 42 preparePrivateKey, 43 preparePublicOrPrivateKey, 44 prepareSecretKey, 45} = require('internal/crypto/keys'); 46 47const { 48 getDefaultEncoding, 49 getArrayBufferOrView, 50 getStringOption, 51 kHandle, 52} = require('internal/crypto/util'); 53 54const { 55 isArrayBufferView, 56} = require('internal/util/types'); 57 58const assert = require('internal/assert'); 59 60const LazyTransform = require('internal/streams/lazy_transform'); 61 62const { normalizeEncoding } = require('internal/util'); 63 64const { StringDecoder } = require('string_decoder'); 65 66function rsaFunctionFor(method, defaultPadding, keyType) { 67 return (options, buffer) => { 68 const { format, type, data, passphrase } = 69 keyType === 'private' ? 70 preparePrivateKey(options) : 71 preparePublicOrPrivateKey(options); 72 const padding = options.padding || defaultPadding; 73 const { oaepHash, encoding } = options; 74 let { oaepLabel } = options; 75 if (oaepHash !== undefined) 76 validateString(oaepHash, 'key.oaepHash'); 77 if (oaepLabel !== undefined) 78 oaepLabel = getArrayBufferOrView(oaepLabel, 'key.oaepLabel', encoding); 79 buffer = getArrayBufferOrView(buffer, 'buffer', encoding); 80 return method(data, format, type, passphrase, buffer, padding, oaepHash, 81 oaepLabel); 82 }; 83} 84 85const publicEncrypt = rsaFunctionFor(_publicEncrypt, RSA_PKCS1_OAEP_PADDING, 86 'public'); 87const publicDecrypt = rsaFunctionFor(_publicDecrypt, RSA_PKCS1_PADDING, 88 'public'); 89const privateEncrypt = rsaFunctionFor(_privateEncrypt, RSA_PKCS1_PADDING, 90 'private'); 91const privateDecrypt = rsaFunctionFor(_privateDecrypt, RSA_PKCS1_OAEP_PADDING, 92 'private'); 93 94function getDecoder(decoder, encoding) { 95 const normalizedEncoding = normalizeEncoding(encoding); 96 decoder = decoder || new StringDecoder(encoding); 97 if (decoder.encoding !== normalizedEncoding) { 98 if (normalizedEncoding === undefined) { 99 throw new ERR_UNKNOWN_ENCODING(encoding); 100 } 101 assert(false, 'Cannot change encoding'); 102 } 103 return decoder; 104} 105 106function getUIntOption(options, key) { 107 let value; 108 if (options && (value = options[key]) != null) { 109 if (value >>> 0 !== value) 110 throw new ERR_INVALID_ARG_VALUE(`options.${key}`, value); 111 return value; 112 } 113 return -1; 114} 115 116function createCipherBase(cipher, credential, options, decipher, iv) { 117 const authTagLength = getUIntOption(options, 'authTagLength'); 118 this[kHandle] = new CipherBase(decipher); 119 if (iv === undefined) { 120 this[kHandle].init(cipher, credential, authTagLength); 121 } else { 122 this[kHandle].initiv(cipher, credential, iv, authTagLength); 123 } 124 this._decoder = null; 125 126 ReflectApply(LazyTransform, this, [options]); 127} 128 129function createCipher(cipher, password, options, decipher) { 130 validateString(cipher, 'cipher'); 131 password = getArrayBufferOrView(password, 'password'); 132 133 ReflectApply(createCipherBase, this, [cipher, password, options, decipher]); 134} 135 136function createCipherWithIV(cipher, key, options, decipher, iv) { 137 validateString(cipher, 'cipher'); 138 const encoding = getStringOption(options, 'encoding'); 139 key = prepareSecretKey(key, encoding); 140 iv = iv === null ? null : getArrayBufferOrView(iv, 'iv'); 141 ReflectApply(createCipherBase, this, [cipher, key, options, decipher, iv]); 142} 143 144// The Cipher class is part of the legacy Node.js crypto API. It exposes 145// a stream-based encryption/decryption model. For backwards compatibility 146// the Cipher class is defined using the legacy function syntax rather than 147// ES6 classes. 148 149function Cipher(cipher, password, options) { 150 if (!(this instanceof Cipher)) 151 return new Cipher(cipher, password, options); 152 153 ReflectApply(createCipher, this, [cipher, password, options, true]); 154} 155 156ObjectSetPrototypeOf(Cipher.prototype, LazyTransform.prototype); 157ObjectSetPrototypeOf(Cipher, LazyTransform); 158 159Cipher.prototype._transform = function _transform(chunk, encoding, callback) { 160 this.push(this[kHandle].update(chunk, encoding)); 161 callback(); 162}; 163 164Cipher.prototype._flush = function _flush(callback) { 165 try { 166 this.push(this[kHandle].final()); 167 } catch (e) { 168 callback(e); 169 return; 170 } 171 callback(); 172}; 173 174Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) { 175 const encoding = getDefaultEncoding(); 176 inputEncoding = inputEncoding || encoding; 177 outputEncoding = outputEncoding || encoding; 178 179 if (typeof data === 'string') { 180 validateEncoding(data, inputEncoding); 181 } else if (!isArrayBufferView(data)) { 182 throw new ERR_INVALID_ARG_TYPE( 183 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); 184 } 185 186 const ret = this[kHandle].update(data, inputEncoding); 187 188 if (outputEncoding && outputEncoding !== 'buffer') { 189 this._decoder = getDecoder(this._decoder, outputEncoding); 190 return this._decoder.write(ret); 191 } 192 193 return ret; 194}; 195 196 197Cipher.prototype.final = function final(outputEncoding) { 198 outputEncoding = outputEncoding || getDefaultEncoding(); 199 const ret = this[kHandle].final(); 200 201 if (outputEncoding && outputEncoding !== 'buffer') { 202 this._decoder = getDecoder(this._decoder, outputEncoding); 203 return this._decoder.end(ret); 204 } 205 206 return ret; 207}; 208 209 210Cipher.prototype.setAutoPadding = function setAutoPadding(ap) { 211 if (!this[kHandle].setAutoPadding(!!ap)) 212 throw new ERR_CRYPTO_INVALID_STATE('setAutoPadding'); 213 return this; 214}; 215 216Cipher.prototype.getAuthTag = function getAuthTag() { 217 const ret = this[kHandle].getAuthTag(); 218 if (ret === undefined) 219 throw new ERR_CRYPTO_INVALID_STATE('getAuthTag'); 220 return ret; 221}; 222 223 224function setAuthTag(tagbuf, encoding) { 225 tagbuf = getArrayBufferOrView(tagbuf, 'buffer', encoding); 226 if (!this[kHandle].setAuthTag(tagbuf)) 227 throw new ERR_CRYPTO_INVALID_STATE('setAuthTag'); 228 return this; 229} 230 231Cipher.prototype.setAAD = function setAAD(aadbuf, options) { 232 const encoding = getStringOption(options, 'encoding'); 233 const plaintextLength = getUIntOption(options, 'plaintextLength'); 234 aadbuf = getArrayBufferOrView(aadbuf, 'aadbuf', encoding); 235 if (!this[kHandle].setAAD(aadbuf, plaintextLength)) 236 throw new ERR_CRYPTO_INVALID_STATE('setAAD'); 237 return this; 238}; 239 240// The Cipheriv class is part of the legacy Node.js crypto API. It exposes 241// a stream-based encryption/decryption model. For backwards compatibility 242// the Cipheriv class is defined using the legacy function syntax rather than 243// ES6 classes. 244 245function Cipheriv(cipher, key, iv, options) { 246 if (!(this instanceof Cipheriv)) 247 return new Cipheriv(cipher, key, iv, options); 248 249 ReflectApply(createCipherWithIV, this, [cipher, key, options, true, iv]); 250} 251 252function addCipherPrototypeFunctions(constructor) { 253 constructor.prototype._transform = Cipher.prototype._transform; 254 constructor.prototype._flush = Cipher.prototype._flush; 255 constructor.prototype.update = Cipher.prototype.update; 256 constructor.prototype.final = Cipher.prototype.final; 257 constructor.prototype.setAutoPadding = Cipher.prototype.setAutoPadding; 258 if (constructor === Cipheriv) { 259 constructor.prototype.getAuthTag = Cipher.prototype.getAuthTag; 260 } else { 261 constructor.prototype.setAuthTag = setAuthTag; 262 } 263 constructor.prototype.setAAD = Cipher.prototype.setAAD; 264} 265 266ObjectSetPrototypeOf(Cipheriv.prototype, LazyTransform.prototype); 267ObjectSetPrototypeOf(Cipheriv, LazyTransform); 268addCipherPrototypeFunctions(Cipheriv); 269 270// The Decipher class is part of the legacy Node.js crypto API. It exposes 271// a stream-based encryption/decryption model. For backwards compatibility 272// the Decipher class is defined using the legacy function syntax rather than 273// ES6 classes. 274 275function Decipher(cipher, password, options) { 276 if (!(this instanceof Decipher)) 277 return new Decipher(cipher, password, options); 278 279 ReflectApply(createCipher, this, [cipher, password, options, false]); 280} 281 282ObjectSetPrototypeOf(Decipher.prototype, LazyTransform.prototype); 283ObjectSetPrototypeOf(Decipher, LazyTransform); 284addCipherPrototypeFunctions(Decipher); 285 286// The Decipheriv class is part of the legacy Node.js crypto API. It exposes 287// a stream-based encryption/decryption model. For backwards compatibility 288// the Decipheriv class is defined using the legacy function syntax rather than 289// ES6 classes. 290 291function Decipheriv(cipher, key, iv, options) { 292 if (!(this instanceof Decipheriv)) 293 return new Decipheriv(cipher, key, iv, options); 294 295 ReflectApply(createCipherWithIV, this, [cipher, key, options, false, iv]); 296} 297 298ObjectSetPrototypeOf(Decipheriv.prototype, LazyTransform.prototype); 299ObjectSetPrototypeOf(Decipheriv, LazyTransform); 300addCipherPrototypeFunctions(Decipheriv); 301 302function getCipherInfo(nameOrNid, options) { 303 if (typeof nameOrNid !== 'string' && typeof nameOrNid !== 'number') { 304 throw new ERR_INVALID_ARG_TYPE( 305 'nameOrNid', 306 ['string', 'number'], 307 nameOrNid); 308 } 309 if (typeof nameOrNid === 'number') 310 validateInt32(nameOrNid, 'nameOrNid'); 311 let keyLength, ivLength; 312 if (options !== undefined) { 313 validateObject(options, 'options'); 314 ({ keyLength, ivLength } = options); 315 if (keyLength !== undefined) 316 validateInt32(keyLength, 'options.keyLength'); 317 if (ivLength !== undefined) 318 validateInt32(ivLength, 'options.ivLength'); 319 } 320 321 const ret = _getCipherInfo({}, nameOrNid, keyLength, ivLength); 322 if (ret !== undefined) { 323 if (ret.name) ret.name = StringPrototypeToLowerCase(ret.name); 324 if (ret.type) ret.type = StringPrototypeToLowerCase(ret.type); 325 } 326 return ret; 327} 328 329module.exports = { 330 Cipher, 331 Cipheriv, 332 Decipher, 333 Decipheriv, 334 privateDecrypt, 335 privateEncrypt, 336 publicDecrypt, 337 publicEncrypt, 338 getCipherInfo, 339}; 340