123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- #include <array>
- #include <cstdlib>
- #include <nan.h>
- #include <secp256k1.h>
- #define THROW_BAD_ARGUMENTS Nan::ThrowTypeError("Not enough arguments")
- #define THROW_BAD_PRIVATE Nan::ThrowTypeError("Expected Private")
- #define THROW_BAD_POINT Nan::ThrowTypeError("Expected Point")
- #define THROW_BAD_TWEAK Nan::ThrowTypeError("Expected Tweak")
- #define THROW_BAD_HASH Nan::ThrowTypeError("Expected Hash")
- #define THROW_BAD_SIGNATURE Nan::ThrowTypeError("Expected Signature")
- #define THROW_BAD_EXTRA_DATA Nan::ThrowTypeError("Expected Extra Data (32 bytes)")
- #define EXPECT_ARGS(N) if (info.Length() < N) return THROW_BAD_ARGUMENTS
- #define RETURNV(X) info.GetReturnValue().Set(X)
- secp256k1_context* context;
- namespace {
- const std::array<uint8_t, 32> ZERO = {};
- const std::array<uint8_t, 32> GROUP_ORDER = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
- };
- 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 isScalar (const T& x) {
- return node::Buffer::HasInstance(x) && node::Buffer::Length(x) == 32;
- }
- template <typename T>
- bool isOrderScalar (const T& x) {
- if (!isScalar<T>(x)) return false;
- return memcmp(asDataPointer(x), GROUP_ORDER.data(), 32) < 0;
- }
- template <typename T>
- bool isPrivate (const T& x) {
- if (!isScalar<T>(x)) return false;
- return secp256k1_ec_seckey_verify(context, asDataPointer(x)) != 0;
- }
- template <typename T>
- bool isPoint (const T& x, secp256k1_pubkey& pubkey) {
- if (!node::Buffer::HasInstance(x)) return false;
- return secp256k1_ec_pubkey_parse(context, &pubkey, asDataPointer(x), node::Buffer::Length(x)) != 0;
- }
- template <typename A>
- bool __isPointCompressed (const A& x) {
- return node::Buffer::Length(x) == 33;
- }
- template <typename T>
- bool isSignature (const T& x, secp256k1_ecdsa_signature& signature) {
- if (!node::Buffer::HasInstance(x)) return false;
- if (node::Buffer::Length(x) != 64) return false;
- return secp256k1_ecdsa_signature_parse_compact(context, &signature, asDataPointer(x)) != 0;
- }
- v8::Local<v8::Object> pointAsBuffer (const secp256k1_pubkey& public_key, const uint32_t flags) {
- unsigned char output[65];
- size_t output_length = 65;
- secp256k1_ec_pubkey_serialize(context, output, &output_length, &public_key, flags);
- return asBuffer(output, output_length);
- }
- template <size_t index, typename I, typename A>
- unsigned int assumeCompression (const I& info, const A& p) {
- if (info.Length() <= index || info[index]->IsUndefined()) {
- return __isPointCompressed(p) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
- }
- #if (NODE_MODULE_VERSION > NODE_11_0_MODULE_VERSION)
- return info[index]->BooleanValue(v8::Isolate::GetCurrent()) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
- #else
- return info[index]->BooleanValue(Nan::GetCurrentContext()).FromJust() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
- #endif
- }
- template <size_t index, typename I>
- unsigned int assumeCompression (const I& info) {
- if (info.Length() <= index) return SECP256K1_EC_COMPRESSED;
- if (info[index]->IsUndefined()) return SECP256K1_EC_COMPRESSED;
- #if (NODE_MODULE_VERSION > NODE_11_0_MODULE_VERSION)
- return info[index]->BooleanValue(v8::Isolate::GetCurrent()) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
- #else
- return info[index]->BooleanValue(Nan::GetCurrentContext()).FromJust() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
- #endif
- }
- }
- // returns Bool
- NAN_METHOD(eccIsPoint) {
- Nan::HandleScope scope;
- EXPECT_ARGS(1);
- const auto p = info[0].As<v8::Object>();
- secp256k1_pubkey public_key;
- return RETURNV(isPoint(p, public_key));
- }
- // returns Bool
- NAN_METHOD(eccIsPointCompressed) {
- Nan::HandleScope scope;
- EXPECT_ARGS(1);
- const auto p = info[0].As<v8::Object>();
- secp256k1_pubkey public_key;
- if (!isPoint(p, public_key)) return THROW_BAD_POINT;
- return RETURNV(__isPointCompressed(p));
- }
- // returns Bool
- NAN_METHOD(eccIsPrivate) {
- Nan::HandleScope scope;
- EXPECT_ARGS(1);
- const auto d = info[0].As<v8::Object>();
- return RETURNV(isPrivate(d));
- }
- // returns ?Point
- NAN_METHOD(eccPointAdd) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto pA = info[0].As<v8::Object>();
- const auto pB = info[1].As<v8::Object>();
- secp256k1_pubkey a, b;
- if (!isPoint(pA, a)) return THROW_BAD_POINT;
- if (!isPoint(pB, b)) return THROW_BAD_POINT;
- const secp256k1_pubkey* points[] = { &a, &b };
- secp256k1_pubkey p;
- if (secp256k1_ec_pubkey_combine(context, &p, points, 2) == 0) return RETURNV(Nan::Null());
- const auto flags = assumeCompression<2>(info, pA);
- return RETURNV(pointAsBuffer(p, flags));
- }
- // returns ?Point
- NAN_METHOD(eccPointAddScalar) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto p = info[0].As<v8::Object>();
- const auto tweak = info[1].As<v8::Object>();
- secp256k1_pubkey public_key;
- if (!isPoint(p, public_key)) return THROW_BAD_POINT;
- if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
- if (secp256k1_ec_pubkey_tweak_add(context, &public_key, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());
- const auto flags = assumeCompression<2>(info, p);
- return RETURNV(pointAsBuffer(public_key, flags));
- }
- // returns Point
- NAN_METHOD(eccPointCompress) {
- Nan::HandleScope scope;
- EXPECT_ARGS(1);
- const auto p = info[0].As<v8::Object>();
- secp256k1_pubkey public_key;
- if (!isPoint(p, public_key)) return THROW_BAD_POINT;
- const auto flags = assumeCompression<1>(info, p);
- return RETURNV(pointAsBuffer(public_key, flags));
- }
- // returns ?Point
- NAN_METHOD(eccPointFromScalar) {
- Nan::HandleScope scope;
- EXPECT_ARGS(1);
- const auto d = info[0].As<v8::Object>();
- if (!isPrivate(d)) return THROW_BAD_PRIVATE;
- secp256k1_pubkey public_key;
- if (secp256k1_ec_pubkey_create(context, &public_key, asDataPointer(d)) == 0) return RETURNV(Nan::Null());
- const auto flags = assumeCompression<1>(info);
- return RETURNV(pointAsBuffer(public_key, flags));
- }
- // returns ?Point
- NAN_METHOD(eccPointMultiply) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto p = info[0].As<v8::Object>();
- const auto tweak = info[1].As<v8::Object>();
- secp256k1_pubkey public_key;
- if (!isPoint(p, public_key)) return THROW_BAD_POINT;
- if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
- if (secp256k1_ec_pubkey_tweak_mul(context, &public_key, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());
- const auto flags = assumeCompression<2>(info, p);
- return RETURNV(pointAsBuffer(public_key, flags));
- }
- // returns ?Secret
- NAN_METHOD(eccPrivateAdd) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto d = info[0].As<v8::Object>();
- const auto tweak = info[1].As<v8::Object>();
- if (!isPrivate(d)) return THROW_BAD_PRIVATE;
- if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
- unsigned char output[32];
- memcpy(output, asDataPointer(d), 32);
- if (secp256k1_ec_privkey_tweak_add(context, output, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());
- return RETURNV(asBuffer(output, 32));
- }
- // returns ?Secret
- NAN_METHOD(eccPrivateSub) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto d = info[0].As<v8::Object>();
- const auto tweak = info[1].As<v8::Object>();
- if (!isPrivate(d)) return THROW_BAD_PRIVATE;
- if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
- unsigned char tweak_negated[32];
- memcpy(tweak_negated, asDataPointer(tweak), 32);
- secp256k1_ec_privkey_negate(context, tweak_negated); // returns 1 always
- unsigned char output[32];
- memcpy(output, asDataPointer(d), 32);
- if (secp256k1_ec_privkey_tweak_add(context, output, tweak_negated) == 0) return RETURNV(Nan::Null());
- return RETURNV(asBuffer(output, 32));
- }
- // returns Signature
- NAN_METHOD(ecdsaSign) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto hash = info[0].As<v8::Object>();
- const auto d = info[1].As<v8::Object>();
- if (!isScalar(hash)) return THROW_BAD_HASH;
- if (!isPrivate(d)) return THROW_BAD_PRIVATE;
- secp256k1_ecdsa_signature signature;
- if (secp256k1_ecdsa_sign(
- context,
- &signature,
- asDataPointer(hash),
- asDataPointer(d),
- secp256k1_nonce_function_rfc6979,
- nullptr
- ) == 0) return THROW_BAD_SIGNATURE;
- unsigned char output[64];
- secp256k1_ecdsa_signature_serialize_compact(context, output, &signature);
- return RETURNV(asBuffer(output, 64));
- }
- // returns Signature
- NAN_METHOD(ecdsaSignWithEntropy) {
- Nan::HandleScope scope;
- EXPECT_ARGS(2);
- const auto hash = info[0].As<v8::Object>();
- const auto d = info[1].As<v8::Object>();
- const auto addData = info[2].As<v8::Object>();
- if (!isScalar(hash)) return THROW_BAD_HASH;
- if (!isPrivate(d)) return THROW_BAD_PRIVATE;
- if (!addData->IsUndefined() && !isScalar(addData)) return THROW_BAD_EXTRA_DATA;
- const unsigned char* extraData;
- if (addData->IsUndefined()) {
- extraData = nullptr;
- } else {
- extraData = asDataPointer(addData);
- }
- secp256k1_ecdsa_signature signature;
- if (secp256k1_ecdsa_sign(
- context,
- &signature,
- asDataPointer(hash),
- asDataPointer(d),
- secp256k1_nonce_function_rfc6979,
- extraData
- ) == 0) return THROW_BAD_SIGNATURE;
- unsigned char output[64];
- secp256k1_ecdsa_signature_serialize_compact(context, output, &signature);
- return RETURNV(asBuffer(output, 64));
- }
- // returns Bool
- NAN_METHOD(ecdsaVerify) {
- Nan::HandleScope scope;
- EXPECT_ARGS(3);
- const auto hash = info[0].As<v8::Object>();
- const auto p = info[1].As<v8::Object>();
- const auto sig = info[2].As<v8::Object>();
- auto strict = false;
- if (info.Length() > 3 && !info[3]->IsUndefined()) {
- #if (NODE_MODULE_VERSION > NODE_11_0_MODULE_VERSION)
- strict = info[3]->BooleanValue(v8::Isolate::GetCurrent());
- #else
- strict = info[3]->BooleanValue(Nan::GetCurrentContext()).FromJust();
- #endif
- }
- secp256k1_pubkey public_key;
- secp256k1_ecdsa_signature signature;
- if (!isScalar(hash)) return THROW_BAD_HASH;
- if (!isPoint(p, public_key)) return THROW_BAD_POINT;
- if (!isSignature(sig, signature)) return THROW_BAD_SIGNATURE;
- if (!strict) {
- const auto copy = signature;
- secp256k1_ecdsa_signature_normalize(context, &signature, ©);
- }
- const auto result = secp256k1_ecdsa_verify(context, &signature, asDataPointer(hash), &public_key) == 1;
- return RETURNV(result);
- }
- NAN_MODULE_INIT(Init) {
- context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
- // ecc
- Nan::Export(target, "isPoint", eccIsPoint);
- Nan::Export(target, "isPointCompressed", eccIsPointCompressed);
- Nan::Export(target, "isPrivate", eccIsPrivate);
- Nan::Export(target, "pointAdd", eccPointAdd);
- Nan::Export(target, "pointAddScalar", eccPointAddScalar);
- Nan::Export(target, "pointCompress", eccPointCompress);
- Nan::Export(target, "pointFromScalar", eccPointFromScalar);
- Nan::Export(target, "pointMultiply", eccPointMultiply);
- Nan::Export(target, "privateAdd", eccPrivateAdd);
- Nan::Export(target, "privateSub", eccPrivateSub);
- // ecdsa
- Nan::Export(target, "sign", ecdsaSign);
- Nan::Export(target, "signWithEntropy", ecdsaSignWithEntropy);
- Nan::Export(target, "verify", ecdsaVerify);
- }
- NODE_MODULE(secp256k1, Init)
|