mirror of
https://github.com/nodejs/node.git
synced 2025-05-01 08:42:45 +00:00

Refs: https://github.com/nodejs/node/pull/41660 PR-URL: https://github.com/nodejs/node/pull/41678 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
308 lines
6.9 KiB
JavaScript
308 lines
6.9 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
FunctionPrototypeCall,
|
|
ObjectSetPrototypeOf,
|
|
ReflectApply,
|
|
} = primordials;
|
|
|
|
const {
|
|
codes: {
|
|
ERR_CRYPTO_SIGN_KEY_REQUIRED,
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_INVALID_ARG_VALUE,
|
|
}
|
|
} = require('internal/errors');
|
|
|
|
const {
|
|
validateFunction,
|
|
validateEncoding,
|
|
validateString,
|
|
} = require('internal/validators');
|
|
|
|
const {
|
|
Sign: _Sign,
|
|
SignJob,
|
|
Verify: _Verify,
|
|
kCryptoJobAsync,
|
|
kCryptoJobSync,
|
|
kSigEncDER,
|
|
kSigEncP1363,
|
|
kSignJobModeSign,
|
|
kSignJobModeVerify,
|
|
} = internalBinding('crypto');
|
|
|
|
const {
|
|
getArrayBufferOrView,
|
|
getDefaultEncoding,
|
|
kHandle,
|
|
} = require('internal/crypto/util');
|
|
|
|
const {
|
|
preparePrivateKey,
|
|
preparePublicOrPrivateKey,
|
|
} = require('internal/crypto/keys');
|
|
|
|
const { Writable } = require('stream');
|
|
|
|
const { Buffer } = require('buffer');
|
|
|
|
const {
|
|
isArrayBufferView,
|
|
} = require('internal/util/types');
|
|
|
|
function Sign(algorithm, options) {
|
|
if (!(this instanceof Sign))
|
|
return new Sign(algorithm, options);
|
|
validateString(algorithm, 'algorithm');
|
|
this[kHandle] = new _Sign();
|
|
this[kHandle].init(algorithm);
|
|
|
|
ReflectApply(Writable, this, [options]);
|
|
}
|
|
|
|
ObjectSetPrototypeOf(Sign.prototype, Writable.prototype);
|
|
ObjectSetPrototypeOf(Sign, Writable);
|
|
|
|
Sign.prototype._write = function _write(chunk, encoding, callback) {
|
|
this.update(chunk, encoding);
|
|
callback();
|
|
};
|
|
|
|
Sign.prototype.update = function update(data, encoding) {
|
|
encoding = encoding || getDefaultEncoding();
|
|
|
|
if (typeof data === 'string') {
|
|
validateEncoding(data, encoding);
|
|
} else if (!isArrayBufferView(data)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data);
|
|
}
|
|
|
|
this[kHandle].update(data, encoding);
|
|
return this;
|
|
};
|
|
|
|
function getPadding(options) {
|
|
return getIntOption('padding', options);
|
|
}
|
|
|
|
function getSaltLength(options) {
|
|
return getIntOption('saltLength', options);
|
|
}
|
|
|
|
function getDSASignatureEncoding(options) {
|
|
if (typeof options === 'object') {
|
|
const { dsaEncoding = 'der' } = options;
|
|
if (dsaEncoding === 'der')
|
|
return kSigEncDER;
|
|
else if (dsaEncoding === 'ieee-p1363')
|
|
return kSigEncP1363;
|
|
throw new ERR_INVALID_ARG_VALUE('options.dsaEncoding', dsaEncoding);
|
|
}
|
|
|
|
return kSigEncDER;
|
|
}
|
|
|
|
function getIntOption(name, options) {
|
|
const value = options[name];
|
|
if (value !== undefined) {
|
|
if (value === value >> 0) {
|
|
return value;
|
|
}
|
|
throw new ERR_INVALID_ARG_VALUE(`options.${name}`, value);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
Sign.prototype.sign = function sign(options, encoding) {
|
|
if (!options)
|
|
throw new ERR_CRYPTO_SIGN_KEY_REQUIRED();
|
|
|
|
const { data, format, type, passphrase } = preparePrivateKey(options, true);
|
|
|
|
// Options specific to RSA
|
|
const rsaPadding = getPadding(options);
|
|
const pssSaltLength = getSaltLength(options);
|
|
|
|
// Options specific to (EC)DSA
|
|
const dsaSigEnc = getDSASignatureEncoding(options);
|
|
|
|
const ret = this[kHandle].sign(data, format, type, passphrase, rsaPadding,
|
|
pssSaltLength, dsaSigEnc);
|
|
|
|
encoding = encoding || getDefaultEncoding();
|
|
if (encoding && encoding !== 'buffer')
|
|
return ret.toString(encoding);
|
|
|
|
return ret;
|
|
};
|
|
|
|
function signOneShot(algorithm, data, key, callback) {
|
|
if (algorithm != null)
|
|
validateString(algorithm, 'algorithm');
|
|
|
|
if (callback !== undefined)
|
|
validateFunction(callback, 'callback');
|
|
|
|
data = getArrayBufferOrView(data, 'data');
|
|
|
|
if (!key)
|
|
throw new ERR_CRYPTO_SIGN_KEY_REQUIRED();
|
|
|
|
// Options specific to RSA
|
|
const rsaPadding = getPadding(key);
|
|
const pssSaltLength = getSaltLength(key);
|
|
|
|
// Options specific to (EC)DSA
|
|
const dsaSigEnc = getDSASignatureEncoding(key);
|
|
|
|
const {
|
|
data: keyData,
|
|
format: keyFormat,
|
|
type: keyType,
|
|
passphrase: keyPassphrase
|
|
} = preparePrivateKey(key);
|
|
|
|
const job = new SignJob(
|
|
callback ? kCryptoJobAsync : kCryptoJobSync,
|
|
kSignJobModeSign,
|
|
keyData,
|
|
keyFormat,
|
|
keyType,
|
|
keyPassphrase,
|
|
data,
|
|
algorithm,
|
|
pssSaltLength,
|
|
rsaPadding,
|
|
dsaSigEnc);
|
|
|
|
if (!callback) {
|
|
const { 0: err, 1: signature } = job.run();
|
|
if (err !== undefined)
|
|
throw err;
|
|
|
|
return Buffer.from(signature);
|
|
}
|
|
|
|
job.ondone = (error, signature) => {
|
|
if (error) return FunctionPrototypeCall(callback, job, error);
|
|
FunctionPrototypeCall(callback, job, null, Buffer.from(signature));
|
|
};
|
|
job.run();
|
|
}
|
|
|
|
function Verify(algorithm, options) {
|
|
if (!(this instanceof Verify))
|
|
return new Verify(algorithm, options);
|
|
validateString(algorithm, 'algorithm');
|
|
this[kHandle] = new _Verify();
|
|
this[kHandle].init(algorithm);
|
|
|
|
ReflectApply(Writable, this, [options]);
|
|
}
|
|
|
|
ObjectSetPrototypeOf(Verify.prototype, Writable.prototype);
|
|
ObjectSetPrototypeOf(Verify, Writable);
|
|
|
|
Verify.prototype._write = Sign.prototype._write;
|
|
Verify.prototype.update = Sign.prototype.update;
|
|
|
|
Verify.prototype.verify = function verify(options, signature, sigEncoding) {
|
|
const {
|
|
data,
|
|
format,
|
|
type,
|
|
passphrase
|
|
} = preparePublicOrPrivateKey(options, true);
|
|
|
|
sigEncoding = sigEncoding || getDefaultEncoding();
|
|
|
|
// Options specific to RSA
|
|
const rsaPadding = getPadding(options);
|
|
const pssSaltLength = getSaltLength(options);
|
|
|
|
// Options specific to (EC)DSA
|
|
const dsaSigEnc = getDSASignatureEncoding(options);
|
|
|
|
signature = getArrayBufferOrView(signature, 'signature', sigEncoding);
|
|
|
|
return this[kHandle].verify(data, format, type, passphrase, signature,
|
|
rsaPadding, pssSaltLength, dsaSigEnc);
|
|
};
|
|
|
|
function verifyOneShot(algorithm, data, key, signature, callback) {
|
|
if (algorithm != null)
|
|
validateString(algorithm, 'algorithm');
|
|
|
|
if (callback !== undefined)
|
|
validateFunction(callback, 'callback');
|
|
|
|
data = getArrayBufferOrView(data, 'data');
|
|
|
|
if (!isArrayBufferView(data)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'data',
|
|
['Buffer', 'TypedArray', 'DataView'],
|
|
data
|
|
);
|
|
}
|
|
|
|
// Options specific to RSA
|
|
const rsaPadding = getPadding(key);
|
|
const pssSaltLength = getSaltLength(key);
|
|
|
|
// Options specific to (EC)DSA
|
|
const dsaSigEnc = getDSASignatureEncoding(key);
|
|
|
|
if (!isArrayBufferView(signature)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'signature',
|
|
['Buffer', 'TypedArray', 'DataView'],
|
|
signature
|
|
);
|
|
}
|
|
|
|
const {
|
|
data: keyData,
|
|
format: keyFormat,
|
|
type: keyType,
|
|
passphrase: keyPassphrase
|
|
} = preparePublicOrPrivateKey(key);
|
|
|
|
const job = new SignJob(
|
|
callback ? kCryptoJobAsync : kCryptoJobSync,
|
|
kSignJobModeVerify,
|
|
keyData,
|
|
keyFormat,
|
|
keyType,
|
|
keyPassphrase,
|
|
data,
|
|
algorithm,
|
|
pssSaltLength,
|
|
rsaPadding,
|
|
dsaSigEnc,
|
|
signature);
|
|
|
|
if (!callback) {
|
|
const { 0: err, 1: result } = job.run();
|
|
if (err !== undefined)
|
|
throw err;
|
|
|
|
return result;
|
|
}
|
|
|
|
job.ondone = (error, result) => {
|
|
if (error) return FunctionPrototypeCall(callback, job, error);
|
|
FunctionPrototypeCall(callback, job, null, result);
|
|
};
|
|
job.run();
|
|
}
|
|
|
|
module.exports = {
|
|
Sign,
|
|
signOneShot,
|
|
Verify,
|
|
verifyOneShot,
|
|
};
|