diff options
author | Fedor Indutny <fedor@indutny.com> | 2014-08-27 18:01:01 +0400 |
---|---|---|
committer | Fedor Indutny <fedor@indutny.com> | 2014-08-29 00:27:09 +0400 |
commit | 6e453fad87c51dc15327628aa75886d3fbb3fa1c (patch) | |
tree | a750ea46af04a3107132a3e21eb6047675a15178 /src | |
parent | f7d6147e43b8a80a0d627f2034271239db500d9f (diff) | |
download | android-node-v8-6e453fad87c51dc15327628aa75886d3fbb3fa1c.tar.gz android-node-v8-6e453fad87c51dc15327628aa75886d3fbb3fa1c.tar.bz2 android-node-v8-6e453fad87c51dc15327628aa75886d3fbb3fa1c.zip |
crypto: introduce ECDH
Diffstat (limited to 'src')
-rw-r--r-- | src/node_constants.cc | 8 | ||||
-rw-r--r-- | src/node_crypto.cc | 219 | ||||
-rw-r--r-- | src/node_crypto.h | 38 |
3 files changed, 265 insertions, 0 deletions
diff --git a/src/node_constants.cc b/src/node_constants.cc index 430a09c685..118824e95d 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -33,6 +33,7 @@ #include <sys/stat.h> #if HAVE_OPENSSL +# include <openssl/ec.h> # include <openssl/ssl.h> # ifndef OPENSSL_NO_ENGINE # include <openssl/engine.h> @@ -974,6 +975,13 @@ void DefineOpenSSLConstants(Handle<Object> target) { #ifdef RSA_PKCS1_PSS_PADDING NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING); #endif + + // NOTE: These are not defines + NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED); + + NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_UNCOMPRESSED); + + NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_HYBRID); } void DefineSystemConstants(Handle<Object> target) { diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 6085a18a4d..6adedeeb77 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4085,6 +4085,224 @@ bool DiffieHellman::VerifyContext() { } +void ECDH::Initialize(Environment* env, Handle<Object> target) { + HandleScope scope(env->isolate()); + + Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate(), New); + + t->InstanceTemplate()->SetInternalFieldCount(1); + + NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys); + NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret); + NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey); + NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey); + NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey); + NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey); + + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ECDH"), + t->GetFunction()); +} + + +void ECDH::New(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + // TODO(indutny): Support raw curves? + CHECK(args[0]->IsString()); + node::Utf8Value curve(args[0]); + + int nid = OBJ_sn2nid(*curve); + if (nid == NID_undef) + return env->ThrowTypeError("First argument should be a valid curve name"); + + EC_KEY* key = EC_KEY_new_by_curve_name(nid); + if (key == NULL) + return env->ThrowError("Failed to create EC_KEY using curve name"); + + new ECDH(env, args.This(), key); +} + + +void ECDH::GenerateKeys(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + ECDH* ecdh = Unwrap<ECDH>(args.Holder()); + + if (!EC_KEY_generate_key(ecdh->key_)) + return env->ThrowError("Failed to generate EC_KEY"); + + ecdh->generated_ = true; +} + + +EC_POINT* ECDH::BufferToPoint(char* data, size_t len) { + EC_POINT* pub; + int r; + + pub = EC_POINT_new(group_); + if (pub == NULL) { + env()->ThrowError("Failed to allocate EC_POINT for a public key"); + return NULL; + } + + r = EC_POINT_oct2point( + group_, + pub, + reinterpret_cast<unsigned char*>(data), + len, + NULL); + if (!r) { + env()->ThrowError("Failed to translate Buffer to a EC_POINT"); + goto fatal; + } + + return pub; + + fatal: + EC_POINT_free(pub); + return NULL; +} + + +void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + ASSERT_IS_BUFFER(args[0]); + + ECDH* ecdh = Unwrap<ECDH>(args.Holder()); + + EC_POINT* pub = ecdh->BufferToPoint(Buffer::Data(args[0]), + Buffer::Length(args[0])); + if (pub == NULL) + return; + + // NOTE: field_size is in bits + int field_size = EC_GROUP_get_degree(ecdh->group_); + size_t out_len = (field_size + 7) / 8; + char* out = static_cast<char*>(malloc(out_len)); + CHECK_NE(out, NULL); + + int r = ECDH_compute_key(out, out_len, pub, ecdh->key_, NULL); + EC_POINT_free(pub); + if (!r) { + free(out); + return env->ThrowError("Failed to compute ECDH key"); + } + + args.GetReturnValue().Set(Buffer::Use(env, out, out_len)); +} + + +void ECDH::GetPublicKey(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + // Conversion form + CHECK_EQ(args.Length(), 1); + + ECDH* ecdh = Unwrap<ECDH>(args.Holder()); + + if (!ecdh->generated_) + return env->ThrowError("You should generate ECDH keys first"); + + const EC_POINT* pub = EC_KEY_get0_public_key(ecdh->key_); + if (pub == NULL) + return env->ThrowError("Failed to get ECDH public key"); + + int size; + point_conversion_form_t form = + static_cast<point_conversion_form_t>(args[0]->Uint32Value()); + + size = EC_POINT_point2oct(ecdh->group_, pub, form, NULL, 0, NULL); + if (size == 0) + return env->ThrowError("Failed to get public key length"); + + unsigned char* out = static_cast<unsigned char*>(malloc(size)); + CHECK_NE(out, NULL); + + int r = EC_POINT_point2oct(ecdh->group_, pub, form, out, size, NULL); + if (r != size) { + free(out); + return env->ThrowError("Failed to get public key"); + } + + args.GetReturnValue().Set(Buffer::Use(env, + reinterpret_cast<char*>(out), + size)); +} + + +void ECDH::GetPrivateKey(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + ECDH* ecdh = Unwrap<ECDH>(args.Holder()); + + if (!ecdh->generated_) + return env->ThrowError("You should generate ECDH keys first"); + + const BIGNUM* b = EC_KEY_get0_private_key(ecdh->key_); + if (b == NULL) + return env->ThrowError("Failed to get ECDH private key"); + + int size = BN_num_bytes(b); + unsigned char* out = static_cast<unsigned char*>(malloc(size)); + CHECK_NE(out, NULL); + + if (size != BN_bn2bin(b, out)) { + free(out); + return env->ThrowError("Failed to convert ECDH private key to Buffer"); + } + + args.GetReturnValue().Set(Buffer::Use(env, + reinterpret_cast<char*>(out), + size)); +} + + +void ECDH::SetPrivateKey(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + ECDH* ecdh = Unwrap<ECDH>(args.Holder()); + + ASSERT_IS_BUFFER(args[0]); + + BIGNUM* priv = BN_bin2bn( + reinterpret_cast<unsigned char*>(Buffer::Data(args[0].As<Object>())), + Buffer::Length(args[0].As<Object>()), + NULL); + if (priv == NULL) + return env->ThrowError("Failed to convert Buffer to BN"); + + if (!EC_KEY_set_private_key(ecdh->key_, priv)) + return env->ThrowError("Failed to convert BN to a private key"); +} + + +void ECDH::SetPublicKey(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + ECDH* ecdh = Unwrap<ECDH>(args.Holder()); + + ASSERT_IS_BUFFER(args[0]); + + EC_POINT* pub = ecdh->BufferToPoint(Buffer::Data(args[0].As<Object>()), + Buffer::Length(args[0].As<Object>())); + if (pub == NULL) + return; + + int r = EC_KEY_set_public_key(ecdh->key_, pub); + EC_POINT_free(pub); + if (!r) + return env->ThrowError("Failed to convert BN to a private key"); +} + + class PBKDF2Request : public AsyncWrap { public: PBKDF2Request(Environment* env, @@ -4855,6 +5073,7 @@ void InitCrypto(Handle<Object> target, Connection::Initialize(env, target); CipherBase::Initialize(env, target); DiffieHellman::Initialize(env, target); + ECDH::Initialize(env, target); Hmac::Initialize(env, target); Hash::Initialize(env, target); Sign::Initialize(env, target); diff --git a/src/node_crypto.h b/src/node_crypto.h index 2a02c89bc2..178afc80ea 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -39,6 +39,8 @@ #include "v8.h" #include <openssl/ssl.h> +#include <openssl/ec.h> +#include <openssl/ecdh.h> #ifndef OPENSSL_NO_ENGINE # include <openssl/engine.h> #endif // !OPENSSL_NO_ENGINE @@ -635,6 +637,42 @@ class DiffieHellman : public BaseObject { DH* dh; }; +class ECDH : public BaseObject { + public: + ~ECDH() { + if (key_ != NULL) + EC_KEY_free(key_); + key_ = NULL; + group_ = NULL; + } + + static void Initialize(Environment* env, v8::Handle<v8::Object> target); + + protected: + ECDH(Environment* env, v8::Local<v8::Object> wrap, EC_KEY* key) + : BaseObject(env, wrap), + generated_(false), + key_(key), + group_(EC_KEY_get0_group(key_)) { + MakeWeak<ECDH>(this); + ASSERT(group_ != NULL); + } + + static void New(const v8::FunctionCallbackInfo<v8::Value>& args); + static void GenerateKeys(const v8::FunctionCallbackInfo<v8::Value>& args); + static void ComputeSecret(const v8::FunctionCallbackInfo<v8::Value>& args); + static void GetPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args); + static void SetPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args); + static void GetPublicKey(const v8::FunctionCallbackInfo<v8::Value>& args); + static void SetPublicKey(const v8::FunctionCallbackInfo<v8::Value>& args); + + EC_POINT* BufferToPoint(char* data, size_t len); + + bool generated_; + EC_KEY* key_; + const EC_GROUP* group_; +}; + class Certificate : public AsyncWrap { public: static void Initialize(Environment* env, v8::Handle<v8::Object> target); |