Merge pull request #1 from jgarzik/eckey
Import BitcoinKey/eckey from bitcoinjs-server
This commit is contained in:
commit
d5c16d0722
|
@ -0,0 +1,2 @@
|
||||||
|
build
|
||||||
|
node_modules
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
'variables': {
|
||||||
|
'node_shared_openssl%': 'true'
|
||||||
|
},
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'eckey',
|
||||||
|
'sources': [
|
||||||
|
'src/eckey.cc'
|
||||||
|
],
|
||||||
|
'conditions': [
|
||||||
|
['node_shared_openssl=="false"', {
|
||||||
|
# so when "node_shared_openssl" is "false", then OpenSSL has been
|
||||||
|
# bundled into the node executable. So we need to include the same
|
||||||
|
# header files that were used when building node.
|
||||||
|
'include_dirs': [
|
||||||
|
'<(node_root_dir)/deps/openssl/openssl/include'
|
||||||
|
],
|
||||||
|
"conditions" : [
|
||||||
|
["target_arch=='ia32'", {
|
||||||
|
"include_dirs": [ "<(node_root_dir)/deps/openssl/config/piii" ]
|
||||||
|
}],
|
||||||
|
["target_arch=='x64'", {
|
||||||
|
"include_dirs": [ "<(node_root_dir)/deps/openssl/config/k8" ]
|
||||||
|
}],
|
||||||
|
["target_arch=='arm'", {
|
||||||
|
"include_dirs": [ "<(node_root_dir)/deps/openssl/config/arm" ]
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
"name": "Stephen Pair",
|
"name": "Stephen Pair",
|
||||||
"email": "stephen@bitpay.com"
|
"email": "stephen@bitpay.com"
|
||||||
},
|
},
|
||||||
|
"contributors": [
|
||||||
|
{"name": "Stefan Thomas", "email": "moon@justmoon.net"},
|
||||||
|
{"name": "Jeff Garzik", "email": "jgarzik@bitpay.com"}
|
||||||
|
],
|
||||||
"main": "./index",
|
"main": "./index",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
|
@ -22,7 +26,8 @@
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classtool": ">=1.0.0",
|
"classtool": ">=1.0.0",
|
||||||
"base58-native": ">=0.1.1"
|
"base58-native": ">=0.1.1",
|
||||||
|
"bindings": "1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef LIBCOIN_SERVER_INCLUDE_COMMON_H_
|
||||||
|
#define LIBCOIN_SERVER_INCLUDE_COMMON_H_
|
||||||
|
|
||||||
|
#include <v8.h>
|
||||||
|
|
||||||
|
#define REQ_FUN_ARG(I, VAR) \
|
||||||
|
if (args.Length() <= (I) || !args[I]->IsFunction()) \
|
||||||
|
return v8::ThrowException(v8::Exception::TypeError( \
|
||||||
|
v8::String::New("Argument " #I " must be a function"))); \
|
||||||
|
v8::Local<v8::Function> VAR = v8::Local<v8::Function>::Cast(args[I]);
|
||||||
|
|
||||||
|
static v8::Handle<v8::Value> VException(const char *msg) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
return v8::ThrowException(v8::Exception::Error(v8::String::New(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,575 @@
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <v8.h>
|
||||||
|
|
||||||
|
#include <node.h>
|
||||||
|
#include <node_buffer.h>
|
||||||
|
#include <node_internals.h>
|
||||||
|
|
||||||
|
#include <openssl/ecdsa.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "eckey.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace v8;
|
||||||
|
using namespace node;
|
||||||
|
|
||||||
|
int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const BIGNUM *priv_key)
|
||||||
|
{
|
||||||
|
int ok = 0;
|
||||||
|
BN_CTX *ctx = NULL;
|
||||||
|
EC_POINT *pub_key = NULL;
|
||||||
|
|
||||||
|
if (!eckey) return 0;
|
||||||
|
|
||||||
|
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
||||||
|
|
||||||
|
if ((ctx = BN_CTX_new()) == NULL)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
pub_key = EC_POINT_new(group);
|
||||||
|
|
||||||
|
if (pub_key == NULL)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
EC_KEY_set_private_key(eckey,priv_key);
|
||||||
|
EC_KEY_set_public_key(eckey,pub_key);
|
||||||
|
|
||||||
|
ok = 1;
|
||||||
|
|
||||||
|
err:
|
||||||
|
|
||||||
|
if (pub_key)
|
||||||
|
EC_POINT_free(pub_key);
|
||||||
|
if (ctx != NULL)
|
||||||
|
BN_CTX_free(ctx);
|
||||||
|
|
||||||
|
return(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace bitcoin {
|
||||||
|
|
||||||
|
void Key::Generate()
|
||||||
|
{
|
||||||
|
if (!EC_KEY_generate_key(ec)) {
|
||||||
|
lastError = "Error from EC_KEY_generate_key";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPublic = true;
|
||||||
|
hasPrivate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Key::VerifySignature(const unsigned char *digest, int digest_len,
|
||||||
|
const unsigned char *sig, int sig_len)
|
||||||
|
{
|
||||||
|
return ECDSA_verify(0, digest, digest_len, sig, sig_len, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Key::EIO_VerifySignature(uv_work_t *req)
|
||||||
|
{
|
||||||
|
verify_sig_baton_t *b = static_cast<verify_sig_baton_t *>(req->data);
|
||||||
|
|
||||||
|
b->result = b->key->VerifySignature(
|
||||||
|
b->digest, b->digestLen,
|
||||||
|
b->sig, b->sigLen
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECDSA_SIG *Key::Sign(const unsigned char *digest, int digest_len)
|
||||||
|
{
|
||||||
|
ECDSA_SIG *sig;
|
||||||
|
|
||||||
|
sig = ECDSA_do_sign(digest, digest_len, ec);
|
||||||
|
if (sig == NULL) {
|
||||||
|
// TODO: ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Key::Init(Handle<Object> target)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Local<FunctionTemplate> t = FunctionTemplate::New(New);
|
||||||
|
|
||||||
|
s_ct = Persistent<FunctionTemplate>::New(t);
|
||||||
|
s_ct->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
|
s_ct->SetClassName(String::NewSymbol("Key"));
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
s_ct->InstanceTemplate()->SetAccessor(String::New("private"),
|
||||||
|
GetPrivate, SetPrivate);
|
||||||
|
s_ct->InstanceTemplate()->SetAccessor(String::New("public"),
|
||||||
|
GetPublic, SetPublic);
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(s_ct, "verifySignature", VerifySignature);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(s_ct, "verifySignatureSync", VerifySignatureSync);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(s_ct, "regenerateSync", RegenerateSync);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(s_ct, "toDER", ToDER);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(s_ct, "signSync", SignSync);
|
||||||
|
|
||||||
|
// Static methods
|
||||||
|
NODE_SET_METHOD(s_ct->GetFunction(), "generateSync", GenerateSync);
|
||||||
|
NODE_SET_METHOD(s_ct->GetFunction(), "fromDER", FromDER);
|
||||||
|
|
||||||
|
target->Set(String::NewSymbol("Key"),
|
||||||
|
s_ct->GetFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::Key() :
|
||||||
|
lastError(NULL),
|
||||||
|
hasPrivate(false),
|
||||||
|
hasPublic(false)
|
||||||
|
{
|
||||||
|
ec = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||||
|
if (ec == NULL) {
|
||||||
|
lastError = "Error from EC_KEY_new_by_curve_name";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::~Key()
|
||||||
|
{
|
||||||
|
EC_KEY_free(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key*
|
||||||
|
Key::New()
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Local<Object> k = s_ct->GetFunction()->NewInstance(0, NULL);
|
||||||
|
if (k.IsEmpty()) return NULL;
|
||||||
|
|
||||||
|
return ObjectWrap::Unwrap<Key>(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::New(const Arguments& args)
|
||||||
|
{
|
||||||
|
if (!args.IsConstructCall()) {
|
||||||
|
return FromConstructorTemplate(s_ct, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Key* key = new Key();
|
||||||
|
if (key->lastError != NULL) {
|
||||||
|
return VException(key->lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
key->Wrap(args.Holder());
|
||||||
|
|
||||||
|
return scope.Close(args.This());
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::GenerateSync(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
Key* key = Key::New();
|
||||||
|
|
||||||
|
key->Generate();
|
||||||
|
|
||||||
|
if (key->lastError != NULL) {
|
||||||
|
return VException(key->lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope.Close(key->handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::GetPrivate(Local<String> property, const AccessorInfo& info)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(info.Holder());
|
||||||
|
|
||||||
|
if (!key->hasPrivate) {
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
const BIGNUM *bn = EC_KEY_get0_private_key(key->ec);
|
||||||
|
int priv_size = BN_num_bytes(bn);
|
||||||
|
|
||||||
|
if (bn == NULL) {
|
||||||
|
// TODO: ERROR: "Error from EC_KEY_get0_private_key(pkey)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv_size > 32) {
|
||||||
|
// TODO: ERROR: "Secret too large (Incorrect curve parameters?)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *priv = (unsigned char *)malloc(32);
|
||||||
|
|
||||||
|
int n = BN_bn2bin(bn, &priv[32 - priv_size]);
|
||||||
|
|
||||||
|
if (n != priv_size) {
|
||||||
|
// TODO: ERROR: "Error from BN_bn2bin(bn, &priv[32 - priv_size])"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer *priv_buf = Buffer::New(32);
|
||||||
|
memcpy(Buffer::Data(priv_buf), priv, 32);
|
||||||
|
|
||||||
|
free(priv);
|
||||||
|
|
||||||
|
return scope.Close(priv_buf->handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Key::SetPrivate(Local<String> property, Local<Value> value, const AccessorInfo& info)
|
||||||
|
{
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(info.Holder());
|
||||||
|
Handle<Object> buffer = value->ToObject();
|
||||||
|
const unsigned char *data = (const unsigned char*) Buffer::Data(buffer);
|
||||||
|
|
||||||
|
BIGNUM *bn = BN_bin2bn(data,Buffer::Length(buffer),BN_new());
|
||||||
|
EC_KEY_set_private_key(key->ec, bn);
|
||||||
|
BN_clear_free(bn);
|
||||||
|
|
||||||
|
key->hasPrivate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::GetPublic(Local<String> property, const AccessorInfo& info)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(info.Holder());
|
||||||
|
|
||||||
|
if (!key->hasPublic) {
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export public
|
||||||
|
int pub_size = i2o_ECPublicKey(key->ec, NULL);
|
||||||
|
if (!pub_size) {
|
||||||
|
// TODO: ERROR: "Error from i2o_ECPublicKey(key->ec, NULL)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
unsigned char *pub_begin, *pub_end;
|
||||||
|
pub_begin = pub_end = (unsigned char *)malloc(pub_size);
|
||||||
|
|
||||||
|
if (i2o_ECPublicKey(key->ec, &pub_end) != pub_size) {
|
||||||
|
// TODO: ERROR: "Error from i2o_ECPublicKey(key->ec, &pub)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
Buffer *pub_buf = Buffer::New(pub_size);
|
||||||
|
memcpy(Buffer::Data(pub_buf), pub_begin, pub_size);
|
||||||
|
|
||||||
|
free(pub_begin);
|
||||||
|
|
||||||
|
return scope.Close(pub_buf->handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Key::SetPublic(Local<String> property, Local<Value> value, const AccessorInfo& info)
|
||||||
|
{
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(info.Holder());
|
||||||
|
Handle<Object> buffer = value->ToObject();
|
||||||
|
const unsigned char *data = (const unsigned char*) Buffer::Data(buffer);
|
||||||
|
|
||||||
|
if (!o2i_ECPublicKey(&(key->ec), &data, Buffer::Length(buffer))) {
|
||||||
|
// TODO: Error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key->hasPublic = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::RegenerateSync(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(args.This());
|
||||||
|
|
||||||
|
if (!key->hasPrivate) {
|
||||||
|
return VException("Regeneration requires a private key.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EC_KEY *old = key->ec;
|
||||||
|
|
||||||
|
key->ec = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||||
|
if (EC_KEY_regenerate_key(key->ec, EC_KEY_get0_private_key(old)) == 1) {
|
||||||
|
key->hasPublic = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EC_KEY_free(old);
|
||||||
|
|
||||||
|
return scope.Close(Undefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::ToDER(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(args.This());
|
||||||
|
|
||||||
|
if (!key->hasPrivate || !key->hasPublic) {
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export DER
|
||||||
|
int der_size = i2d_ECPrivateKey(key->ec, NULL);
|
||||||
|
if (!der_size) {
|
||||||
|
// TODO: ERROR: "Error from i2d_ECPrivateKey(key->ec, NULL)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
unsigned char *der_begin, *der_end;
|
||||||
|
der_begin = der_end = (unsigned char *)malloc(der_size);
|
||||||
|
|
||||||
|
if (i2d_ECPrivateKey(key->ec, &der_end) != der_size) {
|
||||||
|
// TODO: ERROR: "Error from i2d_ECPrivateKey(key->ec, &der_end)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
Buffer *der_buf = Buffer::New(der_size);
|
||||||
|
memcpy(Buffer::Data(der_buf), der_begin, der_size);
|
||||||
|
|
||||||
|
free(der_begin);
|
||||||
|
|
||||||
|
return scope.Close(der_buf->handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::FromDER(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
if (args.Length() != 1) {
|
||||||
|
return VException("One argument expected: der");
|
||||||
|
}
|
||||||
|
if (!Buffer::HasInstance(args[0])) {
|
||||||
|
return VException("Argument 'der' must be of type Buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
Key* key = new Key();
|
||||||
|
if (key->lastError != NULL) {
|
||||||
|
return VException(key->lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Object> der_buf = args[0]->ToObject();
|
||||||
|
const unsigned char *data = (const unsigned char*) Buffer::Data(der_buf);
|
||||||
|
|
||||||
|
if (!d2i_ECPrivateKey(&(key->ec), &data, Buffer::Length(der_buf))) {
|
||||||
|
return VException("Error from d2i_ECPrivateKey(&key, &data, len)");
|
||||||
|
}
|
||||||
|
|
||||||
|
key->hasPrivate = true;
|
||||||
|
key->hasPublic = true;
|
||||||
|
|
||||||
|
Handle<Function> cons = s_ct->GetFunction();
|
||||||
|
Handle<Value> external = External::New(key);
|
||||||
|
Handle<Value> result = cons->NewInstance(1, &external);
|
||||||
|
|
||||||
|
return scope.Close(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::VerifySignature(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(args.This());
|
||||||
|
|
||||||
|
if (args.Length() != 3) {
|
||||||
|
return VException("Three arguments expected: hash, sig, callback");
|
||||||
|
}
|
||||||
|
if (!Buffer::HasInstance(args[0])) {
|
||||||
|
return VException("Argument 'hash' must be of type Buffer");
|
||||||
|
}
|
||||||
|
if (!Buffer::HasInstance(args[1])) {
|
||||||
|
return VException("Argument 'sig' must be of type Buffer");
|
||||||
|
}
|
||||||
|
REQ_FUN_ARG(2, cb);
|
||||||
|
if (!key->hasPublic) {
|
||||||
|
return VException("Key does not have a public key set");
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Object> hash_buf = args[0]->ToObject();
|
||||||
|
Handle<Object> sig_buf = args[1]->ToObject();
|
||||||
|
|
||||||
|
if (Buffer::Length(hash_buf) != 32) {
|
||||||
|
return VException("Argument 'hash' must be Buffer of length 32 bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_sig_baton_t *baton = new verify_sig_baton_t();
|
||||||
|
baton->key = key;
|
||||||
|
baton->digest = (unsigned char *)Buffer::Data(hash_buf);
|
||||||
|
baton->digestLen = Buffer::Length(hash_buf);
|
||||||
|
baton->digestBuf = Persistent<Object>::New(hash_buf);
|
||||||
|
baton->sig = (unsigned char *)Buffer::Data(sig_buf);
|
||||||
|
baton->sigLen = Buffer::Length(sig_buf);
|
||||||
|
baton->sigBuf = Persistent<Object>::New(sig_buf);
|
||||||
|
baton->result = -1;
|
||||||
|
baton->cb = Persistent<Function>::New(cb);
|
||||||
|
|
||||||
|
key->Ref();
|
||||||
|
|
||||||
|
uv_work_t *req = new uv_work_t;
|
||||||
|
req->data = baton;
|
||||||
|
|
||||||
|
uv_queue_work(uv_default_loop(), req, EIO_VerifySignature, VerifySignatureCallback);
|
||||||
|
|
||||||
|
return scope.Close(Undefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Key::VerifySignatureCallback(uv_work_t *req, int status)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
verify_sig_baton_t *baton = static_cast<verify_sig_baton_t *>(req->data);
|
||||||
|
|
||||||
|
baton->key->Unref();
|
||||||
|
baton->digestBuf.Dispose();
|
||||||
|
baton->sigBuf.Dispose();
|
||||||
|
|
||||||
|
Local<Value> argv[2];
|
||||||
|
|
||||||
|
argv[0] = Local<Value>::New(Null());
|
||||||
|
argv[1] = Local<Value>::New(Null());
|
||||||
|
if (baton->result == -1) {
|
||||||
|
argv[0] = Exception::TypeError(String::New("Error during ECDSA_verify"));
|
||||||
|
} else if (baton->result == 0) {
|
||||||
|
// Signature invalid
|
||||||
|
argv[1] = Local<Value>::New(Boolean::New(false));
|
||||||
|
} else if (baton->result == 1) {
|
||||||
|
// Signature valid
|
||||||
|
argv[1] = Local<Value>::New(Boolean::New(true));
|
||||||
|
} else {
|
||||||
|
argv[0] = Exception::TypeError(
|
||||||
|
String::New("ECDSA_verify gave undefined return value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TryCatch try_catch;
|
||||||
|
|
||||||
|
baton->cb->Call(Context::GetCurrent()->Global(), 2, argv);
|
||||||
|
|
||||||
|
|
||||||
|
baton->cb.Dispose();
|
||||||
|
|
||||||
|
delete baton;
|
||||||
|
delete req;
|
||||||
|
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
FatalException(try_catch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::VerifySignatureSync(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(args.This());
|
||||||
|
|
||||||
|
if (args.Length() != 2) {
|
||||||
|
return VException("Two arguments expected: hash, sig");
|
||||||
|
}
|
||||||
|
if (!Buffer::HasInstance(args[0])) {
|
||||||
|
return VException("Argument 'hash' must be of type Buffer");
|
||||||
|
}
|
||||||
|
if (!Buffer::HasInstance(args[1])) {
|
||||||
|
return VException("Argument 'sig' must be of type Buffer");
|
||||||
|
}
|
||||||
|
if (!key->hasPublic) {
|
||||||
|
return VException("Key does not have a public key set");
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Object> hash_buf = args[0]->ToObject();
|
||||||
|
Handle<Object> sig_buf = args[1]->ToObject();
|
||||||
|
|
||||||
|
const unsigned char *hash_data = (unsigned char *) Buffer::Data(hash_buf);
|
||||||
|
const unsigned char *sig_data = (unsigned char *) Buffer::Data(sig_buf);
|
||||||
|
|
||||||
|
unsigned int hash_len = Buffer::Length(hash_buf);
|
||||||
|
unsigned int sig_len = Buffer::Length(sig_buf);
|
||||||
|
|
||||||
|
if (hash_len != 32) {
|
||||||
|
return VException("Argument 'hash' must be Buffer of length 32 bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
int result = key->VerifySignature(hash_data, hash_len, sig_data, sig_len);
|
||||||
|
|
||||||
|
if (result == -1) {
|
||||||
|
return VException("Error during ECDSA_verify");
|
||||||
|
} else if (result == 0) {
|
||||||
|
// Signature invalid
|
||||||
|
return scope.Close(Boolean::New(false));
|
||||||
|
} else if (result == 1) {
|
||||||
|
// Signature valid
|
||||||
|
return scope.Close(Boolean::New(true));
|
||||||
|
} else {
|
||||||
|
return VException("ECDSA_verify gave undefined return value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Value>
|
||||||
|
Key::SignSync(const Arguments& args)
|
||||||
|
{
|
||||||
|
HandleScope scope;
|
||||||
|
Key* key = node::ObjectWrap::Unwrap<Key>(args.This());
|
||||||
|
|
||||||
|
if (args.Length() != 1) {
|
||||||
|
return VException("One argument expected: hash");
|
||||||
|
}
|
||||||
|
if (!Buffer::HasInstance(args[0])) {
|
||||||
|
return VException("Argument 'hash' must be of type Buffer");
|
||||||
|
}
|
||||||
|
if (!key->hasPrivate) {
|
||||||
|
return VException("Key does not have a private key set");
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Object> hash_buf = args[0]->ToObject();
|
||||||
|
|
||||||
|
const unsigned char *hash_data = (unsigned char *) Buffer::Data(hash_buf);
|
||||||
|
|
||||||
|
unsigned int hash_len = Buffer::Length(hash_buf);
|
||||||
|
|
||||||
|
if (hash_len != 32) {
|
||||||
|
return VException("Argument 'hash' must be Buffer of length 32 bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create signature
|
||||||
|
ECDSA_SIG *sig = key->Sign(hash_data, hash_len);
|
||||||
|
|
||||||
|
// Export DER
|
||||||
|
int der_size = i2d_ECDSA_SIG(sig, NULL);
|
||||||
|
if (!der_size) {
|
||||||
|
// TODO: ERROR: "Error from i2d_ECPrivateKey(key->ec, NULL)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
unsigned char *der_begin, *der_end;
|
||||||
|
der_begin = der_end = (unsigned char *)malloc(der_size);
|
||||||
|
|
||||||
|
if (i2d_ECDSA_SIG(sig, &der_end) != der_size) {
|
||||||
|
// TODO: ERROR: "Error from i2d_ECPrivateKey(key->ec, &der_end)"
|
||||||
|
return scope.Close(Null());
|
||||||
|
}
|
||||||
|
Buffer *der_buf = Buffer::New(der_size);
|
||||||
|
memcpy(Buffer::Data(der_buf), der_begin, der_size);
|
||||||
|
|
||||||
|
free(der_begin);
|
||||||
|
ECDSA_SIG_free(sig);
|
||||||
|
|
||||||
|
return scope.Close(der_buf->handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Persistent<FunctionTemplate> Key::s_ct;
|
||||||
|
|
||||||
|
}; // namespace bitcoin
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
init (Handle<Object> target)
|
||||||
|
{
|
||||||
|
bitcoin::Key::Init(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_MODULE(native, init)
|
|
@ -0,0 +1,97 @@
|
||||||
|
#ifndef LIBCOIN_SERVER_INCLUDE_ECKEY_H_
|
||||||
|
#define LIBCOIN_SERVER_INCLUDE_ECKEY_H_
|
||||||
|
|
||||||
|
#include <v8.h>
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
using namespace v8;
|
||||||
|
using namespace node;
|
||||||
|
|
||||||
|
namespace bitcoin {
|
||||||
|
|
||||||
|
class Key : ObjectWrap
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
const char *lastError;
|
||||||
|
EC_KEY *ec;
|
||||||
|
|
||||||
|
bool hasPrivate;
|
||||||
|
bool hasPublic;
|
||||||
|
|
||||||
|
void Generate();
|
||||||
|
|
||||||
|
struct verify_sig_baton_t {
|
||||||
|
// Parameters
|
||||||
|
Key *key;
|
||||||
|
const unsigned char *digest;
|
||||||
|
const unsigned char *sig;
|
||||||
|
int digestLen;
|
||||||
|
int sigLen;
|
||||||
|
Persistent<Object> digestBuf;
|
||||||
|
Persistent<Object> sigBuf;
|
||||||
|
|
||||||
|
// Result
|
||||||
|
// -1 = error, 0 = bad sig, 1 = good
|
||||||
|
int result;
|
||||||
|
Persistent<Function> cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
int VerifySignature(const unsigned char *digest, int digest_len,
|
||||||
|
const unsigned char *sig, int sig_len);
|
||||||
|
|
||||||
|
static void EIO_VerifySignature(uv_work_t *req);
|
||||||
|
|
||||||
|
ECDSA_SIG *Sign(const unsigned char *digest, int digest_len);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static Persistent<FunctionTemplate> s_ct;
|
||||||
|
|
||||||
|
static void Init(Handle<Object> target);
|
||||||
|
|
||||||
|
Key();
|
||||||
|
~Key();
|
||||||
|
|
||||||
|
static Key* New();
|
||||||
|
|
||||||
|
static Handle<Value> New(const Arguments& args);
|
||||||
|
static Handle<Value> GenerateSync(const Arguments& args);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
GetPrivate(Local<String> property, const AccessorInfo& info);
|
||||||
|
|
||||||
|
static void
|
||||||
|
SetPrivate(Local<String> property, Local<Value> value, const AccessorInfo& info);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
GetPublic(Local<String> property, const AccessorInfo& info);
|
||||||
|
|
||||||
|
static void
|
||||||
|
SetPublic(Local<String> property, Local<Value> value, const AccessorInfo& info);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
RegenerateSync(const Arguments& args);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
ToDER(const Arguments& args);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
FromDER(const Arguments& args);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
VerifySignature(const Arguments& args);
|
||||||
|
|
||||||
|
static void
|
||||||
|
VerifySignatureCallback(uv_work_t *req, int status);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
VerifySignatureSync(const Arguments& args);
|
||||||
|
|
||||||
|
static Handle<Value>
|
||||||
|
SignSync(const Arguments& args);
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace bitcoin
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue