Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f8d3ea9

Browse filesBrowse files
tniessentargos
authored andcommitted
crypto: add support for IEEE-P1363 DSA signatures
PR-URL: #29292 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 600c37c commit f8d3ea9
Copy full SHA for f8d3ea9

File tree

Expand file treeCollapse file tree

5 files changed

+277
-24
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+277
-24
lines changed
Open diff view settings
Collapse file

‎doc/api/crypto.md‎

Copy file name to clipboardExpand all lines: doc/api/crypto.md
+18Lines changed: 18 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
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`
Collapse file

‎lib/internal/crypto/sig.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/sig.js
+32-5Lines changed: 32 additions & 5 deletions
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 = {
Collapse file

‎src/node_crypto.cc‎

Copy file name to clipboardExpand all lines: src/node_crypto.cc
+132-4Lines changed: 132 additions & 4 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.