points.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #include <iostream>
  2. #include <tuple>
  3. #include <vector>
  4. #include "shared.hpp"
  5. using V = uint8_t_vec;
  6. // ref https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c#L2160
  7. // iteratively verifies that (d + ...)G == (dG + ...G)
  8. template <typename A, typename B, typename C, typename D>
  9. void test_ec_combine (B& pa, C& pas, D& pfs) {
  10. bool ok = true;
  11. auto sum = ONE;
  12. auto sumQ = _pointFromScalar<A>(sum, ok);
  13. assert(ok);
  14. for (int i = 1; i <= 10; i++) {
  15. const auto d = randomPrivate();
  16. const auto Q = _pointFromScalar<A>(d, ok);
  17. assert(ok);
  18. // dG + ...G
  19. const auto V = _pointAdd<A>(sumQ, Q, ok);
  20. assert(ok);
  21. // (d + ...)G
  22. const auto U = _pointAddScalar<A>(sumQ, d, ok);
  23. assert(ok);
  24. assert(V == U);
  25. // (d + ...)G
  26. sum = _privAdd(sum, d, ok);
  27. assert(ok);
  28. const auto R = _pointFromScalar<A>(sum, ok);
  29. assert(ok);
  30. assert(V == R);
  31. pa.push_back({ sumQ, Q, V });
  32. pas.push_back({ sumQ, d, V });
  33. pfs.push_back({ sum, V });
  34. sumQ = V;
  35. }
  36. }
  37. struct IP { V a; bool e; std::string desc = ""; };
  38. struct PA { V a; V b; V e; std::string except = ""; std::string desc = ""; };
  39. struct PAS { V a; uint8_t_32 b; V e; std::string except = ""; std::string desc = ""; };
  40. struct PC { V a; bool b; V e; std::string except = ""; std::string desc = ""; };
  41. struct PFS { uint8_t_32 a; V e; std::string except = ""; std::string desc = ""; };
  42. auto generate () {
  43. using A = uint8_t_33;
  44. bool ok = true;
  45. const auto G_LESS_1 = _pointFromScalar<A>(GROUP_ORDER_LESS_1, ok);
  46. const auto G_LESS_2 = _pointFromScalar<A>(GROUP_ORDER_LESS_2, ok);
  47. const auto G_LESS_3 = _pointFromScalar<A>(GROUP_ORDER_LESS_3, ok);
  48. const auto G_ONE = _pointFromUInt32<A>(1, ok);
  49. const auto G_TWO = _pointFromUInt32<A>(2, ok);
  50. const auto G_THREE = _pointFromUInt32<A>(3, ok);
  51. const auto G_FOUR = _pointFromUInt32<A>(4, ok);
  52. assert(ok);
  53. const auto NULLQ = vectorify(Null<A>());
  54. const auto BAD_POINTS_C = generateBadPoints<uint8_t_33>();
  55. const auto BAD_POINTS = generateBadPoints<uint8_t_65>();
  56. assert(jsonify(G_ONE) == jsonify(G)); // G == G*1 (duh)
  57. ///////////////////////////////// isPoint
  58. std::vector<IP> ip = {
  59. { G, true },
  60. { G_ONE, true },
  61. { G_TWO, true },
  62. { G_THREE, true },
  63. { _pointFromX(P_LESS_1, 0x02), true, "X == P - 1" }
  64. };
  65. const auto _ip = ip; // prevent trashing ip while adding
  66. for (auto& x : _ip) ip.push_back({ _pointFlip(x.a), x.e, x.desc });
  67. for (const auto x : BAD_POINTS) ip.push_back({ x.a, false, x.desc });
  68. for (const auto x : BAD_POINTS_C) ip.push_back({ x.a, false, x.desc });
  69. // fuzz
  70. for (size_t i = 0; i < 1000; ++i) {
  71. ip.push_back({ _pointFromScalar<uint8_t_33>(randomPrivate(), ok), true }); assert(ok);
  72. }
  73. for (size_t i = 0; i < 1000; ++i) {
  74. ip.push_back({ _pointFromScalar<uint8_t_65>(randomPrivate(), ok), true });
  75. }
  76. assert(ok);
  77. ///////////////////////////////// pointAdd
  78. // XXX: only compressed point fixtures, flip for each combination when testing
  79. std::vector<PA> pa = {
  80. { G_LESS_1, G_LESS_1, G_LESS_2 },
  81. { G_LESS_1, G_LESS_2, G_LESS_3 },
  82. { G_LESS_1, G_LESS_2, G_LESS_3 },
  83. // https://github.com/bitcoin-core/secp256k1/blob/452d8e4d2a2f9f1b5be6b02e18f1ba102e5ca0b4/src/tests.c#L3857
  84. { G_ONE, G_LESS_1, NULLQ, "", "1 + -1 == 0/Infinity" },
  85. { G_ONE, G_LESS_2, G_LESS_1 }, // == -1
  86. { G_TWO, G_LESS_1, G_ONE }, // == 1
  87. { G_ONE, G_ONE, G_TWO, "", "1 + 1 == 2" },
  88. { G_ONE, G_TWO, G_THREE }
  89. };
  90. // fuzz
  91. for (size_t i = 0; i < 100; ++i) {
  92. const auto a = _pointFromScalar<A>(randomPrivate(), ok); assert(ok);
  93. const auto b = _pointFromScalar<A>(randomPrivate(), ok); assert(ok);
  94. const auto e = _pointAdd<A>(a, b, ok);
  95. pa.push_back({ a, b, e });
  96. }
  97. ///////////////////////////////// pointAddScalar
  98. // XXX: only compressed point fixtures, flip for each combination when testing
  99. std::vector<PAS> pas = {
  100. { G_LESS_1, ZERO, G_LESS_1, "", "-1 + 0 == -1" }, // #L3719
  101. { G_LESS_1, ONE, NULLQ, "", "-1 + 1 == 0" },
  102. { G_LESS_1, TWO, G_ONE },
  103. { G_LESS_1, THREE, G_TWO },
  104. { G_LESS_1, GROUP_ORDER_LESS_1, G_LESS_2 },
  105. { G_LESS_1, GROUP_ORDER_LESS_2, G_LESS_3 },
  106. { G_LESS_1, GROUP_ORDER_LESS_2, G_LESS_3 },
  107. { G_LESS_2, ONE, G_LESS_1 },
  108. { G_LESS_2, TWO, NULLQ, "", "-2 + 2 == 0" },
  109. { G_LESS_2, THREE, G_ONE },
  110. { G_ONE, GROUP_ORDER_LESS_1, NULLQ, "", "1 + -1 == 0" },
  111. { G_ONE, GROUP_ORDER_LESS_2, G_LESS_1, "", "1 + -2 == -1" },
  112. { G_TWO, GROUP_ORDER_LESS_1, G_ONE, "", "2 + -1 == 1" }
  113. };
  114. for (uint32_t i = 1; i < 5; ++i) {
  115. bool ok = true;
  116. const auto G_i = _pointFromUInt32<A>(i, ok); assert(ok);
  117. const auto G_i_p1 = _pointFromUInt32<A>(i + 1, ok); assert(ok);
  118. pas.push_back({ G_i, ONE, G_i_p1 });
  119. }
  120. ///////////////////////////////// pointCompress
  121. std::vector<PC> pc = {
  122. { G, true, G, "", "Generator" },
  123. { G, false, GU, "", "Generator (Uncompressed)" },
  124. { GU, true, G },
  125. { GU, false, GU },
  126. };
  127. for (auto i = 1; i < 10; ++i) {
  128. const auto iic = vectorify(_pointFromUInt32<uint8_t_33>(i, ok)); assert(ok);
  129. const auto ii = vectorify(_pointFromUInt32<uint8_t_65>(i, ok)); assert(ok);
  130. pc.push_back({ iic, true, iic });
  131. pc.push_back({ iic, false, ii });
  132. pc.push_back({ ii, true, iic });
  133. pc.push_back({ ii, false, ii });
  134. }
  135. // fuzz
  136. for (size_t i = 0; i < 50; ++i) {
  137. const auto ii = _pointFromScalar<uint8_t_65>(randomPrivate(), ok);
  138. assert(ok);
  139. uint8_t_32 iix;
  140. std::copy(ii.begin() + 1, ii.begin() + 33, iix.begin());
  141. const auto even = ii.at(64) % 2 == 0;
  142. const auto iic = _pointFromX(iix, even ? 0x02 : 0x03);
  143. pc.push_back({ iic, true, iic });
  144. pc.push_back({ iic, false, ii });
  145. pc.push_back({ ii, true, iic });
  146. pc.push_back({ ii, false, ii });
  147. }
  148. ///////////////////////////////// pointFromScalar
  149. // XXX: only compressed point fixtures, flip for each combination when testing
  150. std::vector<PFS> pfs = {
  151. { ONE, G_ONE, "", "== 1" }, // #L3153, #L3692
  152. { TWO, G_TWO, "", "== 2" },
  153. { THREE, G_THREE, "", "== 3" },
  154. { GROUP_ORDER_LESS_1, G_LESS_1, "", "== -1" }, // #L3171, #L3710
  155. { GROUP_ORDER_LESS_2, G_LESS_2, "", "== -2" },
  156. { GROUP_ORDER_LESS_3, G_LESS_3, "", "== -3" }
  157. };
  158. ///////////////////////////////// pointMultiply
  159. // XXX: only compressed point fixtures, flip for each combination when testing
  160. std::vector<PAS> pm = {
  161. { G_ONE, ZERO, NULLQ, "", "1 * 0 == 0" },
  162. { G_ONE, ONE, G_ONE, "", "1 * 1 == 1" },
  163. { G_ONE, TWO, G_TWO, "", "1 * 2 == 2" },
  164. { G_ONE, FOUR, G_FOUR, "", "1 * 4 == 4" },
  165. { G_TWO, ONE, G_TWO, "", "2 * 1 == 2" },
  166. { G_TWO, TWO, G_FOUR, "", "2 * 2 == 4" },
  167. { G_FOUR, ONE, G_FOUR, "", "1 * 4 == 4" }
  168. };
  169. // ref https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c#L2160
  170. test_ec_combine<A>(pa, pas, pfs);
  171. return std::make_tuple(ip, pa, pas, pc, pfs, pm);
  172. }
  173. auto generateBad () {
  174. using A = uint8_t_33;
  175. bool ok = true;
  176. const auto G_ONE = _pointFromUInt32<A>(1, ok);
  177. const auto BAD_POINTS_C = generateBadPoints<uint8_t_33>();
  178. const auto BAD_POINTS = generateBadPoints<uint8_t_65>();
  179. assert(ok);
  180. std::vector<PA> pa;
  181. for (const auto x : BAD_POINTS) {
  182. pa.push_back({ x.a, G_ONE, {}, THROW_BAD_POINT, x.desc });
  183. pa.push_back({ G_ONE, x.a, {}, THROW_BAD_POINT, x.desc });
  184. }
  185. for (const auto x : BAD_POINTS_C) {
  186. pa.push_back({ x.a, G_ONE, {}, THROW_BAD_POINT, x.desc });
  187. pa.push_back({ G_ONE, x.a, {}, THROW_BAD_POINT, x.desc });
  188. }
  189. std::vector<PAS> pas;
  190. for (const auto x : BAD_POINTS) pas.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
  191. for (const auto x : BAD_POINTS_C) pas.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
  192. for (const auto x : BAD_TWEAKS) pas.push_back({ G_ONE, x.a, {}, THROW_BAD_TWEAK, x.desc });
  193. std::vector<PC> pc;
  194. for (const auto x : BAD_POINTS) pc.push_back({ x.a, true, {}, THROW_BAD_POINT, x.desc });
  195. for (const auto x : BAD_POINTS_C) pc.push_back({ x.a, true, {}, THROW_BAD_POINT, x.desc });
  196. std::vector<PFS> pfs;
  197. for (const auto x : BAD_PRIVATES) pfs.push_back({ x.a, {}, THROW_BAD_PRIVATE, x.desc });
  198. std::vector<PAS> pm;
  199. for (const auto x : BAD_POINTS) pm.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
  200. for (const auto x : BAD_POINTS_C) pm.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
  201. for (const auto x : BAD_TWEAKS) pm.push_back({ G_ONE, x.a, {}, THROW_BAD_TWEAK, x.desc });
  202. return std::make_tuple(pa, pas, pc, pfs, pm);
  203. }
  204. template <typename A, typename B>
  205. void dumpJSON (
  206. std::ostream& o,
  207. const A& good,
  208. const B& bad
  209. ) {
  210. const auto jIP = [](auto x) {
  211. return jsonifyO({
  212. x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
  213. jsonp("P", jsonify(x.a)),
  214. jsonp("expected", jsonify(x.e))
  215. });
  216. };
  217. const auto jPA = [](auto x) {
  218. return jsonifyO({
  219. x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
  220. jsonp("P", jsonify(x.a)),
  221. jsonp("Q", jsonify(x.b)),
  222. x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
  223. x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
  224. });
  225. };
  226. const auto jPAS = [](auto x) {
  227. return jsonifyO({
  228. x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
  229. jsonp("P", jsonify(x.a)),
  230. jsonp("d", jsonify(x.b)),
  231. x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
  232. x.except.empty() ? "" : jsonp("exception", jsonify(x.except))
  233. });
  234. };
  235. const auto jPC = [](auto x) {
  236. return jsonifyO({
  237. x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
  238. jsonp("P", jsonify(x.a)),
  239. jsonp("compress", jsonify(x.b)),
  240. x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
  241. x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
  242. });
  243. };
  244. const auto jPFS = [](auto x) {
  245. return jsonifyO({
  246. x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
  247. jsonp("d", jsonify(x.a)),
  248. x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
  249. x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
  250. });
  251. };
  252. o << jsonifyO({
  253. jsonp("valid", jsonifyO({
  254. jsonp("isPoint", jsonifyA(std::get<0>(good), jIP)),
  255. jsonp("pointAdd", jsonifyA(std::get<1>(good), jPA)),
  256. jsonp("pointAddScalar", jsonifyA(std::get<2>(good), jPAS)),
  257. jsonp("pointCompress", jsonifyA(std::get<3>(good), jPC)),
  258. jsonp("pointFromScalar", jsonifyA(std::get<4>(good), jPFS)),
  259. jsonp("pointMultiply", jsonifyA(std::get<5>(good), jPAS))
  260. })),
  261. jsonp("invalid", jsonifyO({
  262. jsonp("pointAdd", jsonifyA(std::get<0>(bad), jPA)),
  263. jsonp("pointAddScalar", jsonifyA(std::get<1>(bad), jPAS)),
  264. jsonp("pointCompress", jsonifyA(std::get<2>(bad), jPC)),
  265. jsonp("pointFromScalar", jsonifyA(std::get<3>(bad), jPFS)),
  266. jsonp("pointMultiply", jsonifyA(std::get<4>(bad), jPAS))
  267. }))
  268. });
  269. }
  270. int main () {
  271. _ec_init();
  272. const auto a = generate();
  273. const auto b = generateBad();
  274. dumpJSON(std::cout, a, b);
  275. return 0;
  276. }