Skip to content

Commit

Permalink
add tiny secp256k1 native binding
Browse files Browse the repository at this point in the history
  • Loading branch information
dcousens committed Oct 11, 2017
1 parent acb9455 commit 03a1d0a
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
build/
coverage/
node_modules/
3 changes: 3 additions & 0 deletions .gitmodules
@@ -0,0 +1,3 @@
[submodule "native/secp256k1"]
path = native/secp256k1
url = git@github.com:bitcoin-core/secp256k1.git
90 changes: 90 additions & 0 deletions binding.gyp
@@ -0,0 +1,90 @@
{
"targets": [{
"target_name": "secp256k1",
"variables": {
"conditions": [
[
"OS=='win'", {
"with_gmp%": "false"
}, {
"with_gmp%": "<!(scripts/checklib gmpxx && scripts/checklib gmp)"
}
]
]
},
"sources": [
"./native/addon.cpp",
"./native/secp256k1/src/secp256k1.c"
],
"include_dirs": [
"/usr/local/include",
"./native/secp256k1",
"./native/secp256k1/contrib",
"./native/secp256k1/include",
"./native/secp256k1/src",
"<!(node -e \"require('nan')\")"
],
"defines": [],
"cflags": [
"-Wall",
"-Wno-maybe-uninitialized",
"-Wno-uninitialized",
"-Wno-unused-function",
"-Wextra"
],
"cflags_cc+": [
"-std=c++11"
],
"conditions": [
[
"with_gmp=='true'", {
"defines": [
"HAVE_LIBGMP=1",
"USE_NUM_GMP=1",
"USE_FIELD_INV_NUM=1",
"USE_SCALAR_INV_NUM=1"
],
"libraries": [
"-lgmpxx",
"-lgmp"
]
}, {
"defines": [
"USE_NUM_NONE=1",
"USE_FIELD_INV_BUILTIN=1",
"USE_SCALAR_INV_BUILTIN=1"
]
}
],
[
"target_arch=='x64' and OS!='win'", {
"defines": [
"HAVE___INT128=1",
"USE_ASM_X86_64=1",
"USE_FIELD_5X52=1",
"USE_FIELD_5X52_INT128=1",
"USE_SCALAR_4X64=1"
]
}, {
"defines": [
"USE_FIELD_10X26=1",
"USE_SCALAR_8X32=1"
]
}
],
[
"OS=='mac'", {
"libraries": [
"-L/usr/local/lib"
],
"xcode_settings": {
"MACOSX_DEPLOYMENT_TARGET": "10.7",
"OTHER_CPLUSPLUSFLAGS": [
"-stdlib=libc++"
]
}
}
]
]
}]
}
130 changes: 130 additions & 0 deletions native/addon.cpp
@@ -0,0 +1,130 @@
#include <memory>
#include <node.h>
#include <nan.h>
#include <secp256k1.h>

#define EXPECT_ARGS(N) if (info.Length() != N) return Nan::ThrowTypeError("Wrong number of arguments")
#define THROW_PRIVATE_KEY Nan::ThrowTypeError("Expected Private Key")
#define THROW_PUBLIC_KEY Nan::ThrowTypeError("Expected Public Key")
#define THROW_BAD_TWEAK Nan::ThrowTypeError("Expected Tweak")
#define RETURNV(X) info.GetReturnValue().Set(X)

extern secp256k1_context* secp256k1ctx;

namespace {
v8::Local<v8::Object> asBuffer (const unsigned char* data, const size_t length) {
return Nan::CopyBuffer(reinterpret_cast<const char*>(data), static_cast<uint32_t>(length)).ToLocalChecked();
}

template <typename T>
const unsigned char* asDataPointer (const T& x) {
return reinterpret_cast<const unsigned char*>(node::Buffer::Data(x));
}

template <typename T>
bool isUInt256 (const T& x) {
return node::Buffer::HasInstance(x) && node::Buffer::Length(x) == 32;
}

template <typename T>
bool isPrivateKey (const T& x) {
if (!isUInt256<T>(x)) return false;
return secp256k1_ec_seckey_verify(secp256k1ctx, asDataPointer(x)) == 1;
}

template <typename T>
bool isPublicKey (const T& x, secp256k1_pubkey& pubkey) {
if (!node::Buffer::HasInstance(x)) return false;
return secp256k1_ec_pubkey_parse(secp256k1ctx, &pubkey, asDataPointer(x), node::Buffer::Length(x)) != 0;
}
}

NAN_METHOD(privateKeyTweakAdd) {
Nan::HandleScope scope;
EXPECT_ARGS(2);

const auto priv = info[0].As<v8::Object>();
const auto tweak = info[1].As<v8::Object>();
if (!isPrivateKey(priv)) return THROW_PRIVATE_KEY;
if (!isUInt256(tweak)) return THROW_BAD_TWEAK;

unsigned char output[32];
memcpy(output, asDataPointer(priv), 32);
if (secp256k1_ec_privkey_tweak_add(secp256k1ctx, output, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());

return RETURNV(asBuffer(output, 32));
}

NAN_METHOD(privateKeyValidate) {
Nan::HandleScope scope;
EXPECT_ARGS(1);

const auto priv = info[0].As<v8::Object>();
return RETURNV(isPrivateKey(priv));
}

NAN_METHOD(publicKeyDerive) {
Nan::HandleScope scope;
EXPECT_ARGS(2);

const auto priv = info[0].As<v8::Object>();
const auto flags = info[1]->BooleanValue() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
if (!isPrivateKey(priv)) return THROW_PRIVATE_KEY;

secp256k1_pubkey public_key;
if (secp256k1_ec_pubkey_create(secp256k1ctx, &public_key, asDataPointer(priv)) == 0) return RETURNV(Nan::Null());

unsigned char output[65];
size_t output_length = 65;
secp256k1_ec_pubkey_serialize(secp256k1ctx, output, &output_length, &public_key, flags);

return RETURNV(asBuffer(output, output_length));
}

NAN_METHOD(publicKeyReform) {
Nan::HandleScope scope;
EXPECT_ARGS(2);

const auto pub = info[0].As<v8::Object>();
const auto flags = info[1]->BooleanValue() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;

secp256k1_pubkey public_key;
if (!isPublicKey(pub, public_key)) return THROW_PUBLIC_KEY;

unsigned char output[65];
size_t output_length = 65;
secp256k1_ec_pubkey_serialize(secp256k1ctx, output, &output_length, &public_key, flags);

return RETURNV(asBuffer(output, output_length));
}

NAN_METHOD(publicKeyTweakAdd) {
Nan::HandleScope scope;
EXPECT_ARGS(3);

const auto pub = info[0].As<v8::Object>();
const auto tweak = info[1].As<v8::Object>();
const auto flags = info[2]->BooleanValue() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;

secp256k1_pubkey public_key;
if (!isPublicKey(pub, public_key)) return THROW_PUBLIC_KEY;
if (!isUInt256(tweak)) return THROW_BAD_TWEAK;

if (secp256k1_ec_pubkey_tweak_add(secp256k1ctx, &public_key, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());

unsigned char output[65];
size_t output_length = 65;
secp256k1_ec_pubkey_serialize(secp256k1ctx, output, &output_length, &public_key, flags);

return RETURNV(asBuffer(output, output_length));
}

NAN_METHOD(publicKeyValidate) {
Nan::HandleScope scope;
EXPECT_ARGS(1);

const auto pub = info[0].As<v8::Object>();

secp256k1_pubkey public_key;
return RETURNV(isPublicKey(pub, public_key));
}
2 changes: 2 additions & 0 deletions native/index.js
@@ -0,0 +1,2 @@
'use strict'
module.exports = require('bindings')('secp256k1')
1 change: 1 addition & 0 deletions native/secp256k1
Submodule secp256k1 added at 0b7024
28 changes: 28 additions & 0 deletions package.json
@@ -0,0 +1,28 @@
{
"name": "tiny-secp256k1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"gypfile": true,
"engines": {
"node": ">=4.0.0"
},
"scripts": {
"build": "node-gyp rebuild"
},
"repository": {
"type": "git",
"url": "git+https://github.com/dcousens/secp256k1-tiny.git"
},
"author": "",
"license": "ISC",
"gypfile": true,
"bugs": {
"url": "https://github.com/dcousens/secp256k1-tiny/issues"
},
"homepage": "https://github.com/dcousens/secp256k1-tiny#readme",
"dependencies": {
"bindings": "^1.3.0",
"nan": "^2.7.0"
}
}
26 changes: 26 additions & 0 deletions scripts/checklib
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
function check () {
local regex="lib$1.+(so|dylib)"

# Add /sbin to path as ldconfig is located there on some systems - e.g. Debian
# (and it still can be used by unprivileged users):
PATH="$PATH:/sbin"
export PATH

# Try just checking common library locations
for dir in /lib /usr/lib /usr/local/lib /opt/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu; do
if test -d $dir; then
# shellcheck disable=SC2010
ls $dir | grep -E "$regex" && return 0
fi
done

return 1
}

check "$1" > /dev/null
if test "$?" -eq 0; then
echo true
else
echo false
fi

0 comments on commit 03a1d0a

Please sign in to comment.