mirror of
https://github.com/nodejs/node.git
synced 2025-05-03 02:06:12 +00:00

getDefaultEncoding() always returns 'buffer' in Node.js 20. It requires some careful justification but the default encoding can be eliminated from sig.js entirely. In Sign.prototype.update, we can safely remove the conditional assignment of getDefaultEncoding() to encoding. This is because SignUpdate() in crypto_sig.cc internally calls node::crypto::Decode, which returns UTF8 for falsy encoding values. In other words, with the conditional assignment, StringBytes::Write() ultimately receives the encoding BUFFER, and without the conditional assignment, it receives the encoding UTF8. However, StringBytes::Write() treats both encodings identically, so there is no need to deviate from the internal default encoding UTF8. In Sign.prototype.sign, we can also safely remove the conditional assignment of getDefaultEncoding() to encoding. Whether encoding is falsy or 'buffer' makes no difference. In Verify.prototype.verify, we can also safely remove the conditional assignment of getDefaultEncoding() to sigEncoding. This is because the function passes the sigEncoding to getArrayBufferOrView(), which passes it to Buffer.from(). If sigEncoding is 'buffer', getArrayBufferOrView() instead passes 'utf8' to Buffer.from(). Because the default encoding of Buffer.from() is 'utf8', passing a falsy encoding to getArrayBufferOrView() instead of 'buffer' results in the same behavior. Refs: https://github.com/nodejs/node/pull/47182 PR-URL: https://github.com/nodejs/node/pull/49145 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
302 lines
6.8 KiB
JavaScript
302 lines
6.8 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,
|
|
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) {
|
|
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);
|
|
|
|
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);
|
|
|
|
// 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,
|
|
};
|