diff options
author | Tobias Nießen <tniessen@tnie.de> | 2020-01-03 14:01:34 +0300 |
---|---|---|
committer | Shelley Vohr <shelley.vohr@gmail.com> | 2020-02-17 21:35:04 +0300 |
commit | bf46c304dd46dbab49db3da16ef93eb4d11ad4f9 (patch) | |
tree | 26b12057e0ddba2005fc949cb002da11c2fed9ca /src/node_crypto.cc | |
parent | 0d3e095941ed812f0300a6c4a3424e329d03a74a (diff) |
crypto: add crypto.diffieHellman
Currently, Node.js has separate (stateful) APIs for DH/ECDH, and no
support for ECDH-ES. This commit adds a single stateless function to
compute the DH/ECDH/ECDH-ES secret based on two KeyObjects.
PR-URL: https://github.com/nodejs/node/pull/31178
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Diffstat (limited to 'src/node_crypto.cc')
-rw-r--r-- | src/node_crypto.cc | 69 |
1 files changed, 59 insertions, 10 deletions
diff --git a/src/node_crypto.cc b/src/node_crypto.cc index a4d0d8bec29..42dab91e662 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5830,6 +5830,20 @@ void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) { }, "No private key - did you forget to generate one?"); } +static void ZeroPadDiffieHellmanSecret(size_t remainder_size, + AllocatedBuffer* ret) { + // DH_size returns number of bytes in a prime number. + // DH_compute_key returns number of bytes in a remainder of exponent, which + // may have less bytes than a prime number. Therefore add 0-padding to the + // allocated buffer. + const size_t prime_size = ret->size(); + if (remainder_size != prime_size) { + CHECK_LT(remainder_size, prime_size); + const size_t padding = prime_size - remainder_size; + memmove(ret->data() + padding, ret->data(), remainder_size); + memset(ret->data(), 0, padding); + } +} void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); @@ -5880,16 +5894,7 @@ void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) { } CHECK_GE(size, 0); - - // DH_size returns number of bytes in a prime number - // DH_compute_key returns number of bytes in a remainder of exponent, which - // may have less bytes than a prime number. Therefore add 0-padding to the - // allocated buffer. - if (static_cast<size_t>(size) != ret.size()) { - CHECK_GT(ret.size(), static_cast<size_t>(size)); - memmove(ret.data() + ret.size() - size, ret.data(), size); - memset(ret.data(), 0, ret.size() - size); - } + ZeroPadDiffieHellmanSecret(static_cast<size_t>(size), &ret); args.GetReturnValue().Set(ret.ToBuffer().ToLocalChecked()); } @@ -7200,6 +7205,49 @@ void ConvertKey(const FunctionCallbackInfo<Value>& args) { args.GetReturnValue().Set(buf); } +AllocatedBuffer StatelessDiffieHellman(Environment* env, ManagedEVPPKey our_key, + ManagedEVPPKey their_key) { + size_t out_size; + + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(our_key.get(), nullptr)); + if (!ctx || + EVP_PKEY_derive_init(ctx.get()) <= 0 || + EVP_PKEY_derive_set_peer(ctx.get(), their_key.get()) <= 0 || + EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) + return AllocatedBuffer(); + + AllocatedBuffer result = env->AllocateManaged(out_size); + CHECK_NOT_NULL(result.data()); + + unsigned char* data = reinterpret_cast<unsigned char*>(result.data()); + if (EVP_PKEY_derive(ctx.get(), data, &out_size) <= 0) + return AllocatedBuffer(); + + ZeroPadDiffieHellmanSecret(out_size, &result); + return result; +} + +void StatelessDiffieHellman(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsObject() && args[1]->IsObject()); + KeyObject* our_key_object; + ASSIGN_OR_RETURN_UNWRAP(&our_key_object, args[0].As<Object>()); + CHECK_EQ(our_key_object->GetKeyType(), kKeyTypePrivate); + KeyObject* their_key_object; + ASSIGN_OR_RETURN_UNWRAP(&their_key_object, args[1].As<Object>()); + CHECK_NE(their_key_object->GetKeyType(), kKeyTypeSecret); + + ManagedEVPPKey our_key = our_key_object->GetAsymmetricKey(); + ManagedEVPPKey their_key = their_key_object->GetAsymmetricKey(); + + AllocatedBuffer out = StatelessDiffieHellman(env, our_key, their_key); + if (out.size() == 0) + return ThrowCryptoError(env, ERR_get_error(), "diffieHellman failed"); + + args.GetReturnValue().Set(out.ToBuffer().ToLocalChecked()); +} + void TimingSafeEqual(const FunctionCallbackInfo<Value>& args) { ArrayBufferViewContents<char> buf1(args[0]); @@ -7369,6 +7417,7 @@ void Initialize(Local<Object> target, NODE_DEFINE_CONSTANT(target, kKeyTypePrivate); NODE_DEFINE_CONSTANT(target, kSigEncDER); NODE_DEFINE_CONSTANT(target, kSigEncP1363); + env->SetMethodNoSideEffect(target, "statelessDH", StatelessDiffieHellman); env->SetMethod(target, "randomBytes", RandomBytes); env->SetMethod(target, "signOneShot", SignOneShot); env->SetMethod(target, "verifyOneShot", VerifyOneShot); |