123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- #include <iostream>
- #include <tuple>
- #include <vector>
- #include "shared.hpp"
- using V = uint8_t_vec;
- // ref https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c#L2160
- // iteratively verifies that (d + ...)G == (dG + ...G)
- template <typename A, typename B, typename C, typename D>
- void test_ec_combine (B& pa, C& pas, D& pfs) {
- bool ok = true;
- auto sum = ONE;
- auto sumQ = _pointFromScalar<A>(sum, ok);
- assert(ok);
- for (int i = 1; i <= 10; i++) {
- const auto d = randomPrivate();
- const auto Q = _pointFromScalar<A>(d, ok);
- assert(ok);
- // dG + ...G
- const auto V = _pointAdd<A>(sumQ, Q, ok);
- assert(ok);
- // (d + ...)G
- const auto U = _pointAddScalar<A>(sumQ, d, ok);
- assert(ok);
- assert(V == U);
- // (d + ...)G
- sum = _privAdd(sum, d, ok);
- assert(ok);
- const auto R = _pointFromScalar<A>(sum, ok);
- assert(ok);
- assert(V == R);
- pa.push_back({ sumQ, Q, V });
- pas.push_back({ sumQ, d, V });
- pfs.push_back({ sum, V });
- sumQ = V;
- }
- }
- struct IP { V a; bool e; std::string desc = ""; };
- struct PA { V a; V b; V e; std::string except = ""; std::string desc = ""; };
- struct PAS { V a; uint8_t_32 b; V e; std::string except = ""; std::string desc = ""; };
- struct PC { V a; bool b; V e; std::string except = ""; std::string desc = ""; };
- struct PFS { uint8_t_32 a; V e; std::string except = ""; std::string desc = ""; };
- auto generate () {
- using A = uint8_t_33;
- bool ok = true;
- const auto G_LESS_1 = _pointFromScalar<A>(GROUP_ORDER_LESS_1, ok);
- const auto G_LESS_2 = _pointFromScalar<A>(GROUP_ORDER_LESS_2, ok);
- const auto G_LESS_3 = _pointFromScalar<A>(GROUP_ORDER_LESS_3, ok);
- const auto G_ONE = _pointFromUInt32<A>(1, ok);
- const auto G_TWO = _pointFromUInt32<A>(2, ok);
- const auto G_THREE = _pointFromUInt32<A>(3, ok);
- const auto G_FOUR = _pointFromUInt32<A>(4, ok);
- assert(ok);
- const auto NULLQ = vectorify(Null<A>());
- const auto BAD_POINTS_C = generateBadPoints<uint8_t_33>();
- const auto BAD_POINTS = generateBadPoints<uint8_t_65>();
- assert(jsonify(G_ONE) == jsonify(G)); // G == G*1 (duh)
- ///////////////////////////////// isPoint
- std::vector<IP> ip = {
- { G, true },
- { G_ONE, true },
- { G_TWO, true },
- { G_THREE, true },
- { _pointFromX(P_LESS_1, 0x02), true, "X == P - 1" }
- };
- const auto _ip = ip; // prevent trashing ip while adding
- for (auto& x : _ip) ip.push_back({ _pointFlip(x.a), x.e, x.desc });
- for (const auto x : BAD_POINTS) ip.push_back({ x.a, false, x.desc });
- for (const auto x : BAD_POINTS_C) ip.push_back({ x.a, false, x.desc });
- // fuzz
- for (size_t i = 0; i < 1000; ++i) {
- ip.push_back({ _pointFromScalar<uint8_t_33>(randomPrivate(), ok), true }); assert(ok);
- }
- for (size_t i = 0; i < 1000; ++i) {
- ip.push_back({ _pointFromScalar<uint8_t_65>(randomPrivate(), ok), true });
- }
- assert(ok);
- ///////////////////////////////// pointAdd
- // XXX: only compressed point fixtures, flip for each combination when testing
- std::vector<PA> pa = {
- { G_LESS_1, G_LESS_1, G_LESS_2 },
- { G_LESS_1, G_LESS_2, G_LESS_3 },
- { G_LESS_1, G_LESS_2, G_LESS_3 },
- // https://github.com/bitcoin-core/secp256k1/blob/452d8e4d2a2f9f1b5be6b02e18f1ba102e5ca0b4/src/tests.c#L3857
- { G_ONE, G_LESS_1, NULLQ, "", "1 + -1 == 0/Infinity" },
- { G_ONE, G_LESS_2, G_LESS_1 }, // == -1
- { G_TWO, G_LESS_1, G_ONE }, // == 1
- { G_ONE, G_ONE, G_TWO, "", "1 + 1 == 2" },
- { G_ONE, G_TWO, G_THREE }
- };
- // fuzz
- for (size_t i = 0; i < 100; ++i) {
- const auto a = _pointFromScalar<A>(randomPrivate(), ok); assert(ok);
- const auto b = _pointFromScalar<A>(randomPrivate(), ok); assert(ok);
- const auto e = _pointAdd<A>(a, b, ok);
- pa.push_back({ a, b, e });
- }
- ///////////////////////////////// pointAddScalar
- // XXX: only compressed point fixtures, flip for each combination when testing
- std::vector<PAS> pas = {
- { G_LESS_1, ZERO, G_LESS_1, "", "-1 + 0 == -1" }, // #L3719
- { G_LESS_1, ONE, NULLQ, "", "-1 + 1 == 0" },
- { G_LESS_1, TWO, G_ONE },
- { G_LESS_1, THREE, G_TWO },
- { G_LESS_1, GROUP_ORDER_LESS_1, G_LESS_2 },
- { G_LESS_1, GROUP_ORDER_LESS_2, G_LESS_3 },
- { G_LESS_1, GROUP_ORDER_LESS_2, G_LESS_3 },
- { G_LESS_2, ONE, G_LESS_1 },
- { G_LESS_2, TWO, NULLQ, "", "-2 + 2 == 0" },
- { G_LESS_2, THREE, G_ONE },
- { G_ONE, GROUP_ORDER_LESS_1, NULLQ, "", "1 + -1 == 0" },
- { G_ONE, GROUP_ORDER_LESS_2, G_LESS_1, "", "1 + -2 == -1" },
- { G_TWO, GROUP_ORDER_LESS_1, G_ONE, "", "2 + -1 == 1" }
- };
- for (uint32_t i = 1; i < 5; ++i) {
- bool ok = true;
- const auto G_i = _pointFromUInt32<A>(i, ok); assert(ok);
- const auto G_i_p1 = _pointFromUInt32<A>(i + 1, ok); assert(ok);
- pas.push_back({ G_i, ONE, G_i_p1 });
- }
- ///////////////////////////////// pointCompress
- std::vector<PC> pc = {
- { G, true, G, "", "Generator" },
- { G, false, GU, "", "Generator (Uncompressed)" },
- { GU, true, G },
- { GU, false, GU },
- };
- for (auto i = 1; i < 10; ++i) {
- const auto iic = vectorify(_pointFromUInt32<uint8_t_33>(i, ok)); assert(ok);
- const auto ii = vectorify(_pointFromUInt32<uint8_t_65>(i, ok)); assert(ok);
- pc.push_back({ iic, true, iic });
- pc.push_back({ iic, false, ii });
- pc.push_back({ ii, true, iic });
- pc.push_back({ ii, false, ii });
- }
- // fuzz
- for (size_t i = 0; i < 50; ++i) {
- const auto ii = _pointFromScalar<uint8_t_65>(randomPrivate(), ok);
- assert(ok);
- uint8_t_32 iix;
- std::copy(ii.begin() + 1, ii.begin() + 33, iix.begin());
- const auto even = ii.at(64) % 2 == 0;
- const auto iic = _pointFromX(iix, even ? 0x02 : 0x03);
- pc.push_back({ iic, true, iic });
- pc.push_back({ iic, false, ii });
- pc.push_back({ ii, true, iic });
- pc.push_back({ ii, false, ii });
- }
- ///////////////////////////////// pointFromScalar
- // XXX: only compressed point fixtures, flip for each combination when testing
- std::vector<PFS> pfs = {
- { ONE, G_ONE, "", "== 1" }, // #L3153, #L3692
- { TWO, G_TWO, "", "== 2" },
- { THREE, G_THREE, "", "== 3" },
- { GROUP_ORDER_LESS_1, G_LESS_1, "", "== -1" }, // #L3171, #L3710
- { GROUP_ORDER_LESS_2, G_LESS_2, "", "== -2" },
- { GROUP_ORDER_LESS_3, G_LESS_3, "", "== -3" }
- };
- ///////////////////////////////// pointMultiply
- // XXX: only compressed point fixtures, flip for each combination when testing
- std::vector<PAS> pm = {
- { G_ONE, ZERO, NULLQ, "", "1 * 0 == 0" },
- { G_ONE, ONE, G_ONE, "", "1 * 1 == 1" },
- { G_ONE, TWO, G_TWO, "", "1 * 2 == 2" },
- { G_ONE, FOUR, G_FOUR, "", "1 * 4 == 4" },
- { G_TWO, ONE, G_TWO, "", "2 * 1 == 2" },
- { G_TWO, TWO, G_FOUR, "", "2 * 2 == 4" },
- { G_FOUR, ONE, G_FOUR, "", "1 * 4 == 4" }
- };
- // ref https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c#L2160
- test_ec_combine<A>(pa, pas, pfs);
- return std::make_tuple(ip, pa, pas, pc, pfs, pm);
- }
- auto generateBad () {
- using A = uint8_t_33;
- bool ok = true;
- const auto G_ONE = _pointFromUInt32<A>(1, ok);
- const auto BAD_POINTS_C = generateBadPoints<uint8_t_33>();
- const auto BAD_POINTS = generateBadPoints<uint8_t_65>();
- assert(ok);
- std::vector<PA> pa;
- for (const auto x : BAD_POINTS) {
- pa.push_back({ x.a, G_ONE, {}, THROW_BAD_POINT, x.desc });
- pa.push_back({ G_ONE, x.a, {}, THROW_BAD_POINT, x.desc });
- }
- for (const auto x : BAD_POINTS_C) {
- pa.push_back({ x.a, G_ONE, {}, THROW_BAD_POINT, x.desc });
- pa.push_back({ G_ONE, x.a, {}, THROW_BAD_POINT, x.desc });
- }
- std::vector<PAS> pas;
- for (const auto x : BAD_POINTS) pas.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
- for (const auto x : BAD_POINTS_C) pas.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
- for (const auto x : BAD_TWEAKS) pas.push_back({ G_ONE, x.a, {}, THROW_BAD_TWEAK, x.desc });
- std::vector<PC> pc;
- for (const auto x : BAD_POINTS) pc.push_back({ x.a, true, {}, THROW_BAD_POINT, x.desc });
- for (const auto x : BAD_POINTS_C) pc.push_back({ x.a, true, {}, THROW_BAD_POINT, x.desc });
- std::vector<PFS> pfs;
- for (const auto x : BAD_PRIVATES) pfs.push_back({ x.a, {}, THROW_BAD_PRIVATE, x.desc });
- std::vector<PAS> pm;
- for (const auto x : BAD_POINTS) pm.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
- for (const auto x : BAD_POINTS_C) pm.push_back({ x.a, ONE, {}, THROW_BAD_POINT, x.desc });
- for (const auto x : BAD_TWEAKS) pm.push_back({ G_ONE, x.a, {}, THROW_BAD_TWEAK, x.desc });
- return std::make_tuple(pa, pas, pc, pfs, pm);
- }
- template <typename A, typename B>
- void dumpJSON (
- std::ostream& o,
- const A& good,
- const B& bad
- ) {
- const auto jIP = [](auto x) {
- return jsonifyO({
- x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
- jsonp("P", jsonify(x.a)),
- jsonp("expected", jsonify(x.e))
- });
- };
- const auto jPA = [](auto x) {
- return jsonifyO({
- x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
- jsonp("P", jsonify(x.a)),
- jsonp("Q", jsonify(x.b)),
- x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
- x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
- });
- };
- const auto jPAS = [](auto x) {
- return jsonifyO({
- x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
- jsonp("P", jsonify(x.a)),
- jsonp("d", jsonify(x.b)),
- x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
- x.except.empty() ? "" : jsonp("exception", jsonify(x.except))
- });
- };
- const auto jPC = [](auto x) {
- return jsonifyO({
- x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
- jsonp("P", jsonify(x.a)),
- jsonp("compress", jsonify(x.b)),
- x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
- x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
- });
- };
- const auto jPFS = [](auto x) {
- return jsonifyO({
- x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
- jsonp("d", jsonify(x.a)),
- x.except.empty() ? jsonp("expected", isNull(x.e) ? "null" : jsonify(x.e)) : "",
- x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
- });
- };
- o << jsonifyO({
- jsonp("valid", jsonifyO({
- jsonp("isPoint", jsonifyA(std::get<0>(good), jIP)),
- jsonp("pointAdd", jsonifyA(std::get<1>(good), jPA)),
- jsonp("pointAddScalar", jsonifyA(std::get<2>(good), jPAS)),
- jsonp("pointCompress", jsonifyA(std::get<3>(good), jPC)),
- jsonp("pointFromScalar", jsonifyA(std::get<4>(good), jPFS)),
- jsonp("pointMultiply", jsonifyA(std::get<5>(good), jPAS))
- })),
- jsonp("invalid", jsonifyO({
- jsonp("pointAdd", jsonifyA(std::get<0>(bad), jPA)),
- jsonp("pointAddScalar", jsonifyA(std::get<1>(bad), jPAS)),
- jsonp("pointCompress", jsonifyA(std::get<2>(bad), jPC)),
- jsonp("pointFromScalar", jsonifyA(std::get<3>(bad), jPFS)),
- jsonp("pointMultiply", jsonifyA(std::get<4>(bad), jPAS))
- }))
- });
- }
- int main () {
- _ec_init();
- const auto a = generate();
- const auto b = generateBad();
- dumpJSON(std::cout, a, b);
- return 0;
- }
|