#include #include #include #include #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 ZERO = {}; const std::array 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 asBuffer (const unsigned char* data, const size_t length) { return Nan::CopyBuffer(reinterpret_cast(data), static_cast(length)).ToLocalChecked(); } template const unsigned char* asDataPointer (const T& x) { return reinterpret_cast(node::Buffer::Data(x)); } template bool isScalar (const T& x) { return node::Buffer::HasInstance(x) && node::Buffer::Length(x) == 32; } template bool isOrderScalar (const T& x) { if (!isScalar(x)) return false; return memcmp(asDataPointer(x), GROUP_ORDER.data(), 32) < 0; } template bool isPrivate (const T& x) { if (!isScalar(x)) return false; return secp256k1_ec_seckey_verify(context, asDataPointer(x)) != 0; } template 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 bool __isPointCompressed (const A& x) { return node::Buffer::Length(x) == 33; } template 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 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 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 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(); 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(); 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(); return RETURNV(isPrivate(d)); } // returns ?Point NAN_METHOD(eccPointAdd) { Nan::HandleScope scope; EXPECT_ARGS(2); const auto pA = info[0].As(); const auto pB = info[1].As(); 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(); const auto tweak = info[1].As(); 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(); 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(); 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(); const auto tweak = info[1].As(); 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(); const auto tweak = info[1].As(); 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(); const auto tweak = info[1].As(); 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(); const auto d = info[1].As(); 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(); const auto d = info[1].As(); const auto addData = info[2].As(); 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(); const auto p = info[1].As(); const auto sig = info[2].As(); 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)