init
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
all: json-privates json-points json-ecdsa
|
||||
|
||||
privates: privates.cpp shared.hpp
|
||||
g++ $< -L../native/secp256k1/.libs/ -lgmp -lsecp256k1 -o $@
|
||||
|
||||
points: points.cpp shared.hpp
|
||||
g++ $< -L../native/secp256k1/.libs/ -lgmp -lsecp256k1 -o $@
|
||||
|
||||
ecdsa: ecdsa.cpp shared.hpp
|
||||
g++ $< -L../native/secp256k1/.libs/ -lgmp -lcrypto -lssl -lsecp256k1 -o $@
|
||||
|
||||
clean:
|
||||
rm privates points ecdsa
|
||||
|
||||
json-points: points
|
||||
./points | jq . > ../tests/fixtures/points.json
|
||||
|
||||
json-privates: privates
|
||||
./privates | jq . > ../tests/fixtures/privates.json
|
||||
|
||||
json-ecdsa: ecdsa
|
||||
./ecdsa | jq . > ../tests/fixtures/ecdsa.json
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env sh
|
||||
# from https://github.com/cryptocoinjs/secp256k1-node/blob/25a4b6cb567b49a40f47f50c5ca9a756f5343e4d/utils/has_lib.sh
|
||||
|
||||
check () {
|
||||
regex="lib$1.+(so|dylib)"
|
||||
|
||||
# Add /sbin to path as ldconfig is located there on some systems - e.g. Debian
|
||||
# (and it still can be used by unprivileged users):
|
||||
PATH="$PATH:/sbin"
|
||||
export PATH
|
||||
|
||||
# Try just checking common library locations
|
||||
for dir in /lib /usr/lib /usr/local/lib /opt/local/lib /usr/lib/x86_64-linux-gnu /usr/lib/i386-linux-gnu; do
|
||||
if test -d $dir; then
|
||||
# shellcheck disable=SC2010
|
||||
ls $dir | grep -E "$regex" && return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
check "$1" > /dev/null
|
||||
if test "$?" -eq 0; then
|
||||
echo true
|
||||
else
|
||||
echo false
|
||||
fi
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "shared.hpp"
|
||||
|
||||
/////////// bitcoinjs-lib/ecdsa test fixtures
|
||||
// https://github.com/bitcoinjs/bitcoinjs-lib/blob/6b3c41a06c6e38ec79dc2f3389fa2362559b4a46/test/fixtures/ecdsa.json
|
||||
const auto BJS_KEYS = std::vector<std::string>({
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",
|
||||
"00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",
|
||||
"000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",
|
||||
});
|
||||
const auto BJS_MESSAGES = std::vector<std::string>({
|
||||
"Everything should be made as simple as possible, but not simpler.",
|
||||
"Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
|
||||
"Not only is the Universe stranger than we think, it is stranger than we can think.",
|
||||
"How wonderful that we have met with a paradox. Now we have some hope of making progress.",
|
||||
"Computer science is no more about computers than astronomy is about telescopes.",
|
||||
"...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",
|
||||
"The question of whether computers can think is like the question of whether submarines can swim.",
|
||||
});
|
||||
|
||||
struct S { uint8_t_32 d; uint8_t_32 m; uint8_t_64 e; std::string desc; };
|
||||
auto generateSigns () {
|
||||
bool ok = true;
|
||||
std::vector<S> s;
|
||||
|
||||
size_t i = 0;
|
||||
for (const auto& message : BJS_MESSAGES) {
|
||||
const auto d = scalarFromHex(BJS_KEYS[i++]);
|
||||
const auto hash = sha256(message);
|
||||
const auto sig = _eccSign(d, hash, ok);
|
||||
s.push_back({ d, hash, sig, message });
|
||||
}
|
||||
|
||||
for (const auto& message : BJS_MESSAGES) {
|
||||
const auto d = randomPrivate();
|
||||
const auto hash = sha256(message);
|
||||
const auto sig = _eccSign(d, hash, ok);
|
||||
s.push_back({ d, hash, sig, message });
|
||||
}
|
||||
|
||||
s.push_back({ ONE, ZERO, _eccSign(ONE, ZERO, ok), "Strange hash" });
|
||||
s.push_back({ ONE, UINT256_MAX, _eccSign(ONE, UINT256_MAX, ok), "Strange hash" });
|
||||
s.push_back({ GROUP_ORDER_LESS_1, ZERO, _eccSign(GROUP_ORDER_LESS_1, ZERO, ok), "Stange hash" });
|
||||
s.push_back({ GROUP_ORDER_LESS_1, UINT256_MAX, _eccSign(GROUP_ORDER_LESS_1, UINT256_MAX, ok), "Strange hash" });
|
||||
|
||||
// fuzz
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
const auto rkey = randomPrivate();
|
||||
const auto hash = randomScalar();
|
||||
auto sig = _eccSign(rkey, hash, ok);
|
||||
const auto Q = _pointFromScalar<uint8_t_33>(rkey, ok);
|
||||
assert(ok);
|
||||
auto verified = ok;
|
||||
assert(_eccVerify(Q, hash, sig) == verified);
|
||||
|
||||
s.push_back({ rkey, hash, sig, "" });
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct BS { uint8_t_32 d; uint8_t_32 m; std::string except; std::string desc = ""; };
|
||||
auto generateBadSigns () {
|
||||
std::vector<BS> bs;
|
||||
for (auto x : BAD_PRIVATES) bs.push_back({ x.a, ONE, THROW_BAD_PRIVATE, x.desc });
|
||||
return bs;
|
||||
}
|
||||
|
||||
struct BV { uint8_t_vec Q; uint8_t_32 m; uint8_t_64 s; std::string except; std::string desc = ""; };
|
||||
auto generateBadVerify () {
|
||||
bool ok = true;
|
||||
const auto G_ONE = _pointFromUInt32<uint8_t_33>(1, ok);
|
||||
assert(ok);
|
||||
const auto BAD_POINTS = generateBadPoints<uint8_t_65>();
|
||||
const auto BAD_POINTS_C = generateBadPoints<uint8_t_33>();
|
||||
|
||||
std::vector<BV> bv;
|
||||
for (auto x : BAD_POINTS) bv.push_back({ x.a, THREE, _signatureFromRS(ONE, ONE), THROW_BAD_POINT, x.desc });
|
||||
for (auto x : BAD_POINTS_C) bv.push_back({ x.a, THREE, _signatureFromRS(ONE, ONE), THROW_BAD_POINT, x.desc });
|
||||
|
||||
for (auto x : BAD_SIGNATURES_VERIFY) bv.push_back({ G_ONE, THREE, x.a, "", x.desc }); // never verify, but dont throw
|
||||
for (auto x : BAD_SIGNATURES) bv.push_back({ G_ONE, THREE, x.a, THROW_BAD_SIGNATURE, x.desc });
|
||||
return bv;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void dumpJSON (std::ostream& o, const T& t) {
|
||||
o << jsonifyO({
|
||||
jsonp("valid", jsonifyA(std::get<0>(t), [&](auto x) {
|
||||
return jsonifyO({
|
||||
x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
|
||||
jsonp("d", jsonify(x.d)),
|
||||
jsonp("m", jsonify(x.m)),
|
||||
jsonp("signature", jsonify(x.e))
|
||||
});
|
||||
})),
|
||||
jsonp("invalid", jsonifyO({
|
||||
jsonp("sign", jsonifyA(std::get<1>(t), [&](auto x) {
|
||||
return jsonifyO({
|
||||
x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
|
||||
jsonp("exception", jsonify(x.except)),
|
||||
jsonp("d", jsonify(x.d)),
|
||||
jsonp("m", jsonify(x.m))
|
||||
});
|
||||
})),
|
||||
jsonp("verify", jsonifyA(std::get<2>(t), [&](auto x) {
|
||||
return jsonifyO({
|
||||
x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
|
||||
x.except.empty() ? "" : jsonp("exception", jsonify(x.except)),
|
||||
jsonp("Q", jsonify(x.Q)),
|
||||
jsonp("m", jsonify(x.m)),
|
||||
jsonp("signature", jsonify(x.s))
|
||||
});
|
||||
}))
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
int main () {
|
||||
_ec_init();
|
||||
const auto s = generateSigns();
|
||||
const auto bs = generateBadSigns();
|
||||
const auto bv = generateBadVerify();
|
||||
|
||||
dumpJSON(std::cout, std::make_tuple(s, bs, bv));
|
||||
|
||||
return 0;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// from https://github.com/dcousens/hexxer/blob/47191b839fc4bbdc60dc267d9f9673640a50c161/hexxer.hpp
|
||||
#pragma once
|
||||
|
||||
namespace hexxer {
|
||||
static const char HEX_ALPHABET[] = "0123456789abcdef";
|
||||
static const int HEX_TABLE[] = {
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
0,1,2,3,4,5,6,7,8,9, // 0-9
|
||||
255,255,255,255,255,255,255,
|
||||
10,11,12,13,14,15, // a-f
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
|
||||
10,11,12,13,14,15, // A-F
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
|
||||
};
|
||||
|
||||
inline auto encodeFirst (const unsigned char x) {
|
||||
return HEX_ALPHABET[x >> 4];
|
||||
}
|
||||
|
||||
inline auto encodeSecond (const unsigned char x) {
|
||||
return HEX_ALPHABET[x & 0x0f];
|
||||
}
|
||||
|
||||
inline auto decode (const char a, const char b) {
|
||||
const auto ia = HEX_TABLE[static_cast<unsigned char>(a)];
|
||||
const auto ib = HEX_TABLE[static_cast<unsigned char>(b)];
|
||||
if (ia == 255) return 0x100;
|
||||
if (ib == 255) return 0x100;
|
||||
|
||||
return (ia << 4) + ib;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "hexxer.hpp"
|
||||
|
||||
template <typename R>
|
||||
auto hexify (const R& range) {
|
||||
std::stringstream ss;
|
||||
for (auto& x : range) {
|
||||
ss << hexxer::encodeFirst(x) << hexxer::encodeSecond(x);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto jsonify (const std::string& v) {
|
||||
return "\"" + v + "\"";
|
||||
}
|
||||
|
||||
template <typename R>
|
||||
std::enable_if_t<std::is_same<typename R::value_type, uint8_t>::value, std::string>
|
||||
jsonify (const R& r) {
|
||||
return jsonify(hexify<R>(r));
|
||||
}
|
||||
|
||||
auto jsonp (const std::string& k, const std::string& v) {
|
||||
std::stringstream ss;
|
||||
ss << "\"" << k << "\": " << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto jsonify (const bool v) {
|
||||
return v ? "true" : "false";
|
||||
}
|
||||
|
||||
template <char L = ' ', char R = ' ', typename Range, typename F>
|
||||
auto jsonify_csv (const Range r, F f) {
|
||||
std::stringstream ss;
|
||||
size_t i = 0;
|
||||
if (L != ' ') ss << L;
|
||||
for (const auto& x : r) {
|
||||
const auto fx = f(x);
|
||||
if (fx.empty()) continue;
|
||||
if (i++ > 0) ss << ',';
|
||||
ss << fx;
|
||||
}
|
||||
if (R != ' ') ss << R;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto json_identity (const std::string& s) { return s; }
|
||||
|
||||
template <typename R = std::initializer_list<std::string>, typename F = decltype(json_identity)>
|
||||
auto jsonifyO (const R& r, F f = json_identity) {
|
||||
return jsonify_csv<'{', '}'>(r, f);
|
||||
}
|
||||
|
||||
template <typename R = std::initializer_list<std::string>, typename F = decltype(json_identity)>
|
||||
auto jsonifyA (const R& r, F f = json_identity) {
|
||||
return jsonify_csv<'[', ']'>(r, f);
|
||||
}
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
#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;
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "shared.hpp"
|
||||
|
||||
struct IP { uint8_t_32 a = {}; bool e = false; std::string desc = ""; };
|
||||
struct PA { uint8_t_32 a; uint8_t_32 b; uint8_t_32 e = Null<uint8_t_32>(); std::string except = ""; std::string desc = ""; };
|
||||
|
||||
void generate (std::ostream& o) {
|
||||
///////////////////////////////// isPrivate
|
||||
|
||||
// edge cases (verify)
|
||||
// from https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c
|
||||
std::vector<IP> ip = {
|
||||
{ ZERO, false, "== 0" }, // #L3145
|
||||
{ ONE, true, "== 1" }, // #L3153
|
||||
{ GROUP_ORDER_LESS_1, true, "== G - 1" }, // #L3171
|
||||
{ GROUP_ORDER, false, "== G" }, // #L3115
|
||||
{ GROUP_ORDER_OVER_1, false, "> G" }, // #L3162
|
||||
{ UINT256_MAX, false, "2^256 - 1" }, // #L3131
|
||||
};
|
||||
|
||||
// fuzz
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
ip.push_back({ randomPrivate(), true });
|
||||
}
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
const auto key = randomScalarHigh();
|
||||
const auto verified = secp256k1_ec_seckey_verify(ctx, key.data());
|
||||
|
||||
ip.push_back({ key, verified });
|
||||
}
|
||||
|
||||
///////////////////////////////// privateAdd
|
||||
std::vector<PA> pa;
|
||||
|
||||
// visually inspected
|
||||
// covers https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c
|
||||
pa.push_back({ ONE, ZERO, ONE, "", "1 + 0 == 1" });
|
||||
for (size_t i = 1; i < 5; ++i) pa.push_back({ ONE, scalarFromUInt32(i), scalarFromUInt32(1 + i) });
|
||||
for (size_t i = 1; i < 5; ++i) pa.push_back({ scalarFromUInt32(i), TWO, scalarFromUInt32(i + 2) });
|
||||
|
||||
pa.push_back({ ONE, GROUP_ORDER_LESS_1, Null<uint8_t_32>(), "", "1 + -1 == 0" });
|
||||
pa.push_back({ ONE, GROUP_ORDER_LESS_2, GROUP_ORDER_LESS_1, "", "1 + -2 == -1" });
|
||||
pa.push_back({ ONE, GROUP_ORDER_LESS_3, GROUP_ORDER_LESS_2, "", "1 + -3 == -2" });
|
||||
pa.push_back({ GROUP_ORDER_LESS_1, GROUP_ORDER_LESS_1, GROUP_ORDER_LESS_2 });
|
||||
pa.push_back({ GROUP_ORDER_LESS_2, GROUP_ORDER_LESS_1, GROUP_ORDER_LESS_3 });
|
||||
pa.push_back({ GROUP_ORDER_LESS_3, ONE, GROUP_ORDER_LESS_2 });
|
||||
pa.push_back({ GROUP_ORDER_LESS_3, TWO, GROUP_ORDER_LESS_1, "", "-3 + 2 == -1" });
|
||||
pa.push_back({ GROUP_ORDER_LESS_3, THREE, Null<uint8_t_32>(), "", "-3 + 3 == 0" });
|
||||
|
||||
// fuzz
|
||||
for (size_t i = 0; i < 10000; ++i) {
|
||||
const auto paPush = [&](const auto k, const auto t) {
|
||||
bool ok = true;
|
||||
const auto expected = _privAdd(k, t, ok);
|
||||
if (ok) pa.push_back({ k, t, expected });
|
||||
else pa.push_back({ k, t, Null<uint8_t_32>() });
|
||||
};
|
||||
|
||||
paPush(randomPrivate(), randomPrivate());
|
||||
paPush(randomPrivateHigh(), randomPrivateLow());
|
||||
paPush(randomPrivateLow(), randomPrivateHigh());
|
||||
}
|
||||
|
||||
std::vector<PA> paf;
|
||||
for (const auto x : BAD_PRIVATES) paf.push_back({ x.a, ONE, {}, THROW_BAD_PRIVATE, x.desc });
|
||||
for (const auto x : BAD_TWEAKS) paf.push_back({ ONE, x.a, {}, THROW_BAD_TWEAK, x.desc });
|
||||
|
||||
///////////////////////////////// privateSub
|
||||
std::vector<PA> ps;
|
||||
|
||||
// visually inspected
|
||||
// covers https://github.com/bitcoin-core/secp256k1/blob/6ad5cdb42a1a8257289a0423d644dcbdeab0f83c/src/tests.c
|
||||
ps.push_back({ ONE, ZERO, ONE, "", "1 - 0 == 1" });
|
||||
for (size_t i = 2; i < 7; ++i) ps.push_back({ scalarFromUInt32(i), ONE, scalarFromUInt32(i - 1) });
|
||||
for (size_t i = 1; i < 10; ++i) ps.push_back({ scalarFromUInt32(10), scalarFromUInt32(i), scalarFromUInt32(10 - i) });
|
||||
|
||||
ps.push_back({ ONE, ONE, Null<uint8_t_32>(), "", "1 - 1 == 0" });
|
||||
ps.push_back({ THREE, THREE, Null<uint8_t_32>(), "", "3 - 3 == 0" });
|
||||
ps.push_back({ GROUP_ORDER_LESS_1, ONE, GROUP_ORDER_LESS_2 });
|
||||
ps.push_back({ GROUP_ORDER_LESS_2, ONE, GROUP_ORDER_LESS_3 });
|
||||
ps.push_back({ GROUP_ORDER_LESS_1, GROUP_ORDER_LESS_2, ONE });
|
||||
ps.push_back({ GROUP_ORDER_LESS_2, GROUP_ORDER_LESS_3, ONE });
|
||||
|
||||
// fuzz
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
const auto psPush = [&](const auto k, const auto t) {
|
||||
bool ok = true;
|
||||
const auto expected = _privSub(k, t, ok);
|
||||
if (ok) ps.push_back({ k, t, expected });
|
||||
else ps.push_back({ k, t, Null<uint8_t_32>() });
|
||||
};
|
||||
|
||||
psPush(randomPrivate(), randomPrivate());
|
||||
psPush(randomPrivateHigh(), randomPrivateLow());
|
||||
psPush(randomPrivateLow(), randomPrivateHigh());
|
||||
}
|
||||
|
||||
std::vector<PA> psf;
|
||||
for (const auto x : BAD_PRIVATES) psf.push_back({ x.a, ONE, {}, THROW_BAD_PRIVATE, x.desc });
|
||||
for (const auto x : BAD_TWEAKS) psf.push_back({ ONE, x.a, {}, THROW_BAD_TWEAK, x.desc });
|
||||
|
||||
// dump JSON
|
||||
const auto jPA = [](auto x) {
|
||||
return jsonifyO({
|
||||
x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
|
||||
jsonp("d", jsonify(x.a)),
|
||||
jsonp("tweak", jsonify(x.b)),
|
||||
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("isPrivate", jsonifyA(ip, [](auto x) {
|
||||
return jsonifyO({
|
||||
x.desc.empty() ? "" : jsonp("description", jsonify(x.desc)),
|
||||
jsonp("d", jsonify(x.a)),
|
||||
jsonp("expected", jsonify(x.e))
|
||||
});
|
||||
})),
|
||||
jsonp("privateAdd", jsonifyA(pa, jPA)),
|
||||
jsonp("privateSub", jsonifyA(ps, jPA))
|
||||
})),
|
||||
jsonp("invalid", jsonifyO({
|
||||
jsonp("privateAdd", jsonifyA(paf, jPA)),
|
||||
jsonp("privateSub", jsonifyA(psf, jPA))
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
int main () {
|
||||
_ec_init();
|
||||
generate(std::cout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
#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";
|
||||
Reference in New Issue
Block a user