summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2019-08-21 00:05:55 +0200
committerTobias Nießen <tniessen@tnie.de>2019-11-20 12:55:47 -0400
commitc63af4fea041673eb7c33f6df3c474d4537fe5eb (patch)
tree73e1942eb0ca50d5414fbe4619099c49d0666494 /src
parent80efb80f3f9dffb412aa1a41ab36c843c90c60e5 (diff)
downloadandroid-node-v8-c63af4fea041673eb7c33f6df3c474d4537fe5eb.tar.gz
android-node-v8-c63af4fea041673eb7c33f6df3c474d4537fe5eb.tar.bz2
android-node-v8-c63af4fea041673eb7c33f6df3c474d4537fe5eb.zip
crypto: add support for IEEE-P1363 DSA signatures
PR-URL: https://github.com/nodejs/node/pull/29292 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/node_crypto.cc136
-rw-r--r--src/node_crypto.h20
2 files changed, 147 insertions, 9 deletions
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 20cc52fbff..fa85f78553 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -4910,6 +4910,9 @@ void CheckThrow(Environment* env, SignBase::Error error) {
case SignBase::Error::kSignNotInitialised:
return env->ThrowError("Not initialised");
+ case SignBase::Error::kSignMalformedSignature:
+ return env->ThrowError("Malformed signature");
+
case SignBase::Error::kSignInit:
case SignBase::Error::kSignUpdate:
case SignBase::Error::kSignPrivateKey:
@@ -5007,6 +5010,89 @@ static int GetDefaultSignPadding(const ManagedEVPPKey& key) {
RSA_PKCS1_PADDING;
}
+static const unsigned int kNoDsaSignature = static_cast<unsigned int>(-1);
+
+// Returns the maximum size of each of the integers (r, s) of the DSA signature.
+static unsigned int GetBytesOfRS(const ManagedEVPPKey& pkey) {
+ int bits, base_id = EVP_PKEY_base_id(pkey.get());
+
+ if (base_id == EVP_PKEY_DSA) {
+ DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.get());
+ // Both r and s are computed mod q, so their width is limited by that of q.
+ bits = BN_num_bits(DSA_get0_q(dsa_key));
+ } else if (base_id == EVP_PKEY_EC) {
+ EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
+ const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key);
+ bits = EC_GROUP_order_bits(ec_group);
+ } else {
+ return kNoDsaSignature;
+ }
+
+ return (bits + 7) / 8;
+}
+
+static AllocatedBuffer ConvertSignatureToP1363(Environment* env,
+ const ManagedEVPPKey& pkey,
+ AllocatedBuffer&& signature) {
+ unsigned int n = GetBytesOfRS(pkey);
+ if (n == kNoDsaSignature)
+ return std::move(signature);
+
+ const unsigned char* sig_data =
+ reinterpret_cast<unsigned char*>(signature.data());
+
+ ECDSA_SIG* asn1_sig = d2i_ECDSA_SIG(nullptr, &sig_data, signature.size());
+ if (asn1_sig == nullptr)
+ return AllocatedBuffer();
+
+ AllocatedBuffer buf = env->AllocateManaged(2 * n);
+ unsigned char* data = reinterpret_cast<unsigned char*>(buf.data());
+
+ const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig);
+ const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig);
+ CHECK_EQ(n, BN_bn2binpad(r, data, n));
+ CHECK_EQ(n, BN_bn2binpad(s, data + n, n));
+
+ ECDSA_SIG_free(asn1_sig);
+
+ return buf;
+}
+
+static ByteSource ConvertSignatureToDER(
+ const ManagedEVPPKey& pkey,
+ const ArrayBufferViewContents<char>& signature) {
+ unsigned int n = GetBytesOfRS(pkey);
+ if (n == kNoDsaSignature)
+ return ByteSource::Foreign(signature.data(), signature.length());
+
+ const unsigned char* sig_data =
+ reinterpret_cast<const unsigned char*>(signature.data());
+
+ if (signature.length() != 2 * n)
+ return ByteSource();
+
+ ECDSA_SIG* asn1_sig = ECDSA_SIG_new();
+ CHECK_NOT_NULL(asn1_sig);
+ BIGNUM* r = BN_new();
+ CHECK_NOT_NULL(r);
+ BIGNUM* s = BN_new();
+ CHECK_NOT_NULL(s);
+ CHECK_EQ(r, BN_bin2bn(sig_data, n, r));
+ CHECK_EQ(s, BN_bin2bn(sig_data + n, n, s));
+ CHECK_EQ(1, ECDSA_SIG_set0(asn1_sig, r, s));
+
+ unsigned char* data = nullptr;
+ int len = i2d_ECDSA_SIG(asn1_sig, &data);
+ ECDSA_SIG_free(asn1_sig);
+
+ if (len <= 0)
+ return ByteSource();
+
+ CHECK_NOT_NULL(data);
+
+ return ByteSource::Allocated(reinterpret_cast<char*>(data), len);
+}
+
static AllocatedBuffer Node_SignFinal(Environment* env,
EVPMDPointer&& mdctx,
const ManagedEVPPKey& pkey,
@@ -5066,7 +5152,8 @@ static inline bool ValidateDSAParameters(EVP_PKEY* key) {
Sign::SignResult Sign::SignFinal(
const ManagedEVPPKey& pkey,
int padding,
- const Maybe<int>& salt_len) {
+ const Maybe<int>& salt_len,
+ DSASigEnc dsa_sig_enc) {
if (!mdctx_)
return SignResult(kSignNotInitialised);
@@ -5078,6 +5165,10 @@ Sign::SignResult Sign::SignFinal(
AllocatedBuffer buffer =
Node_SignFinal(env(), std::move(mdctx), pkey, padding, salt_len);
Error error = buffer.data() == nullptr ? kSignPrivateKey : kSignOk;
+ if (error == kSignOk && dsa_sig_enc == kSigEncP1363) {
+ buffer = ConvertSignatureToP1363(env(), pkey, std::move(buffer));
+ CHECK_NOT_NULL(buffer.data());
+ }
return SignResult(error, std::move(buffer));
}
@@ -5105,10 +5196,15 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
salt_len = Just<int>(args[offset + 1].As<Int32>()->Value());
}
+ CHECK(args[offset + 2]->IsInt32());
+ DSASigEnc dsa_sig_enc =
+ static_cast<DSASigEnc>(args[offset + 2].As<Int32>()->Value());
+
SignResult ret = sign->SignFinal(
key,
padding,
- salt_len);
+ salt_len,
+ dsa_sig_enc);
if (ret.error != kSignOk)
return sign->CheckThrow(ret.error);
@@ -5152,6 +5248,10 @@ void SignOneShot(const FunctionCallbackInfo<Value>& args) {
rsa_salt_len = Just<int>(args[offset + 3].As<Int32>()->Value());
}
+ CHECK(args[offset + 4]->IsInt32());
+ DSASigEnc dsa_sig_enc =
+ static_cast<DSASigEnc>(args[offset + 4].As<Int32>()->Value());
+
EVP_PKEY_CTX* pkctx = nullptr;
EVPMDPointer mdctx(EVP_MD_CTX_new());
if (!mdctx ||
@@ -5179,6 +5279,10 @@ void SignOneShot(const FunctionCallbackInfo<Value>& args) {
signature.Resize(sig_len);
+ if (dsa_sig_enc == kSigEncP1363) {
+ signature = ConvertSignatureToP1363(env, key, std::move(signature));
+ }
+
args.GetReturnValue().Set(signature.ToBuffer().ToLocalChecked());
}
@@ -5284,6 +5388,17 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
salt_len = Just<int>(args[offset + 2].As<Int32>()->Value());
}
+ CHECK(args[offset + 3]->IsInt32());
+ DSASigEnc dsa_sig_enc =
+ static_cast<DSASigEnc>(args[offset + 3].As<Int32>()->Value());
+
+ ByteSource signature = ByteSource::Foreign(hbuf.data(), hbuf.length());
+ if (dsa_sig_enc == kSigEncP1363) {
+ signature = ConvertSignatureToDER(pkey, hbuf);
+ if (signature.get() == nullptr)
+ return verify->CheckThrow(Error::kSignMalformedSignature);
+ }
+
bool verify_result;
Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding,
salt_len, &verify_result);
@@ -5327,6 +5442,10 @@ void VerifyOneShot(const FunctionCallbackInfo<Value>& args) {
rsa_salt_len = Just<int>(args[offset + 4].As<Int32>()->Value());
}
+ CHECK(args[offset + 5]->IsInt32());
+ DSASigEnc dsa_sig_enc =
+ static_cast<DSASigEnc>(args[offset + 5].As<Int32>()->Value());
+
EVP_PKEY_CTX* pkctx = nullptr;
EVPMDPointer mdctx(EVP_MD_CTX_new());
if (!mdctx ||
@@ -5337,11 +5456,18 @@ void VerifyOneShot(const FunctionCallbackInfo<Value>& args) {
if (!ApplyRSAOptions(key, pkctx, rsa_padding, rsa_salt_len))
return CheckThrow(env, SignBase::Error::kSignPublicKey);
+ ByteSource sig_bytes = ByteSource::Foreign(sig.data(), sig.length());
+ if (dsa_sig_enc == kSigEncP1363) {
+ sig_bytes = ConvertSignatureToDER(key, sig);
+ if (!sig_bytes)
+ return CheckThrow(env, SignBase::Error::kSignMalformedSignature);
+ }
+
bool verify_result;
const int r = EVP_DigestVerify(
mdctx.get(),
- reinterpret_cast<const unsigned char*>(sig.data()),
- sig.length(),
+ reinterpret_cast<const unsigned char*>(sig_bytes.get()),
+ sig_bytes.size(),
reinterpret_cast<const unsigned char*>(data.data()),
data.length());
switch (r) {
@@ -7129,6 +7255,8 @@ void Initialize(Local<Object> target,
NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);
+ NODE_DEFINE_CONSTANT(target, kSigEncDER);
+ NODE_DEFINE_CONSTANT(target, kSigEncP1363);
env->SetMethod(target, "randomBytes", RandomBytes);
env->SetMethod(target, "signOneShot", SignOneShot);
env->SetMethod(target, "verifyOneShot", VerifyOneShot);
diff --git a/src/node_crypto.h b/src/node_crypto.h
index 777ba5d302..56a9ad3104 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -326,6 +326,13 @@ class ByteSource {
const char* get() const;
size_t size() const;
+ inline operator bool() const {
+ return data_ != nullptr;
+ }
+
+ static ByteSource Allocated(char* data, size_t size);
+ static ByteSource Foreign(const char* data, size_t size);
+
static ByteSource FromStringOrBuffer(Environment* env,
v8::Local<v8::Value> value);
@@ -350,9 +357,6 @@ class ByteSource {
size_t size_ = 0;
ByteSource(const char* data, char* allocated_data, size_t size);
-
- static ByteSource Allocated(char* data, size_t size);
- static ByteSource Foreign(const char* data, size_t size);
};
enum PKEncodingType {
@@ -628,7 +632,8 @@ class SignBase : public BaseObject {
kSignNotInitialised,
kSignUpdate,
kSignPrivateKey,
- kSignPublicKey
+ kSignPublicKey,
+ kSignMalformedSignature
} Error;
SignBase(Environment* env, v8::Local<v8::Object> wrap)
@@ -649,6 +654,10 @@ class SignBase : public BaseObject {
EVPMDPointer mdctx_;
};
+enum DSASigEnc {
+ kSigEncDER, kSigEncP1363
+};
+
class Sign : public SignBase {
public:
static void Initialize(Environment* env, v8::Local<v8::Object> target);
@@ -666,7 +675,8 @@ class Sign : public SignBase {
SignResult SignFinal(
const ManagedEVPPKey& pkey,
int padding,
- const v8::Maybe<int>& saltlen);
+ const v8::Maybe<int>& saltlen,
+ DSASigEnc dsa_sig_enc);
protected:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);