Skip to content

Commit f8d3ea9

Browse files
tniessentargos
authored andcommitted
crypto: add support for IEEE-P1363 DSA signatures
PR-URL: #29292 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 600c37c commit f8d3ea9

File tree

5 files changed

+277
-24
lines changed

5 files changed

+277
-24
lines changed

doc/api/crypto.md

+18
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,7 @@ changes:
14051405
-->
14061406

14071407
* `privateKey` {Object | string | Buffer | KeyObject}
1408+
* `dsaEncoding` {string}
14081409
* `padding` {integer}
14091410
* `saltLength` {integer}
14101411
* `outputEncoding` {string} The [encoding][] of the return value.
@@ -1417,6 +1418,10 @@ If `privateKey` is not a [`KeyObject`][], this function behaves as if
14171418
`privateKey` had been passed to [`crypto.createPrivateKey()`][]. If it is an
14181419
object, the following additional properties can be passed:
14191420

1421+
* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
1422+
format of the generated signature. It can be one of the following:
1423+
* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
1424+
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.
14201425
* `padding` {integer} Optional padding value for RSA, one of the following:
14211426
* `crypto.constants.RSA_PKCS1_PADDING` (default)
14221427
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
@@ -1513,6 +1518,7 @@ changes:
15131518
-->
15141519

15151520
* `object` {Object | string | Buffer | KeyObject}
1521+
* `dsaEncoding` {string}
15161522
* `padding` {integer}
15171523
* `saltLength` {integer}
15181524
* `signature` {string | Buffer | TypedArray | DataView}
@@ -1526,6 +1532,10 @@ If `object` is not a [`KeyObject`][], this function behaves as if
15261532
`object` had been passed to [`crypto.createPublicKey()`][]. If it is an
15271533
object, the following additional properties can be passed:
15281534

1535+
* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
1536+
format of the generated signature. It can be one of the following:
1537+
* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
1538+
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.
15291539
* `padding` {integer} Optional padding value for RSA, one of the following:
15301540
* `crypto.constants.RSA_PKCS1_PADDING` (default)
15311541
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
@@ -2891,6 +2901,10 @@ If `key` is not a [`KeyObject`][], this function behaves as if `key` had been
28912901
passed to [`crypto.createPrivateKey()`][]. If it is an object, the following
28922902
additional properties can be passed:
28932903

2904+
* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
2905+
format of the generated signature. It can be one of the following:
2906+
* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
2907+
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.
28942908
* `padding` {integer} Optional padding value for RSA, one of the following:
28952909
* `crypto.constants.RSA_PKCS1_PADDING` (default)
28962910
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
@@ -2944,6 +2958,10 @@ If `key` is not a [`KeyObject`][], this function behaves as if `key` had been
29442958
passed to [`crypto.createPublicKey()`][]. If it is an object, the following
29452959
additional properties can be passed:
29462960

2961+
* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
2962+
format of the generated signature. It can be one of the following:
2963+
* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
2964+
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.
29472965
* `padding` {integer} Optional padding value for RSA, one of the following:
29482966
* `crypto.constants.RSA_PKCS1_PADDING` (default)
29492967
* `crypto.constants.RSA_PKCS1_PSS_PADDING`

lib/internal/crypto/sig.js

+32-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const { validateString } = require('internal/validators');
1111
const {
1212
Sign: _Sign,
1313
Verify: _Verify,
14+
kSigEncDER,
15+
kSigEncP1363,
1416
signOneShot: _signOneShot,
1517
verifyOneShot: _verifyOneShot
1618
} = internalBinding('crypto');
@@ -59,6 +61,20 @@ function getSaltLength(options) {
5961
return getIntOption('saltLength', options);
6062
}
6163

64+
function getDSASignatureEncoding(options) {
65+
if (typeof options === 'object') {
66+
const { dsaEncoding = 'der' } = options;
67+
if (dsaEncoding === 'der')
68+
return kSigEncDER;
69+
else if (dsaEncoding === 'ieee-p1363')
70+
return kSigEncP1363;
71+
else
72+
throw new ERR_INVALID_OPT_VALUE('dsaEncoding', dsaEncoding);
73+
}
74+
75+
return kSigEncDER;
76+
}
77+
6278
function getIntOption(name, options) {
6379
const value = options[name];
6480
if (value !== undefined) {
@@ -81,8 +97,11 @@ Sign.prototype.sign = function sign(options, encoding) {
8197
const rsaPadding = getPadding(options);
8298
const pssSaltLength = getSaltLength(options);
8399

100+
// Options specific to (EC)DSA
101+
const dsaSigEnc = getDSASignatureEncoding(options);
102+
84103
const ret = this[kHandle].sign(data, format, type, passphrase, rsaPadding,
85-
pssSaltLength);
104+
pssSaltLength, dsaSigEnc);
86105

87106
encoding = encoding || getDefaultEncoding();
88107
if (encoding && encoding !== 'buffer')
@@ -117,8 +136,11 @@ function signOneShot(algorithm, data, key) {
117136
const rsaPadding = getPadding(key);
118137
const pssSaltLength = getSaltLength(key);
119138

139+
// Options specific to (EC)DSA
140+
const dsaSigEnc = getDSASignatureEncoding(key);
141+
120142
return _signOneShot(keyData, keyFormat, keyType, keyPassphrase, data,
121-
algorithm, rsaPadding, pssSaltLength);
143+
algorithm, rsaPadding, pssSaltLength, dsaSigEnc);
122144
}
123145

124146
function Verify(algorithm, options) {
@@ -149,13 +171,15 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) {
149171

150172
// Options specific to RSA
151173
const rsaPadding = getPadding(options);
152-
153174
const pssSaltLength = getSaltLength(options);
154175

176+
// Options specific to (EC)DSA
177+
const dsaSigEnc = getDSASignatureEncoding(options);
178+
155179
signature = getArrayBufferView(signature, 'signature', sigEncoding);
156180

157181
return this[kHandle].verify(data, format, type, passphrase, signature,
158-
rsaPadding, pssSaltLength);
182+
rsaPadding, pssSaltLength, dsaSigEnc);
159183
};
160184

161185
function verifyOneShot(algorithm, data, key, signature) {
@@ -181,6 +205,9 @@ function verifyOneShot(algorithm, data, key, signature) {
181205
const rsaPadding = getPadding(key);
182206
const pssSaltLength = getSaltLength(key);
183207

208+
// Options specific to (EC)DSA
209+
const dsaSigEnc = getDSASignatureEncoding(key);
210+
184211
if (!isArrayBufferView(signature)) {
185212
throw new ERR_INVALID_ARG_TYPE(
186213
'signature',
@@ -190,7 +217,7 @@ function verifyOneShot(algorithm, data, key, signature) {
190217
}
191218

192219
return _verifyOneShot(keyData, keyFormat, keyType, keyPassphrase, signature,
193-
data, algorithm, rsaPadding, pssSaltLength);
220+
data, algorithm, rsaPadding, pssSaltLength, dsaSigEnc);
194221
}
195222

196223
module.exports = {

src/node_crypto.cc

+132-4
Original file line numberDiff line numberDiff line change
@@ -4907,6 +4907,9 @@ void CheckThrow(Environment* env, SignBase::Error error) {
49074907
case SignBase::Error::kSignNotInitialised:
49084908
return env->ThrowError("Not initialised");
49094909

4910+
case SignBase::Error::kSignMalformedSignature:
4911+
return env->ThrowError("Malformed signature");
4912+
49104913
case SignBase::Error::kSignInit:
49114914
case SignBase::Error::kSignUpdate:
49124915
case SignBase::Error::kSignPrivateKey:
@@ -5004,6 +5007,89 @@ static int GetDefaultSignPadding(const ManagedEVPPKey& key) {
50045007
RSA_PKCS1_PADDING;
50055008
}
50065009

5010+
static const unsigned int kNoDsaSignature = static_cast<unsigned int>(-1);
5011+
5012+
// Returns the maximum size of each of the integers (r, s) of the DSA signature.
5013+
static unsigned int GetBytesOfRS(const ManagedEVPPKey& pkey) {
5014+
int bits, base_id = EVP_PKEY_base_id(pkey.get());
5015+
5016+
if (base_id == EVP_PKEY_DSA) {
5017+
DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.get());
5018+
// Both r and s are computed mod q, so their width is limited by that of q.
5019+
bits = BN_num_bits(DSA_get0_q(dsa_key));
5020+
} else if (base_id == EVP_PKEY_EC) {
5021+
EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
5022+
const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key);
5023+
bits = EC_GROUP_order_bits(ec_group);
5024+
} else {
5025+
return kNoDsaSignature;
5026+
}
5027+
5028+
return (bits + 7) / 8;
5029+
}
5030+
5031+
static AllocatedBuffer ConvertSignatureToP1363(Environment* env,
5032+
const ManagedEVPPKey& pkey,
5033+
AllocatedBuffer&& signature) {
5034+
unsigned int n = GetBytesOfRS(pkey);
5035+
if (n == kNoDsaSignature)
5036+
return std::move(signature);
5037+
5038+
const unsigned char* sig_data =
5039+
reinterpret_cast<unsigned char*>(signature.data());
5040+
5041+
ECDSA_SIG* asn1_sig = d2i_ECDSA_SIG(nullptr, &sig_data, signature.size());
5042+
if (asn1_sig == nullptr)
5043+
return AllocatedBuffer();
5044+
5045+
AllocatedBuffer buf = env->AllocateManaged(2 * n);
5046+
unsigned char* data = reinterpret_cast<unsigned char*>(buf.data());
5047+
5048+
const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig);
5049+
const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig);
5050+
CHECK_EQ(n, BN_bn2binpad(r, data, n));
5051+
CHECK_EQ(n, BN_bn2binpad(s, data + n, n));
5052+
5053+
ECDSA_SIG_free(asn1_sig);
5054+
5055+
return buf;
5056+
}
5057+
5058+
static ByteSource ConvertSignatureToDER(
5059+
const ManagedEVPPKey& pkey,
5060+
const ArrayBufferViewContents<char>& signature) {
5061+
unsigned int n = GetBytesOfRS(pkey);
5062+
if (n == kNoDsaSignature)
5063+
return ByteSource::Foreign(signature.data(), signature.length());
5064+
5065+
const unsigned char* sig_data =
5066+
reinterpret_cast<const unsigned char*>(signature.data());
5067+
5068+
if (signature.length() != 2 * n)
5069+
return ByteSource();
5070+
5071+
ECDSA_SIG* asn1_sig = ECDSA_SIG_new();
5072+
CHECK_NOT_NULL(asn1_sig);
5073+
BIGNUM* r = BN_new();
5074+
CHECK_NOT_NULL(r);
5075+
BIGNUM* s = BN_new();
5076+
CHECK_NOT_NULL(s);
5077+
CHECK_EQ(r, BN_bin2bn(sig_data, n, r));
5078+
CHECK_EQ(s, BN_bin2bn(sig_data + n, n, s));
5079+
CHECK_EQ(1, ECDSA_SIG_set0(asn1_sig, r, s));
5080+
5081+
unsigned char* data = nullptr;
5082+
int len = i2d_ECDSA_SIG(asn1_sig, &data);
5083+
ECDSA_SIG_free(asn1_sig);
5084+
5085+
if (len <= 0)
5086+
return ByteSource();
5087+
5088+
CHECK_NOT_NULL(data);
5089+
5090+
return ByteSource::Allocated(reinterpret_cast<char*>(data), len);
5091+
}
5092+
50075093
static AllocatedBuffer Node_SignFinal(Environment* env,
50085094
EVPMDPointer&& mdctx,
50095095
const ManagedEVPPKey& pkey,
@@ -5063,7 +5149,8 @@ static inline bool ValidateDSAParameters(EVP_PKEY* key) {
50635149
Sign::SignResult Sign::SignFinal(
50645150
const ManagedEVPPKey& pkey,
50655151
int padding,
5066-
const Maybe<int>& salt_len) {
5152+
const Maybe<int>& salt_len,
5153+
DSASigEnc dsa_sig_enc) {
50675154
if (!mdctx_)
50685155
return SignResult(kSignNotInitialised);
50695156

@@ -5075,6 +5162,10 @@ Sign::SignResult Sign::SignFinal(
50755162
AllocatedBuffer buffer =
50765163
Node_SignFinal(env(), std::move(mdctx), pkey, padding, salt_len);
50775164
Error error = buffer.data() == nullptr ? kSignPrivateKey : kSignOk;
5165+
if (error == kSignOk && dsa_sig_enc == kSigEncP1363) {
5166+
buffer = ConvertSignatureToP1363(env(), pkey, std::move(buffer));
5167+
CHECK_NOT_NULL(buffer.data());
5168+
}
50785169
return SignResult(error, std::move(buffer));
50795170
}
50805171

@@ -5102,10 +5193,15 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
51025193
salt_len = Just<int>(args[offset + 1].As<Int32>()->Value());
51035194
}
51045195

5196+
CHECK(args[offset + 2]->IsInt32());
5197+
DSASigEnc dsa_sig_enc =
5198+
static_cast<DSASigEnc>(args[offset + 2].As<Int32>()->Value());
5199+
51055200
SignResult ret = sign->SignFinal(
51065201
key,
51075202
padding,
5108-
salt_len);
5203+
salt_len,
5204+
dsa_sig_enc);
51095205

51105206
if (ret.error != kSignOk)
51115207
return sign->CheckThrow(ret.error);
@@ -5149,6 +5245,10 @@ void SignOneShot(const FunctionCallbackInfo<Value>& args) {
51495245
rsa_salt_len = Just<int>(args[offset + 3].As<Int32>()->Value());
51505246
}
51515247

5248+
CHECK(args[offset + 4]->IsInt32());
5249+
DSASigEnc dsa_sig_enc =
5250+
static_cast<DSASigEnc>(args[offset + 4].As<Int32>()->Value());
5251+
51525252
EVP_PKEY_CTX* pkctx = nullptr;
51535253
EVPMDPointer mdctx(EVP_MD_CTX_new());
51545254
if (!mdctx ||
@@ -5176,6 +5276,10 @@ void SignOneShot(const FunctionCallbackInfo<Value>& args) {
51765276

51775277
signature.Resize(sig_len);
51785278

5279+
if (dsa_sig_enc == kSigEncP1363) {
5280+
signature = ConvertSignatureToP1363(env, key, std::move(signature));
5281+
}
5282+
51795283
args.GetReturnValue().Set(signature.ToBuffer().ToLocalChecked());
51805284
}
51815285

@@ -5281,6 +5385,17 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
52815385
salt_len = Just<int>(args[offset + 2].As<Int32>()->Value());
52825386
}
52835387

5388+
CHECK(args[offset + 3]->IsInt32());
5389+
DSASigEnc dsa_sig_enc =
5390+
static_cast<DSASigEnc>(args[offset + 3].As<Int32>()->Value());
5391+
5392+
ByteSource signature = ByteSource::Foreign(hbuf.data(), hbuf.length());
5393+
if (dsa_sig_enc == kSigEncP1363) {
5394+
signature = ConvertSignatureToDER(pkey, hbuf);
5395+
if (signature.get() == nullptr)
5396+
return verify->CheckThrow(Error::kSignMalformedSignature);
5397+
}
5398+
52845399
bool verify_result;
52855400
Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding,
52865401
salt_len, &verify_result);
@@ -5324,6 +5439,10 @@ void VerifyOneShot(const FunctionCallbackInfo<Value>& args) {
53245439
rsa_salt_len = Just<int>(args[offset + 4].As<Int32>()->Value());
53255440
}
53265441

5442+
CHECK(args[offset + 5]->IsInt32());
5443+
DSASigEnc dsa_sig_enc =
5444+
static_cast<DSASigEnc>(args[offset + 5].As<Int32>()->Value());
5445+
53275446
EVP_PKEY_CTX* pkctx = nullptr;
53285447
EVPMDPointer mdctx(EVP_MD_CTX_new());
53295448
if (!mdctx ||
@@ -5334,11 +5453,18 @@ void VerifyOneShot(const FunctionCallbackInfo<Value>& args) {
53345453
if (!ApplyRSAOptions(key, pkctx, rsa_padding, rsa_salt_len))
53355454
return CheckThrow(env, SignBase::Error::kSignPublicKey);
53365455

5456+
ByteSource sig_bytes = ByteSource::Foreign(sig.data(), sig.length());
5457+
if (dsa_sig_enc == kSigEncP1363) {
5458+
sig_bytes = ConvertSignatureToDER(key, sig);
5459+
if (!sig_bytes)
5460+
return CheckThrow(env, SignBase::Error::kSignMalformedSignature);
5461+
}
5462+
53375463
bool verify_result;
53385464
const int r = EVP_DigestVerify(
53395465
mdctx.get(),
5340-
reinterpret_cast<const unsigned char*>(sig.data()),
5341-
sig.length(),
5466+
reinterpret_cast<const unsigned char*>(sig_bytes.get()),
5467+
sig_bytes.size(),
53425468
reinterpret_cast<const unsigned char*>(data.data()),
53435469
data.length());
53445470
switch (r) {
@@ -7126,6 +7252,8 @@ void Initialize(Local<Object> target,
71267252
NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
71277253
NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
71287254
NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);
7255+
NODE_DEFINE_CONSTANT(target, kSigEncDER);
7256+
NODE_DEFINE_CONSTANT(target, kSigEncP1363);
71297257
env->SetMethod(target, "randomBytes", RandomBytes);
71307258
env->SetMethod(target, "signOneShot", SignOneShot);
71317259
env->SetMethod(target, "verifyOneShot", VerifyOneShot);

0 commit comments

Comments
 (0)