123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- #pragma once
- #include <array>
- #include <cassert>
- #include <iostream>
- #include <openssl/sha.h>
- #include <sstream>
- #include <vector>
- #include "../native/secp256k1/include/secp256k1.h"
- #include "hexxer.hpp"
- #include "json.hpp"
- typedef std::array<uint8_t, 32> uint8_t_32;
- typedef std::array<uint8_t, 33> uint8_t_33;
- typedef std::array<uint8_t, 64> uint8_t_64;
- typedef std::array<uint8_t, 65> uint8_t_65;
- typedef std::vector<uint8_t> uint8_t_vec;
- template <typename A>
- auto vectorify (const A a) {
- return uint8_t_vec(a.begin(), a.end());
- }
- namespace {
- uint32_t s = 0xdeadbeef;
- uint32_t xorshift32() {
- s ^= s << 13;
- s ^= s >> 17;
- s ^= s << 5;
- return s;
- }
- }
- auto randomUInt8 () {
- return xorshift32() % 255;
- }
- template <typename A>
- auto random () {
- A a;
- for (auto& x : a) x = randomUInt8();
- return a;
- }
- template <typename A>
- auto randomHigh () {
- auto x = random<A>();
- for (auto i = x.size() / 2; i < x.size(); ++i) {
- x.at(i) = 0xff;
- }
- return x;
- }
- template <typename A>
- auto randomLow () {
- auto x = random<A>();
- for (auto i = 0ul; i < x.size() / 2; ++i) {
- x.at(i) = 0;
- }
- return x;
- }
- template <typename A>
- auto fromUInt32 (const uint32_t i) {
- A x;
- x.fill(0);
- const auto s = x.size();
- x.at(s - 4) = i >> 24;
- x.at(s - 3) = i >> 16;
- x.at(s - 2) = i >> 8;
- x.at(s - 1) = i & 0xff;
- return x;
- }
- auto randomScalar () { return random<uint8_t_32>(); }
- auto randomScalarHigh () { return randomHigh<uint8_t_32>(); }
- auto randomScalarLow () { return randomLow<uint8_t_32>(); }
- auto scalarFromUInt32 (const uint32_t i) { return fromUInt32<uint8_t_32>(i); }
- template <typename A>
- auto fromHex (const std::string& s) {
- assert(s.size() == sizeof(A) * 2);
- A x;
- auto i = 0;
- for (auto& y : x) {
- const auto a = s.at(i++);
- const auto b = s.at(i++);
- y = hexxer::decode(a, b);
- }
- return x;
- }
- auto pointFromHex (const std::string& s) {
- if (s.size() == 66) return vectorify(fromHex<uint8_t_33>(s));
- if (s.size() == 130) return vectorify(fromHex<uint8_t_65>(s));
- assert(false);
- }
- auto scalarFromHex (const std::string& s) { return fromHex<uint8_t_32>(s); }
- auto signatureFromHex (const std::string& s) { return fromHex<uint8_t_64>(s); }
- secp256k1_context* ctx = nullptr;
- auto randomPrivate () {
- while (true) {
- const auto key = randomScalar();
- if (secp256k1_ec_seckey_verify(ctx, key.data())) return key;
- }
- }
- auto randomPrivateHigh () {
- while (true) {
- const auto key = randomScalarHigh();
- if (secp256k1_ec_seckey_verify(ctx, key.data())) return key;
- }
- }
- auto randomPrivateLow () {
- while (true) {
- const auto key = randomScalarLow();
- if (secp256k1_ec_seckey_verify(ctx, key.data())) return key;
- }
- }
- // utility functions
- void _ec_init () {
- ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
- }
- auto _privAdd (uint8_t_32 key, const uint8_t_32 tweak, bool& ok) {
- ok &= secp256k1_ec_privkey_tweak_add(ctx, key.data(), tweak.data());
- return key;
- }
- auto _privSub (uint8_t_32 key, uint8_t_32 tweak, bool& ok) {
- ok &= secp256k1_ec_privkey_negate(ctx, tweak.data());
- ok &= secp256k1_ec_privkey_tweak_add(ctx, key.data(), tweak.data());
- return key;
- }
- template <typename A>
- uint8_t_vec _ec_pubkey_to_vec (const secp256k1_pubkey& public_key, bool& ok) {
- static_assert(sizeof(A) == 33 || sizeof(A) == 65);
- if (!ok) return {};
- A out;
- size_t outlen = out.size();
- ok &= secp256k1_ec_pubkey_serialize(ctx, out.data(), &outlen, &public_key,
- sizeof(A) == 33 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
- return vectorify<A>(out);
- }
- template <typename A, typename V>
- auto _pointAdd (const V p, const V q, bool& ok) {
- secp256k1_pubkey a, b;
- ok &= secp256k1_ec_pubkey_parse(ctx, &a, p.data(), p.size());
- ok &= secp256k1_ec_pubkey_parse(ctx, &b, q.data(), q.size());
- const secp256k1_pubkey* points[] = { &a, &b };
- secp256k1_pubkey public_key;
- ok &= secp256k1_ec_pubkey_combine(ctx, &public_key, points, 2);
- return _ec_pubkey_to_vec<A>(public_key, ok);
- }
- template <typename A, typename V>
- auto _pointMul (const V p, const uint8_t_32 d, bool& ok) {
- secp256k1_pubkey public_key;
- ok &= secp256k1_ec_pubkey_parse(ctx, &public_key, p.data(), p.size());
- ok &= secp256k1_ec_pubkey_tweak_mul(ctx, &public_key, d.data());
- return _ec_pubkey_to_vec<A>(public_key, ok);
- }
- template <typename A, typename V>
- auto _pointAddScalar (const V p, const uint8_t_32 d, bool& ok) {
- secp256k1_pubkey public_key;
- ok &= secp256k1_ec_pubkey_parse(ctx, &public_key, p.data(), p.size());
- ok &= secp256k1_ec_pubkey_tweak_add(ctx, &public_key, d.data());
- return _ec_pubkey_to_vec<A>(public_key, ok);
- }
- uint8_t_vec _pointFlip (const uint8_t_vec& p) {
- assert(!p.empty());
- secp256k1_pubkey public_key;
- bool ok = secp256k1_ec_pubkey_parse(ctx, &public_key, p.data(), p.size());
- assert(ok);
- uint8_t_vec r;
- if (p.size() == 33) r = _ec_pubkey_to_vec<uint8_t_65>(public_key, ok);
- else r = _ec_pubkey_to_vec<uint8_t_33>(public_key, ok);
- assert(ok);
- return std::move(r);
- }
- template <typename A>
- auto _pointFromScalar (const uint8_t_32 s, bool& ok) {
- secp256k1_pubkey public_key;
- ok &= secp256k1_ec_pubkey_create(ctx, &public_key, s.data());
- return _ec_pubkey_to_vec<A>(public_key, ok);
- }
- template <typename A>
- auto _pointFromUInt32 (const uint32_t i, bool& ok) {
- return _pointFromScalar<A>(scalarFromUInt32(i), ok);
- }
- auto _pointFromX (const uint8_t_32 x, uint8_t prefix) {
- uint8_t_vec p = { prefix };
- p.reserve(33);
- for (auto i : x) p.emplace_back(i);
- return p;
- }
- auto _pointFromXY (const uint8_t_32 x, const uint8_t_32 y, const uint8_t prefix = 0x04) {
- uint8_t_vec p = { prefix };
- p.reserve(65);
- for (auto i : x) p.emplace_back(i);
- for (auto i : y) p.emplace_back(i);
- return p;
- }
- auto _signatureFromRS (const uint8_t_32 r, const uint8_t_32 s) {
- uint8_t_64 sig;
- std::copy(r.begin(), r.end(), sig.begin());
- std::copy(s.begin(), s.end(), sig.begin() + 32);
- return sig;
- }
- auto _eccSign (const uint8_t_32 d, const uint8_t_32 message, bool& ok) {
- uint8_t_64 output;
- secp256k1_ecdsa_signature signature;
- ok &= secp256k1_ecdsa_sign(ctx, &signature, message.data(), d.data(), nullptr, nullptr);
- ok &= secp256k1_ecdsa_signature_serialize_compact(ctx, output.data(), &signature);
- return output;
- }
- template <typename A>
- auto _eccVerify (const A& p, const uint8_t_32 message, const uint8_t_64 signature) {
- secp256k1_pubkey public_key;
- bool ok = true;
- ok &= secp256k1_ec_pubkey_parse(ctx, &public_key, p.data(), p.size());
- if (!ok) return false;
- secp256k1_ecdsa_signature _signature;
- ok &= secp256k1_ecdsa_signature_parse_compact(ctx, &_signature, signature.data());
- if (!ok) return false;
- ok &= secp256k1_ecdsa_verify(ctx, &_signature, message.data(), &public_key);
- return ok;
- }
- template <typename A>
- auto sha256 (const A& m) {
- uint8_t_32 h;
- SHA256_CTX hctx;
- SHA256_Init(&hctx);
- SHA256_Update(&hctx, m.data(), m.size());
- SHA256_Final(h.data(), &hctx);
- return h;
- }
- // we use 0xfefefefefefefe.... as a null placeholder
- template <typename A>
- auto Null () {
- A a;
- a.fill(0xfe);
- return a;
- }
- template <typename A>
- auto isNull (const A& a) {
- for (auto x : a) if (x != 0xfe) return false;
- return true;
- }
- const auto ZERO = scalarFromHex("0000000000000000000000000000000000000000000000000000000000000000");
- const auto ONE = scalarFromHex("0000000000000000000000000000000000000000000000000000000000000001");
- const auto TWO = scalarFromHex("0000000000000000000000000000000000000000000000000000000000000002");
- const auto THREE = scalarFromHex("0000000000000000000000000000000000000000000000000000000000000003");
- const auto FOUR = scalarFromHex("0000000000000000000000000000000000000000000000000000000000000004");
- const auto GROUP_ORDER = scalarFromHex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
- const auto GROUP_ORDER_LESS_3 = scalarFromHex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e");
- const auto GROUP_ORDER_LESS_2 = scalarFromHex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f");
- const auto GROUP_ORDER_LESS_1 = scalarFromHex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140");
- const auto GROUP_ORDER_OVER_1 = scalarFromHex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142");
- const auto UINT256_MAX = scalarFromHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
- const auto G = pointFromHex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798");
- const auto GU = pointFromHex("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8");
- const auto P_LESS_1 = scalarFromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2e");
- const auto P_LESS_2 = scalarFromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeeffffc2d");
- const auto P = scalarFromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f");
- const auto P_OVER_1 = scalarFromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30");
- template <typename A> struct B { A a; std::string desc = ""; };
- const std::vector<B<uint8_t_32>> BAD_PRIVATES = {
- { ZERO, "Private key == 0" }, // #L3145, #L3684, fail, == 0
- { GROUP_ORDER, "Private key >= G" }, // #L3115, #L3670, fail, == G
- { GROUP_ORDER_OVER_1, "Private key >= G" }, // #L3162, #L3701, fail, >= G
- { UINT256_MAX, "Private key >= G" } // #L3131, #L3676, fail, > G
- };
- // excludes exact complement of a key, assumed to be tested elsewhere
- const std::vector<B<uint8_t_32>> BAD_TWEAKS = {
- { GROUP_ORDER, "Tweak >= G" },
- { GROUP_ORDER_OVER_1, "Tweak >= G" },
- { UINT256_MAX, "Tweak >= G" }
- };
- const std::vector<B<uint8_t_64>> BAD_SIGNATURES_VERIFY = {
- { _signatureFromRS(ZERO, ZERO), "Invalid r, s values (== 0)" },
- { _signatureFromRS(ZERO, ONE), "Invalid r value (== 0)" },
- { _signatureFromRS(ONE, ZERO), "Invalid s value (== 0)" },
- };
- const std::vector<B<uint8_t_64>> BAD_SIGNATURES = {
- { _signatureFromRS(GROUP_ORDER, ONE), "Invalid r value (>= n)" },
- { _signatureFromRS(ONE, GROUP_ORDER), "Invalid s value (>= n)" }
- };
- // from https://github.com/cryptocoinjs/ecurve/blob/14d72f5f468d53ff33dc13c1c7af350a41d52aab/test/fixtures/point.json#L84
- template <typename A = uint8_t_33>
- std::vector<B<uint8_t_vec>> generateBadPoints () {
- return {
- { _pointFromX(ONE, 0x01), "Bad sequence prefix" },
- { _pointFromX(ONE, 0x04), "Bad sequence prefix" },
- { _pointFromX(ONE, 0x05), "Bad sequence prefix" },
- { _pointFromX(ZERO, 0x02), "Bad X coordinate (== 0)" },
- { _pointFromX(ZERO, 0x03), "Bad X coordinate (== 0)" },
- { _pointFromX(P, 0x02), "Bad X coordinate (== P)" },
- { _pointFromX(P, 0x03), "Bad X coordinate (== P)" },
- { _pointFromX(P_LESS_2, 0x02), "Bad X coordinate (P - 2)" },
- { _pointFromX(P_LESS_2, 0x03), "Bad X coordinate (P - 2)" },
- { _pointFromX(P_OVER_1, 0x03), "Bad X coordinate (> P)" },
- };
- }
- template <>
- std::vector<B<uint8_t_vec>> generateBadPoints<uint8_t_65> () {
- return {
- { _pointFromXY(ONE, ONE, 0x01), "Bad sequence prefix" },
- { _pointFromXY(ONE, ONE, 0x02), "Bad sequence prefix" },
- { _pointFromXY(ONE, ONE, 0x03), "Bad sequence prefix" },
- { _pointFromXY(ONE, ONE, 0x05), "Bad sequence prefix" },
- { _pointFromXY(ZERO, ONE), "Bad X coordinate (== 0)" },
- { _pointFromXY(ONE, ZERO), "Bad Y coordinate (== 0)" },
- { _pointFromXY(ZERO, ZERO, 0x04), "Bad X/Y coordinate (== 0)" },
- { _pointFromXY(P, ONE), "Bad X coordinate (== P)" },
- { _pointFromXY(ONE, P), "Bad Y coordinate (== P)" },
- { _pointFromXY(P_OVER_1, ONE), "Bad X coordinate (> P)" },
- { _pointFromXY(ONE, P_OVER_1), "Bad Y coordinate (> P)" },
- };
- }
- const auto THROW_BAD_PRIVATE = "Expected Private";
- const auto THROW_BAD_POINT = "Expected Point";
- const auto THROW_BAD_TWEAK = "Expected Tweak";
- const auto THROW_BAD_HASH = "Expected Hash";
- const auto THROW_BAD_SIGNATURE = "Expected Signature";
|