1'use strict'; 2 3const { 4 FunctionPrototypeCall, 5 ObjectSetPrototypeOf, 6 ReflectApply, 7} = primordials; 8 9const { 10 codes: { 11 ERR_CRYPTO_SIGN_KEY_REQUIRED, 12 ERR_INVALID_ARG_TYPE, 13 ERR_INVALID_ARG_VALUE, 14 }, 15} = require('internal/errors'); 16 17const { 18 validateFunction, 19 validateEncoding, 20 validateString, 21} = require('internal/validators'); 22 23const { 24 Sign: _Sign, 25 SignJob, 26 Verify: _Verify, 27 kCryptoJobAsync, 28 kCryptoJobSync, 29 kSigEncDER, 30 kSigEncP1363, 31 kSignJobModeSign, 32 kSignJobModeVerify, 33} = internalBinding('crypto'); 34 35const { 36 getArrayBufferOrView, 37 getDefaultEncoding, 38 kHandle, 39} = require('internal/crypto/util'); 40 41const { 42 preparePrivateKey, 43 preparePublicOrPrivateKey, 44} = require('internal/crypto/keys'); 45 46const { Writable } = require('stream'); 47 48const { Buffer } = require('buffer'); 49 50const { 51 isArrayBufferView, 52} = require('internal/util/types'); 53 54function Sign(algorithm, options) { 55 if (!(this instanceof Sign)) 56 return new Sign(algorithm, options); 57 validateString(algorithm, 'algorithm'); 58 this[kHandle] = new _Sign(); 59 this[kHandle].init(algorithm); 60 61 ReflectApply(Writable, this, [options]); 62} 63 64ObjectSetPrototypeOf(Sign.prototype, Writable.prototype); 65ObjectSetPrototypeOf(Sign, Writable); 66 67Sign.prototype._write = function _write(chunk, encoding, callback) { 68 this.update(chunk, encoding); 69 callback(); 70}; 71 72Sign.prototype.update = function update(data, encoding) { 73 encoding = encoding || getDefaultEncoding(); 74 75 if (typeof data === 'string') { 76 validateEncoding(data, encoding); 77 } else if (!isArrayBufferView(data)) { 78 throw new ERR_INVALID_ARG_TYPE( 79 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); 80 } 81 82 this[kHandle].update(data, encoding); 83 return this; 84}; 85 86function getPadding(options) { 87 return getIntOption('padding', options); 88} 89 90function getSaltLength(options) { 91 return getIntOption('saltLength', options); 92} 93 94function getDSASignatureEncoding(options) { 95 if (typeof options === 'object') { 96 const { dsaEncoding = 'der' } = options; 97 if (dsaEncoding === 'der') 98 return kSigEncDER; 99 else if (dsaEncoding === 'ieee-p1363') 100 return kSigEncP1363; 101 throw new ERR_INVALID_ARG_VALUE('options.dsaEncoding', dsaEncoding); 102 } 103 104 return kSigEncDER; 105} 106 107function getIntOption(name, options) { 108 const value = options[name]; 109 if (value !== undefined) { 110 if (value === value >> 0) { 111 return value; 112 } 113 throw new ERR_INVALID_ARG_VALUE(`options.${name}`, value); 114 } 115 return undefined; 116} 117 118Sign.prototype.sign = function sign(options, encoding) { 119 if (!options) 120 throw new ERR_CRYPTO_SIGN_KEY_REQUIRED(); 121 122 const { data, format, type, passphrase } = preparePrivateKey(options, true); 123 124 // Options specific to RSA 125 const rsaPadding = getPadding(options); 126 const pssSaltLength = getSaltLength(options); 127 128 // Options specific to (EC)DSA 129 const dsaSigEnc = getDSASignatureEncoding(options); 130 131 const ret = this[kHandle].sign(data, format, type, passphrase, rsaPadding, 132 pssSaltLength, dsaSigEnc); 133 134 encoding = encoding || getDefaultEncoding(); 135 if (encoding && encoding !== 'buffer') 136 return ret.toString(encoding); 137 138 return ret; 139}; 140 141function signOneShot(algorithm, data, key, callback) { 142 if (algorithm != null) 143 validateString(algorithm, 'algorithm'); 144 145 if (callback !== undefined) 146 validateFunction(callback, 'callback'); 147 148 data = getArrayBufferOrView(data, 'data'); 149 150 if (!key) 151 throw new ERR_CRYPTO_SIGN_KEY_REQUIRED(); 152 153 // Options specific to RSA 154 const rsaPadding = getPadding(key); 155 const pssSaltLength = getSaltLength(key); 156 157 // Options specific to (EC)DSA 158 const dsaSigEnc = getDSASignatureEncoding(key); 159 160 const { 161 data: keyData, 162 format: keyFormat, 163 type: keyType, 164 passphrase: keyPassphrase, 165 } = preparePrivateKey(key); 166 167 const job = new SignJob( 168 callback ? kCryptoJobAsync : kCryptoJobSync, 169 kSignJobModeSign, 170 keyData, 171 keyFormat, 172 keyType, 173 keyPassphrase, 174 data, 175 algorithm, 176 pssSaltLength, 177 rsaPadding, 178 dsaSigEnc); 179 180 if (!callback) { 181 const { 0: err, 1: signature } = job.run(); 182 if (err !== undefined) 183 throw err; 184 185 return Buffer.from(signature); 186 } 187 188 job.ondone = (error, signature) => { 189 if (error) return FunctionPrototypeCall(callback, job, error); 190 FunctionPrototypeCall(callback, job, null, Buffer.from(signature)); 191 }; 192 job.run(); 193} 194 195function Verify(algorithm, options) { 196 if (!(this instanceof Verify)) 197 return new Verify(algorithm, options); 198 validateString(algorithm, 'algorithm'); 199 this[kHandle] = new _Verify(); 200 this[kHandle].init(algorithm); 201 202 ReflectApply(Writable, this, [options]); 203} 204 205ObjectSetPrototypeOf(Verify.prototype, Writable.prototype); 206ObjectSetPrototypeOf(Verify, Writable); 207 208Verify.prototype._write = Sign.prototype._write; 209Verify.prototype.update = Sign.prototype.update; 210 211Verify.prototype.verify = function verify(options, signature, sigEncoding) { 212 const { 213 data, 214 format, 215 type, 216 passphrase, 217 } = preparePublicOrPrivateKey(options, true); 218 219 sigEncoding = sigEncoding || getDefaultEncoding(); 220 221 // Options specific to RSA 222 const rsaPadding = getPadding(options); 223 const pssSaltLength = getSaltLength(options); 224 225 // Options specific to (EC)DSA 226 const dsaSigEnc = getDSASignatureEncoding(options); 227 228 signature = getArrayBufferOrView(signature, 'signature', sigEncoding); 229 230 return this[kHandle].verify(data, format, type, passphrase, signature, 231 rsaPadding, pssSaltLength, dsaSigEnc); 232}; 233 234function verifyOneShot(algorithm, data, key, signature, callback) { 235 if (algorithm != null) 236 validateString(algorithm, 'algorithm'); 237 238 if (callback !== undefined) 239 validateFunction(callback, 'callback'); 240 241 data = getArrayBufferOrView(data, 'data'); 242 243 if (!isArrayBufferView(data)) { 244 throw new ERR_INVALID_ARG_TYPE( 245 'data', 246 ['Buffer', 'TypedArray', 'DataView'], 247 data, 248 ); 249 } 250 251 // Options specific to RSA 252 const rsaPadding = getPadding(key); 253 const pssSaltLength = getSaltLength(key); 254 255 // Options specific to (EC)DSA 256 const dsaSigEnc = getDSASignatureEncoding(key); 257 258 if (!isArrayBufferView(signature)) { 259 throw new ERR_INVALID_ARG_TYPE( 260 'signature', 261 ['Buffer', 'TypedArray', 'DataView'], 262 signature, 263 ); 264 } 265 266 const { 267 data: keyData, 268 format: keyFormat, 269 type: keyType, 270 passphrase: keyPassphrase, 271 } = preparePublicOrPrivateKey(key); 272 273 const job = new SignJob( 274 callback ? kCryptoJobAsync : kCryptoJobSync, 275 kSignJobModeVerify, 276 keyData, 277 keyFormat, 278 keyType, 279 keyPassphrase, 280 data, 281 algorithm, 282 pssSaltLength, 283 rsaPadding, 284 dsaSigEnc, 285 signature); 286 287 if (!callback) { 288 const { 0: err, 1: result } = job.run(); 289 if (err !== undefined) 290 throw err; 291 292 return result; 293 } 294 295 job.ondone = (error, result) => { 296 if (error) return FunctionPrototypeCall(callback, job, error); 297 FunctionPrototypeCall(callback, job, null, result); 298 }; 299 job.run(); 300} 301 302module.exports = { 303 Sign, 304 signOneShot, 305 Verify, 306 verifyOneShot, 307}; 308