addon.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. #include <array>
  2. #include <cstdlib>
  3. #include <nan.h>
  4. #include <secp256k1.h>
  5. #define THROW_BAD_ARGUMENTS Nan::ThrowTypeError("Not enough arguments")
  6. #define THROW_BAD_PRIVATE Nan::ThrowTypeError("Expected Private")
  7. #define THROW_BAD_POINT Nan::ThrowTypeError("Expected Point")
  8. #define THROW_BAD_TWEAK Nan::ThrowTypeError("Expected Tweak")
  9. #define THROW_BAD_HASH Nan::ThrowTypeError("Expected Hash")
  10. #define THROW_BAD_SIGNATURE Nan::ThrowTypeError("Expected Signature")
  11. #define THROW_BAD_EXTRA_DATA Nan::ThrowTypeError("Expected Extra Data (32 bytes)")
  12. #define EXPECT_ARGS(N) if (info.Length() < N) return THROW_BAD_ARGUMENTS
  13. #define RETURNV(X) info.GetReturnValue().Set(X)
  14. secp256k1_context* context;
  15. namespace {
  16. const std::array<uint8_t, 32> ZERO = {};
  17. const std::array<uint8_t, 32> GROUP_ORDER = {
  18. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
  19. 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
  20. };
  21. v8::Local<v8::Object> asBuffer (const unsigned char* data, const size_t length) {
  22. return Nan::CopyBuffer(reinterpret_cast<const char*>(data), static_cast<uint32_t>(length)).ToLocalChecked();
  23. }
  24. template <typename T>
  25. const unsigned char* asDataPointer (const T& x) {
  26. return reinterpret_cast<const unsigned char*>(node::Buffer::Data(x));
  27. }
  28. template <typename T>
  29. bool isScalar (const T& x) {
  30. return node::Buffer::HasInstance(x) && node::Buffer::Length(x) == 32;
  31. }
  32. template <typename T>
  33. bool isOrderScalar (const T& x) {
  34. if (!isScalar<T>(x)) return false;
  35. return memcmp(asDataPointer(x), GROUP_ORDER.data(), 32) < 0;
  36. }
  37. template <typename T>
  38. bool isPrivate (const T& x) {
  39. if (!isScalar<T>(x)) return false;
  40. return secp256k1_ec_seckey_verify(context, asDataPointer(x)) != 0;
  41. }
  42. template <typename T>
  43. bool isPoint (const T& x, secp256k1_pubkey& pubkey) {
  44. if (!node::Buffer::HasInstance(x)) return false;
  45. return secp256k1_ec_pubkey_parse(context, &pubkey, asDataPointer(x), node::Buffer::Length(x)) != 0;
  46. }
  47. template <typename A>
  48. bool __isPointCompressed (const A& x) {
  49. return node::Buffer::Length(x) == 33;
  50. }
  51. template <typename T>
  52. bool isSignature (const T& x, secp256k1_ecdsa_signature& signature) {
  53. if (!node::Buffer::HasInstance(x)) return false;
  54. if (node::Buffer::Length(x) != 64) return false;
  55. return secp256k1_ecdsa_signature_parse_compact(context, &signature, asDataPointer(x)) != 0;
  56. }
  57. v8::Local<v8::Object> pointAsBuffer (const secp256k1_pubkey& public_key, const uint32_t flags) {
  58. unsigned char output[65];
  59. size_t output_length = 65;
  60. secp256k1_ec_pubkey_serialize(context, output, &output_length, &public_key, flags);
  61. return asBuffer(output, output_length);
  62. }
  63. template <size_t index, typename I, typename A>
  64. unsigned int assumeCompression (const I& info, const A& p) {
  65. if (info.Length() <= index || info[index]->IsUndefined()) {
  66. return __isPointCompressed(p) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
  67. }
  68. #if (NODE_MODULE_VERSION > NODE_11_0_MODULE_VERSION)
  69. return info[index]->BooleanValue(v8::Isolate::GetCurrent()) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
  70. #else
  71. return info[index]->BooleanValue(Nan::GetCurrentContext()).FromJust() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
  72. #endif
  73. }
  74. template <size_t index, typename I>
  75. unsigned int assumeCompression (const I& info) {
  76. if (info.Length() <= index) return SECP256K1_EC_COMPRESSED;
  77. if (info[index]->IsUndefined()) return SECP256K1_EC_COMPRESSED;
  78. #if (NODE_MODULE_VERSION > NODE_11_0_MODULE_VERSION)
  79. return info[index]->BooleanValue(v8::Isolate::GetCurrent()) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
  80. #else
  81. return info[index]->BooleanValue(Nan::GetCurrentContext()).FromJust() ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
  82. #endif
  83. }
  84. }
  85. // returns Bool
  86. NAN_METHOD(eccIsPoint) {
  87. Nan::HandleScope scope;
  88. EXPECT_ARGS(1);
  89. const auto p = info[0].As<v8::Object>();
  90. secp256k1_pubkey public_key;
  91. return RETURNV(isPoint(p, public_key));
  92. }
  93. // returns Bool
  94. NAN_METHOD(eccIsPointCompressed) {
  95. Nan::HandleScope scope;
  96. EXPECT_ARGS(1);
  97. const auto p = info[0].As<v8::Object>();
  98. secp256k1_pubkey public_key;
  99. if (!isPoint(p, public_key)) return THROW_BAD_POINT;
  100. return RETURNV(__isPointCompressed(p));
  101. }
  102. // returns Bool
  103. NAN_METHOD(eccIsPrivate) {
  104. Nan::HandleScope scope;
  105. EXPECT_ARGS(1);
  106. const auto d = info[0].As<v8::Object>();
  107. return RETURNV(isPrivate(d));
  108. }
  109. // returns ?Point
  110. NAN_METHOD(eccPointAdd) {
  111. Nan::HandleScope scope;
  112. EXPECT_ARGS(2);
  113. const auto pA = info[0].As<v8::Object>();
  114. const auto pB = info[1].As<v8::Object>();
  115. secp256k1_pubkey a, b;
  116. if (!isPoint(pA, a)) return THROW_BAD_POINT;
  117. if (!isPoint(pB, b)) return THROW_BAD_POINT;
  118. const secp256k1_pubkey* points[] = { &a, &b };
  119. secp256k1_pubkey p;
  120. if (secp256k1_ec_pubkey_combine(context, &p, points, 2) == 0) return RETURNV(Nan::Null());
  121. const auto flags = assumeCompression<2>(info, pA);
  122. return RETURNV(pointAsBuffer(p, flags));
  123. }
  124. // returns ?Point
  125. NAN_METHOD(eccPointAddScalar) {
  126. Nan::HandleScope scope;
  127. EXPECT_ARGS(2);
  128. const auto p = info[0].As<v8::Object>();
  129. const auto tweak = info[1].As<v8::Object>();
  130. secp256k1_pubkey public_key;
  131. if (!isPoint(p, public_key)) return THROW_BAD_POINT;
  132. if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
  133. if (secp256k1_ec_pubkey_tweak_add(context, &public_key, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());
  134. const auto flags = assumeCompression<2>(info, p);
  135. return RETURNV(pointAsBuffer(public_key, flags));
  136. }
  137. // returns Point
  138. NAN_METHOD(eccPointCompress) {
  139. Nan::HandleScope scope;
  140. EXPECT_ARGS(1);
  141. const auto p = info[0].As<v8::Object>();
  142. secp256k1_pubkey public_key;
  143. if (!isPoint(p, public_key)) return THROW_BAD_POINT;
  144. const auto flags = assumeCompression<1>(info, p);
  145. return RETURNV(pointAsBuffer(public_key, flags));
  146. }
  147. // returns ?Point
  148. NAN_METHOD(eccPointFromScalar) {
  149. Nan::HandleScope scope;
  150. EXPECT_ARGS(1);
  151. const auto d = info[0].As<v8::Object>();
  152. if (!isPrivate(d)) return THROW_BAD_PRIVATE;
  153. secp256k1_pubkey public_key;
  154. if (secp256k1_ec_pubkey_create(context, &public_key, asDataPointer(d)) == 0) return RETURNV(Nan::Null());
  155. const auto flags = assumeCompression<1>(info);
  156. return RETURNV(pointAsBuffer(public_key, flags));
  157. }
  158. // returns ?Point
  159. NAN_METHOD(eccPointMultiply) {
  160. Nan::HandleScope scope;
  161. EXPECT_ARGS(2);
  162. const auto p = info[0].As<v8::Object>();
  163. const auto tweak = info[1].As<v8::Object>();
  164. secp256k1_pubkey public_key;
  165. if (!isPoint(p, public_key)) return THROW_BAD_POINT;
  166. if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
  167. if (secp256k1_ec_pubkey_tweak_mul(context, &public_key, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());
  168. const auto flags = assumeCompression<2>(info, p);
  169. return RETURNV(pointAsBuffer(public_key, flags));
  170. }
  171. // returns ?Secret
  172. NAN_METHOD(eccPrivateAdd) {
  173. Nan::HandleScope scope;
  174. EXPECT_ARGS(2);
  175. const auto d = info[0].As<v8::Object>();
  176. const auto tweak = info[1].As<v8::Object>();
  177. if (!isPrivate(d)) return THROW_BAD_PRIVATE;
  178. if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
  179. unsigned char output[32];
  180. memcpy(output, asDataPointer(d), 32);
  181. if (secp256k1_ec_privkey_tweak_add(context, output, asDataPointer(tweak)) == 0) return RETURNV(Nan::Null());
  182. return RETURNV(asBuffer(output, 32));
  183. }
  184. // returns ?Secret
  185. NAN_METHOD(eccPrivateSub) {
  186. Nan::HandleScope scope;
  187. EXPECT_ARGS(2);
  188. const auto d = info[0].As<v8::Object>();
  189. const auto tweak = info[1].As<v8::Object>();
  190. if (!isPrivate(d)) return THROW_BAD_PRIVATE;
  191. if (!isOrderScalar(tweak)) return THROW_BAD_TWEAK;
  192. unsigned char tweak_negated[32];
  193. memcpy(tweak_negated, asDataPointer(tweak), 32);
  194. secp256k1_ec_privkey_negate(context, tweak_negated); // returns 1 always
  195. unsigned char output[32];
  196. memcpy(output, asDataPointer(d), 32);
  197. if (secp256k1_ec_privkey_tweak_add(context, output, tweak_negated) == 0) return RETURNV(Nan::Null());
  198. return RETURNV(asBuffer(output, 32));
  199. }
  200. // returns Signature
  201. NAN_METHOD(ecdsaSign) {
  202. Nan::HandleScope scope;
  203. EXPECT_ARGS(2);
  204. const auto hash = info[0].As<v8::Object>();
  205. const auto d = info[1].As<v8::Object>();
  206. if (!isScalar(hash)) return THROW_BAD_HASH;
  207. if (!isPrivate(d)) return THROW_BAD_PRIVATE;
  208. secp256k1_ecdsa_signature signature;
  209. if (secp256k1_ecdsa_sign(
  210. context,
  211. &signature,
  212. asDataPointer(hash),
  213. asDataPointer(d),
  214. secp256k1_nonce_function_rfc6979,
  215. nullptr
  216. ) == 0) return THROW_BAD_SIGNATURE;
  217. unsigned char output[64];
  218. secp256k1_ecdsa_signature_serialize_compact(context, output, &signature);
  219. return RETURNV(asBuffer(output, 64));
  220. }
  221. // returns Signature
  222. NAN_METHOD(ecdsaSignWithEntropy) {
  223. Nan::HandleScope scope;
  224. EXPECT_ARGS(2);
  225. const auto hash = info[0].As<v8::Object>();
  226. const auto d = info[1].As<v8::Object>();
  227. const auto addData = info[2].As<v8::Object>();
  228. if (!isScalar(hash)) return THROW_BAD_HASH;
  229. if (!isPrivate(d)) return THROW_BAD_PRIVATE;
  230. if (!addData->IsUndefined() && !isScalar(addData)) return THROW_BAD_EXTRA_DATA;
  231. const unsigned char* extraData;
  232. if (addData->IsUndefined()) {
  233. extraData = nullptr;
  234. } else {
  235. extraData = asDataPointer(addData);
  236. }
  237. secp256k1_ecdsa_signature signature;
  238. if (secp256k1_ecdsa_sign(
  239. context,
  240. &signature,
  241. asDataPointer(hash),
  242. asDataPointer(d),
  243. secp256k1_nonce_function_rfc6979,
  244. extraData
  245. ) == 0) return THROW_BAD_SIGNATURE;
  246. unsigned char output[64];
  247. secp256k1_ecdsa_signature_serialize_compact(context, output, &signature);
  248. return RETURNV(asBuffer(output, 64));
  249. }
  250. // returns Bool
  251. NAN_METHOD(ecdsaVerify) {
  252. Nan::HandleScope scope;
  253. EXPECT_ARGS(3);
  254. const auto hash = info[0].As<v8::Object>();
  255. const auto p = info[1].As<v8::Object>();
  256. const auto sig = info[2].As<v8::Object>();
  257. auto strict = false;
  258. if (info.Length() > 3 && !info[3]->IsUndefined()) {
  259. #if (NODE_MODULE_VERSION > NODE_11_0_MODULE_VERSION)
  260. strict = info[3]->BooleanValue(v8::Isolate::GetCurrent());
  261. #else
  262. strict = info[3]->BooleanValue(Nan::GetCurrentContext()).FromJust();
  263. #endif
  264. }
  265. secp256k1_pubkey public_key;
  266. secp256k1_ecdsa_signature signature;
  267. if (!isScalar(hash)) return THROW_BAD_HASH;
  268. if (!isPoint(p, public_key)) return THROW_BAD_POINT;
  269. if (!isSignature(sig, signature)) return THROW_BAD_SIGNATURE;
  270. if (!strict) {
  271. const auto copy = signature;
  272. secp256k1_ecdsa_signature_normalize(context, &signature, &copy);
  273. }
  274. const auto result = secp256k1_ecdsa_verify(context, &signature, asDataPointer(hash), &public_key) == 1;
  275. return RETURNV(result);
  276. }
  277. NAN_MODULE_INIT(Init) {
  278. context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
  279. // ecc
  280. Nan::Export(target, "isPoint", eccIsPoint);
  281. Nan::Export(target, "isPointCompressed", eccIsPointCompressed);
  282. Nan::Export(target, "isPrivate", eccIsPrivate);
  283. Nan::Export(target, "pointAdd", eccPointAdd);
  284. Nan::Export(target, "pointAddScalar", eccPointAddScalar);
  285. Nan::Export(target, "pointCompress", eccPointCompress);
  286. Nan::Export(target, "pointFromScalar", eccPointFromScalar);
  287. Nan::Export(target, "pointMultiply", eccPointMultiply);
  288. Nan::Export(target, "privateAdd", eccPrivateAdd);
  289. Nan::Export(target, "privateSub", eccPrivateSub);
  290. // ecdsa
  291. Nan::Export(target, "sign", ecdsaSign);
  292. Nan::Export(target, "signWithEntropy", ecdsaSignWithEntropy);
  293. Nan::Export(target, "verify", ecdsaVerify);
  294. }
  295. NODE_MODULE(secp256k1, Init)