From 25a5361777b6dca1fc386c30bcacc1e4112dfab2 Mon Sep 17 00:00:00 2001 From: SChernykh <15806605+SChernykh@users.noreply.github.com> Date: Thu, 23 Oct 2025 11:40:59 +0200 Subject: [PATCH] Wallet: added checks for FCMP++ compatibility --- CMakeLists.txt | 1 + external/src/cryptonote/crypto-ops-data.c | 14 +- external/src/cryptonote/crypto-ops.c | 309 +++++++++++++--- external/src/cryptonote/crypto-ops.h | 39 +- external/src/cryptonote/fcmp_pp_crypto.cpp | 251 +++++++++++++ external/src/cryptonote/fcmp_pp_crypto.h | 36 ++ src/params.cpp | 34 +- src/side_chain.cpp | 4 +- src/wallet.cpp | 32 +- src/wallet.h | 23 +- tests/CMakeLists.txt | 1 + tests/src/crypto_tests.cpp | 29 +- tests/src/crypto_tests.txt | 401 +++++++++++++++++++++ 13 files changed, 1100 insertions(+), 74 deletions(-) create mode 100644 external/src/cryptonote/fcmp_pp_crypto.cpp create mode 100644 external/src/cryptonote/fcmp_pp_crypto.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f651dbc..3088734 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ set(SOURCES external/src/crypto/sha256.c external/src/cryptonote/crypto-ops-data.c external/src/cryptonote/crypto-ops.c + external/src/cryptonote/fcmp_pp_crypto.cpp external/src/hardforks/hardforks.cpp src/block_cache.cpp src/block_template.cpp diff --git a/external/src/cryptonote/crypto-ops-data.c b/external/src/cryptonote/crypto-ops-data.c index 45bc23e..8da7c38 100644 --- a/external/src/cryptonote/crypto-ops-data.c +++ b/external/src/cryptonote/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2020, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -42,6 +42,13 @@ const fe fe_d = {-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -627 const fe fe_sqrtm1 = {-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482}; /* sqrt(-1) */ const fe fe_d2 = {-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; /* 2 * d */ +/* a = -1 */ +// TODO: double check these consts +const fe fe_a_sub_d = {10913609, -13857413, 15372611, -6949391, -114729, 8787816, 6275908, 3247719, 18696448, 12055116}; /* a - d */ +const fe fe_a0 = {-21827241, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; /* A0 = 2 * (a + d) */ +const fe fe_ap = {-23454401, 11679213, -5618422, 5756869, -458917, 1596832, 25103633, 12990876, 7676928, 14666033}; /* Ap = -2 * A0 */ +const fe fe_msqrt2b = {-1359796, -3165658, 8463188, -8916281, -9242332, 8801166, -2887120, 14417306, 28934311, 6371549}; + /* base[i][j] = (j+1)*256^i*B */ const ge_precomp ge_base[32][8] = { { @@ -874,6 +881,11 @@ const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -3210 const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */ const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ +const fe fe_a_inv_3 = {-22207407, 11184811, 22369621, -11184811, -22369621, 11184811, 22369621, -11184811, -22369621, 11184811}; /* A / 3*/ +const fe fe_c = {-12222970, -8312128, -11511410, 9067497, -15300785, -241793, 25456130, 14121551, -12187136, 3972024}; /* sqrt(-(A + 2))*/ +const fe fe_one = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +const fe fe_m1 = {-1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +const fe fe_inv2 = {10, 0, 0, 0, 0, 0, 0, 0, 0, -16777216}; /* 1 / 2 */ const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} }; const ge_p3 ge_p3_H = { {7329926, -15101362, 31411471, 7614783, 27996851, -3197071, -11157635, -6878293, 466949, -7986503}, diff --git a/external/src/cryptonote/crypto-ops.c b/external/src/cryptonote/crypto-ops.c index d9667ae..4d7c51e 100644 --- a/external/src/cryptonote/crypto-ops.c +++ b/external/src/cryptonote/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2020, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -34,13 +34,13 @@ #include #include +#include +#include #include "crypto-ops.h" /* Predeclarations */ -static void fe_mul(fe, const fe, const fe); -static void fe_sq(fe, const fe); static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *); static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *); static void ge_p2_0(ge_p2 *); @@ -73,7 +73,7 @@ uint64_t load_4(const unsigned char *in) h = 0 */ -static void fe_0(fe h) { +void fe_0(fe h) { h[0] = 0; h[1] = 0; h[2] = 0; @@ -92,7 +92,7 @@ static void fe_0(fe h) { h = 1 */ -static void fe_1(fe h) { +void fe_1(fe h) { h[0] = 1; h[1] = 0; h[2] = 0; @@ -232,7 +232,7 @@ static void fe_cmov(fe f, const fe g, unsigned int b) { h = f */ -static void fe_copy(fe h, const fe f) { +void fe_copy(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; @@ -315,6 +315,39 @@ void fe_invert(fe out, const fe z) { return; } +// Montgomery's trick +// https://iacr.org/archive/pkc2004/29470042/29470042.pdf 2.2 +int fe_batch_invert(fe *out, const fe *in, const int n) { + if (n == 0) { + return 0; + } + + // Step 1: collect initial muls + fe *init_muls = (fe *) malloc(n * sizeof(fe)); + if (!init_muls) { + return 1; + } + memcpy(&init_muls[0], &in[0], sizeof(fe)); + for (int i = 1; i < n; ++i) { + fe_mul(init_muls[i], init_muls[i-1], in[i]); + } + + // Step 2: get the inverse of all elems multiplied together + fe a; + fe_invert(a, init_muls[n-1]); + + // Step 3: get each inverse + for (int i = n; i > 1; --i) { + fe_mul(out[i-1], a, init_muls[i-2]); + fe_mul(a, a, in[i-1]); + } + memcpy(&out[0], &a, sizeof(fe)); + + free(init_muls); + + return 0; +} + /* From fe_isnegative.c */ /* @@ -325,7 +358,7 @@ Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ -static int fe_isnegative(const fe f) { +int fe_isnegative(const fe f) { unsigned char s[32]; fe_tobytes(s, f); return s[0] & 1; @@ -376,7 +409,7 @@ Can get away with 11 carries, but then data flow is much deeper. With tighter constraints on inputs can squeeze carries into int32. */ -static void fe_mul(fe h, const fe f, const fe g) { +void fe_mul(fe h, const fe f, const fe g) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; @@ -606,7 +639,7 @@ Postconditions: |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */ -static void fe_neg(fe h, const fe f) { +void fe_neg(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; @@ -656,7 +689,7 @@ Postconditions: See fe_mul.c for discussion of implementation strategy. */ -static void fe_sq(fe h, const fe f) { +void fe_sq(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; @@ -960,7 +993,7 @@ Postconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ -static void fe_sub(fe h, const fe f, const fe g) { +void fe_sub(fe h, const fe f, const fe g) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; @@ -1330,16 +1363,9 @@ void ge_double_scalarmult_base_vartime_p3(ge_p3 *r3, const unsigned char *a, con } } -/* From ge_frombytes.c, modified */ - -int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) { - fe u; - fe v; - fe vxx; - fe check; - - /* From fe_frombytes.c */ +/* From fe_frombytes.c */ +int fe_frombytes_vartime(fe y, const unsigned char *s) { int64_t h0 = load_4(s); int64_t h1 = load_3(s + 4) << 6; int64_t h2 = load_3(s + 7) << 5; @@ -1380,18 +1406,31 @@ int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) { carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; - h->Y[0] = h0; - h->Y[1] = h1; - h->Y[2] = h2; - h->Y[3] = h3; - h->Y[4] = h4; - h->Y[5] = h5; - h->Y[6] = h6; - h->Y[7] = h7; - h->Y[8] = h8; - h->Y[9] = h9; + y[0] = h0; + y[1] = h1; + y[2] = h2; + y[3] = h3; + y[4] = h4; + y[5] = h5; + y[6] = h6; + y[7] = h7; + y[8] = h8; + y[9] = h9; - /* End fe_frombytes.c */ + return 0; +} + +/* From ge_frombytes.c, modified */ + +int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) { + fe u; + fe v; + fe vxx; + fe check; + + if (fe_frombytes_vartime(h->Y, s) != 0) { + return -1; + } fe_1(h->Z); fe_sq(u, h->Y); @@ -1608,7 +1647,7 @@ static void ge_precomp_cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) fe_cmov(t->xy2d, u->xy2d, b); } -static void select(ge_precomp *t, int pos, signed char b) { +static void _select(ge_precomp *t, int pos, signed char b) { ge_precomp minust; unsigned char bnegative = negative(b); unsigned char babs = b - (((-bnegative) & b) << 1); @@ -1690,7 +1729,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { ge_p3_0(h); for (i = 1; i < 64; i += 2) { - select(&t, i / 2, e[i]); + _select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } @@ -1700,7 +1739,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r); for (i = 0; i < 64; i += 2) { - select(&t, i / 2, e[i]); + _select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } } @@ -2023,26 +2062,18 @@ void sc_reduce(unsigned char *s) { s[31] = s11 >> 17; } -/* New code */ +/* From fe_pow22523.c */ -static void fe_divpowm1(fe r, const fe u, const fe v) { - fe v3, uv7, t0, t1, t2; +void fe_pow22523(fe out, const fe z) { + fe t0; + fe t1; + fe t2; int i; - fe_sq(v3, v); - fe_mul(v3, v3, v); /* v3 = v^3 */ - fe_sq(uv7, v3); - fe_mul(uv7, uv7, v); - fe_mul(uv7, uv7, u); /* uv7 = uv^7 */ - - /*fe_pow22523(uv7, uv7);*/ - - /* From fe_pow22523.c */ - - fe_sq(t0, uv7); + fe_sq(t0, z); fe_sq(t1, t0); fe_sq(t1, t1); - fe_mul(t1, uv7, t1); + fe_mul(t1, z, t1); fe_mul(t0, t0, t1); fe_sq(t0, t0); fe_mul(t0, t1, t0); @@ -2081,12 +2112,24 @@ static void fe_divpowm1(fe r, const fe u, const fe v) { fe_mul(t0, t1, t0); fe_sq(t0, t0); fe_sq(t0, t0); - fe_mul(t0, t0, uv7); + fe_mul(out, t0, z); +} - /* End fe_pow22523.c */ - /* t0 = (uv^7)^((q-5)/8) */ - fe_mul(t0, t0, v3); - fe_mul(r, t0, u); /* u^(m+1)v^(-(m+1)) */ +/* New code */ + +static void fe_divpowm1(fe r, const fe u, const fe v) { + fe v3, uv7; + + fe_sq(v3, v); + fe_mul(v3, v3, v); /* v3 = v^3 */ + fe_sq(uv7, v3); + fe_mul(uv7, uv7, v); + fe_mul(uv7, uv7, u); /* uv7 = uv^7 */ + + fe_pow22523(r, uv7); /* (uv^7)^((q-5)/8) */ + + fe_mul(r, r, v3); + fe_mul(r, r, u); /* u^(m+1)v^(-(m+1)) */ } static void ge_cached_0(ge_cached *r) { @@ -2536,6 +2579,14 @@ void sc_0(unsigned char *s) { } } +void sc_1(unsigned char *s) { + int i; + s[0] = 1; + for (i = 1; i < 32; i++) { + s[i] = 0; + } +} + void sc_reduce32(unsigned char *s) { int64_t s0 = 2097151 & load_3(s); int64_t s1 = 2097151 & (load_4(s + 2) >> 5); @@ -3936,6 +3987,92 @@ int sc_isnonzero(const unsigned char *s) { s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; } +static void edwardsYZ_to_x25519(unsigned char *xbytes, const fe Y, const fe Z) { + // y = Y/Z + // x_mont = (1 + y) / (1 - y) + // = (1 + Y/Z) / (1 - Y/Z) + // = (Z + Y) / (Z - Y) + + fe tmp0; + fe tmp1; + fe_add(tmp0, Z, Y); // Z + Y + fe_sub(tmp1, Z, Y); // Z - Y + fe_invert(tmp1, tmp1); // 1/(Z - Y) + fe_mul(tmp0, tmp0, tmp1); // (Z + Y) / (Z - Y) + fe_tobytes(xbytes, tmp0); // tobytes((Z + Y) / (Z - Y)) +} + +void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h) +{ + edwardsYZ_to_x25519(xbytes, h->Y, h->Z); +} + +int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s) +{ + /* From fe_frombytes.c */ + + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + /* Validate the number to be canonical */ + if (h9 == 33554428 && h8 == 268435440 && h7 == 536870880 && h6 == 2147483520 && + h5 == 4294967295 && h4 == 67108860 && h3 == 134217720 && h2 == 536870880 && + h1 == 1073741760 && h0 >= 4294967277) { + return -1; + } + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + fe Y; + Y[0] = h0; + Y[1] = h1; + Y[2] = h2; + Y[3] = h3; + Y[4] = h4; + Y[5] = h5; + Y[6] = h6; + Y[7] = h7; + Y[8] = h8; + Y[9] = h9; + + /* End fe_frombytes.c */ + + fe Z; + fe_1(Z); + + edwardsYZ_to_x25519(xbytes, Y, Z); + + return 0; +} + int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) { // https://eprint.iacr.org/2008/522 // X == T == 0 and Y/Z == 1 @@ -3984,3 +4121,67 @@ int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) { // Y/Z = 0/0 return 0; } + +// https://www.ietf.org/archive/id/draft-ietf-lwig-curve-representations-02.pdf E.2 +static void fe_ed_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y) +{ + // (1/(1-y))*(1+y) + fe inv_one_minus_y_mul_one_plus_y; + fe_mul(inv_one_minus_y_mul_one_plus_y, inv_one_minus_y, one_plus_y); + + // wei x = (1/(1-y))*(1+y) + (A/3) + fe wei_x_fe; + fe_add(wei_x_fe, inv_one_minus_y_mul_one_plus_y, fe_a_inv_3); + fe_tobytes(wei_x, wei_x_fe); +} + +// https://www.ietf.org/archive/id/draft-ietf-lwig-curve-representations-02.pdf E.2 +void fe_ed_derivatives_to_wei_x_y(unsigned char *wei_x, unsigned char *wei_y, const fe inv_one_minus_y, const fe one_plus_y, const fe inv_one_minus_y_mul_x) +{ + fe_ed_derivatives_to_wei_x(wei_x, inv_one_minus_y, one_plus_y); + + // c*(1+y) + fe fe_c_mul_one_plus_y; + fe_mul(fe_c_mul_one_plus_y, fe_c, one_plus_y); + + // wei y = c * (1+y) * (1/((1-y)*x)) + fe wei_y_fe; + fe_mul(wei_y_fe, fe_c_mul_one_plus_y, inv_one_minus_y_mul_x); + fe_tobytes(wei_y, wei_y_fe); +} + +/* +Since fe_add and fe_sub enforce the following conditions: + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +We sometimes need to "reduce" field elems when they are in the poscondition's +larger domain to match the precondition domain. This way we can take the output +of fe_add or fe_sub and use it as input to another call to fe_add or fe_sub. + +We reduce by converting the field elem to its byte repr, then re-deriving the +field elem from the byte repr. +*/ +void fe_reduce(fe reduced_f, const fe f) +{ + unsigned char f_bytes[32]; + fe_tobytes(f_bytes, f); + fe_frombytes_vartime(reduced_f, f_bytes); +} + +void fe_dbl(fe h, const fe f) +{ + // Reduce the input for safety to ensure we meet the preconditions for fe_add + fe f_reduced; + fe_reduce(f_reduced, f); + fe h_res; + fe_add(h_res, f_reduced, f_reduced); + // Reduce the output for safety to ensure the result can be used as input to + // fe_add or fe_sub without an extra call to fe_reduce + fe_reduce(h, h_res); +} diff --git a/external/src/cryptonote/crypto-ops.h b/external/src/cryptonote/crypto-ops.h index 8c433b1..c3fe99c 100644 --- a/external/src/cryptonote/crypto-ops.h +++ b/external/src/cryptonote/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2020, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -30,6 +30,8 @@ #pragma once +#include + /* From fe.h */ typedef int32_t fe[10]; @@ -86,6 +88,7 @@ void ge_double_scalarmult_base_vartime_p3(ge_p3 *, const unsigned char *, const extern const fe fe_sqrtm1; extern const fe fe_d; +int fe_frombytes_vartime(fe, const unsigned char *); int ge_frombytes_vartime(ge_p3 *, const unsigned char *); /* From ge_p1p1_to_p2.c */ @@ -128,6 +131,10 @@ void ge_tobytes(unsigned char *, const ge_p2 *); void sc_reduce(unsigned char *); +/* From fe_pow22523.c */ + +void fe_pow22523(fe, const fe); + /* New code */ void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *); @@ -138,16 +145,26 @@ void ge_triple_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp); void ge_mul8(ge_p1p1 *, const ge_p2 *); +extern const fe fe_a_sub_d; +extern const fe fe_a0; +extern const fe fe_ap; +extern const fe fe_msqrt2b; extern const fe fe_ma2; extern const fe fe_ma; extern const fe fe_fffb1; extern const fe fe_fffb2; extern const fe fe_fffb3; extern const fe fe_fffb4; +extern const fe fe_a_inv_3; +extern const fe fe_c; +extern const fe fe_one; +extern const fe fe_m1; +extern const fe fe_inv2; extern const ge_p3 ge_p3_identity; extern const ge_p3 ge_p3_H; void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *); void sc_0(unsigned char *); +void sc_1(unsigned char *); void sc_reduce32(unsigned char *); void sc_add(unsigned char *, const unsigned char *, const unsigned char *); void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); @@ -157,12 +174,32 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, int sc_check(const unsigned char *); int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ +/** + * brief: Convert Ed25519 y-coord to X25519 x-coord, AKA "ConvertPointE()" in the Carrot spec + */ +void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h); +int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s); + // internal uint64_t load_3(const unsigned char *in); uint64_t load_4(const unsigned char *in); void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); void fe_add(fe h, const fe f, const fe g); +void fe_neg(fe h, const fe f); void fe_tobytes(unsigned char *, const fe); +void fe_copy(fe h, const fe f); +int fe_isnegative(const fe f); void fe_invert(fe out, const fe z); +int fe_batch_invert(fe *out, const fe *in, const int n); +void fe_mul(fe out, const fe, const fe); +void fe_sq(fe h, const fe f); +void fe_sub(fe h, const fe f, const fe g); +void fe_0(fe h); +void fe_1(fe h); int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p); + +void fe_ed_derivatives_to_wei_x_y(unsigned char *wei_x, unsigned char *wei_y, const fe inv_one_minus_y, const fe one_plus_y, const fe inv_one_minus_y_mul_x); + +void fe_reduce(fe reduced_f, const fe f); +void fe_dbl(fe h, const fe f); diff --git a/external/src/cryptonote/fcmp_pp_crypto.cpp b/external/src/cryptonote/fcmp_pp_crypto.cpp new file mode 100644 index 0000000..4882d5f --- /dev/null +++ b/external/src/cryptonote/fcmp_pp_crypto.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2024, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifdef _MSC_VER +#pragma warning(disable: 4800) +#endif + +extern "C" { +#include "crypto-ops.h" +} + +#include "fcmp_pp_crypto.h" +#include + +static bool fe_compare(const fe a, const fe b) +{ + unsigned char a_bytes[32]; + unsigned char b_bytes[32]; + + fe_tobytes(a_bytes, a); + fe_tobytes(b_bytes, b); + + return memcmp(a_bytes, b_bytes, sizeof(a_bytes)) == 0; +} + +static bool sqrt_ext(fe y, const fe x) +{ + fe y_res; + + fe x2; + fe_dbl(x2, x); + + fe b; + fe_pow22523(b, x2); + + fe b_sq; + fe_sq(b_sq, b); + + fe c; + fe_mul(c, x2, b_sq); + + if (fe_compare(c, fe_one) || fe_compare(c, fe_m1)) + { + fe_0(c); + c[0] = 3; + } + + fe c_sub_1; + fe_sub(c_sub_1, c, fe_one); + + fe_mul(y_res, x, b); + fe_mul(y_res, y_res, c_sub_1); + + if (fe_isnegative(y_res)) { + fe_neg(y_res, y_res); + } + + fe y_sq; + fe_sq(y_sq, y_res); + bool r = fe_compare(x, y_sq); + + fe_copy(y, y_res); + return r; +}; + +namespace fcmp_pp +{ + // TODO: impl faster sqrt + bool sqrt(fe y, const fe x) + { + return sqrt_ext(y, x); + }; +} // namespace fcmp_pp + +static void inv_iso(fe u_out, fe w_out, const fe u, const fe w) +{ + // 4u + fe_dbl(u_out, u); + fe_dbl(u_out, u_out); + // 2w + fe_dbl(w_out, w); +}; + +static void inv_psi1(fe e_out, fe u_out, fe w_out, const fe e, const fe u, const fe w) +{ + fe e_res, u_res, w_res; + + fe tt; + bool cc = sqrt_ext(tt, u); + fe_copy(w_res, tt); + fe w_; + fe_copy(w_, w); + fe_copy(e_res, e); + + if (!cc) + { + fe tt_sq; + fe_sq(tt_sq, tt); + fe neg_u_dbl; + fe_dbl(neg_u_dbl, u); + fe_neg(neg_u_dbl, neg_u_dbl); + if (fe_compare(tt_sq, neg_u_dbl)) { + fe_mul(tt, tt, fe_sqrtm1); + } + + fe_mul(w_, w, tt); + + fe e_sq; + fe_sq(e_sq, e); + fe_mul(w_res, fe_msqrt2b, e_sq); + + fe_mul(e_res, e_res, tt); + } + + fe w_res_sq; + fe_sq(w_res_sq, w_res); + + fe e_res_sq; + fe_sq(e_res_sq, e_res); + + fe A_e_sq; + fe_mul(A_e_sq, fe_a0, e_res_sq); + + fe w_res_w; + fe_mul(w_res_w, w_res, w_); + + fe_sub(u_res, w_res_sq, A_e_sq); + fe_reduce(u_res, u_res); + fe_sub(u_res, u_res, w_res_w); + fe_mul(u_res, u_res, fe_inv2); + + fe_copy(e_out, e_res); + fe_copy(u_out, u_res); + fe_copy(w_out, w_res); +}; + +static bool inv_psi2(fe u_out, fe w_out, const fe e, const fe u, const fe w) +{ + fe u_res, w_res; + + if (!fcmp_pp::sqrt(w_res, u)) + return false; + fe e_sq; + fe_sq(e_sq, e); + fe Ap_e_sq; + fe_mul(Ap_e_sq, fe_ap, e_sq); + + fe w_res_w; + fe_mul(w_res_w, w_res, w); + + fe_sub(u_res, u, Ap_e_sq); + fe_reduce(u_res, u_res); + fe_sub(u_res, u_res, w_res_w); + fe_mul(u_res, u_res, fe_inv2); + + fe_copy(u_out, u_res); + fe_copy(w_out, w_res); + + return true; +}; + +namespace fcmp_pp +{ +//---------------------------------------------------------------------------------------------------------------------- +bool mul8_is_identity(const ge_p3 &point) { + ge_p2 point_ge_p2; + ge_p3_to_p2(&point_ge_p2, &point); + ge_p1p1 point_mul8; + ge_mul8(&point_mul8, &point_ge_p2); + ge_p3 point_mul8_p3; + ge_p1p1_to_p3(&point_mul8_p3, &point_mul8); + return ge_p3_is_point_at_infinity_vartime(&point_mul8_p3); +} +//---------------------------------------------------------------------------------------------------------------------- +// https://github.com/kayabaNerve/fcmp-plus-plus/blob/94744c5324e869a9483bbbd93a864e108304bf76/crypto/divisors/src/tests/torsion_check.rs +// Returns true if point is torsion free +// Pre-condition: point is a valid point and point*8 not equal to identity +// WARNING1: this approach needs to be carefully vetted academically and audited +// before it can be used in production. +// WARNING2: since fe_add and fe_sub expect the input fe's to be within a +// smaller domain than the output fe, we sometimes need to "reduce" a field elem +// to chain calls to fe_add and fe_sub. Notice all calls to fe_reduce. +bool torsion_check_vartime(const ge_p3 &point) { + //assert(!mul8_is_identity(point)); + + // ed to wei + fe e, u, w; + { + fe z_plus_ed_y, z_minus_ed_y; + fe_add(z_plus_ed_y, fe_one, point.Y); + fe_sub(z_minus_ed_y, fe_one, point.Y); + + // e + fe_mul(e, z_minus_ed_y, point.X); + // u + fe_mul(u, fe_a_sub_d, z_plus_ed_y); + fe_mul(u, u, point.X); + fe_mul(u, u, e); + // w + fe_dbl(w, z_minus_ed_y); + } + + //assert(check_e_u_w(e, u, w)); + + // Torsion check + for (int i = 0; i < 2; ++i) { + inv_iso(u, w, u, w); + if (!inv_psi2(u, w, e, u, w)) { + return false; + } + inv_psi1(e, u, w, e, u, w); + //assert(check_e_u_w(e, u, w)); + } + + fe _; + inv_iso(u, _, u, w); + + if (!sqrt(u, u)) { + return false; + } + + return true; +} +//---------------------------------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- +}//namespace fcmp_pp diff --git a/external/src/cryptonote/fcmp_pp_crypto.h b/external/src/cryptonote/fcmp_pp_crypto.h new file mode 100644 index 0000000..a6c5da6 --- /dev/null +++ b/external/src/cryptonote/fcmp_pp_crypto.h @@ -0,0 +1,36 @@ +// Copyright (c) 2024, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace fcmp_pp +{ +bool sqrt(fe y, const fe x); +bool mul8_is_identity(const ge_p3 &point); +bool torsion_check_vartime(const ge_p3 &point); +} //namespace fcmp_pp diff --git a/src/params.cpp b/src/params.cpp index d7257ed..9d2f6ff 100644 --- a/src/params.cpp +++ b/src/params.cpp @@ -73,12 +73,22 @@ Params::Params(int argc, char* const argv[]) } if ((strcmp(argv[i], "--wallet") == 0) && (i + 1 < argc)) { - m_mainWallet.decode(argv[++i]); + const char* s = argv[++i]; + + if (!m_mainWallet.decode(s)) { + LOGERR(1, "Wallet " << s << " failed to decode"); + } + ok = true; } if ((strcmp(argv[i], "--subaddress") == 0) && (i + 1 < argc)) { - m_subaddress.decode(argv[++i]); + const char* s = argv[++i]; + + if (!m_subaddress.decode(s)) { + LOGERR(1, "Subaddress " << s << " failed to decode"); + } + ok = true; } @@ -333,8 +343,14 @@ Params::Params(int argc, char* const argv[]) char display_wallet_buf[Wallet::ADDRESS_LENGTH] = {}; if (m_mainWallet.valid() && m_subaddress.valid()) { - m_miningWallet.assign(m_subaddress.spend_public_key(), m_mainWallet.view_public_key(), m_mainWallet.type(), false); - m_subaddress.encode(display_wallet_buf); + if (!m_miningWallet.assign(m_subaddress.spend_public_key(), m_mainWallet.view_public_key(), m_mainWallet.type(), false)) { + LOGERR(1, "Failed to configure the mining wallet, falling back to " << m_mainWallet); + m_miningWallet = m_mainWallet; + m_mainWallet.encode(display_wallet_buf); + } + else { + m_subaddress.encode(display_wallet_buf); + } } else if (m_mainWallet.valid()) { m_miningWallet = m_mainWallet; @@ -367,6 +383,16 @@ bool Params::valid() const } } + if (!m_mainWallet.torsion_check()) { + LOGERR(1, m_mainWallet << " didn't pass the torsion check. It will be incompatible with FCMP++."); + return false; + } + + if (m_subaddress.valid() && !m_subaddress.torsion_check()) { + LOGERR(1, m_subaddress << " didn't pass the torsion check. It will be incompatible with FCMP++."); + return false; + } + if (m_mergeMiningHosts.size() > 10) { LOGERR(1, "Too many merge mining blockchains."); return false; diff --git a/src/side_chain.cpp b/src/side_chain.cpp index 0738c1e..0ac137d 100644 --- a/src/side_chain.cpp +++ b/src/side_chain.cpp @@ -2486,7 +2486,9 @@ void SideChain::precalc_worker() for (const std::pair& w : wallets) { hash eph_public_key; uint8_t view_tag; - w.second->get_eph_public_key(job->m_txkeySec, w.first, eph_public_key, view_tag); + if (!w.second->get_eph_public_key(job->m_txkeySec, w.first, eph_public_key, view_tag)) { + LOGWARN(6, "get_eph_public_key failed in precalc_worker"); + } } } while (true); } diff --git a/src/wallet.cpp b/src/wallet.cpp index d342a9b..de9fb14 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -27,6 +27,10 @@ extern "C" { #include "crypto-ops.h" } +#include "fcmp_pp_crypto.h" + +LOG_CATEGORY(Wallet) + namespace { // Allow only regular addresses (no integrated addresses, no subaddresses) @@ -81,7 +85,9 @@ namespace p2pool { Wallet::Wallet(const char* address) : m_prefix(0), m_checksum(0), m_type(NetworkType::Invalid), m_subaddress(false) { - decode(address); + if (!decode(address) && address) { + LOGWARN(1, address << " failed to decode"); + } } Wallet::Wallet(const Wallet& w) @@ -179,6 +185,11 @@ bool Wallet::decode(const char* address) m_type = NetworkType::Invalid; } + if (!torsion_check()) { + LOGWARN(1, "Torsion check failed for wallet " << *this << "! It will not be compatible with FCMP++."); + // TODO: add "m_type = NetworkType::Invalid;" and return false in a later release, closer to FCMP++ hardfork + } + return valid(); } @@ -213,6 +224,11 @@ bool Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key, Network m_type = type; m_subaddress = subaddress; + if (!torsion_check()) { + LOGWARN(1, "Torsion check failed for wallet " << *this << "! It will not be compatible with FCMP++."); + // TODO: add "m_type = NetworkType::Invalid;" and return false in a later release, closer to FCMP++ hardfork + } + return true; } @@ -256,4 +272,18 @@ bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash return true; } +bool Wallet::torsion_check() const +{ + ge_p3 p1, p2; + if ((ge_frombytes_vartime(&p1, m_spendPublicKey.h) != 0) || (ge_frombytes_vartime(&p2, m_viewPublicKey.h) != 0)) { + return false; + } + + return + !fcmp_pp::mul8_is_identity(p1) && + !fcmp_pp::mul8_is_identity(p2) && + fcmp_pp::torsion_check_vartime(p1) && + fcmp_pp::torsion_check_vartime(p2); +} + } // namespace p2pool diff --git a/src/wallet.h b/src/wallet.h index 5a4dfa5..ae8ba2f 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -34,31 +34,32 @@ public: Wallet(const Wallet& w); Wallet& operator=(const Wallet& w); - FORCEINLINE bool valid() const { return m_type != NetworkType::Invalid; } + [[nodiscard]] FORCEINLINE bool valid() const { return m_type != NetworkType::Invalid; } - bool decode(const char* address); - bool assign(const hash& spend_pub_key, const hash& view_pub_key, NetworkType type, bool subaddress); + [[nodiscard]] bool decode(const char* address); + [[nodiscard]] bool assign(const hash& spend_pub_key, const hash& view_pub_key, NetworkType type, bool subaddress); void encode(char (&buf)[ADDRESS_LENGTH]) const; - FORCEINLINE std::string encode() const + [[nodiscard]] FORCEINLINE std::string encode() const { char buf[ADDRESS_LENGTH]; encode(buf); return std::string(buf, buf + ADDRESS_LENGTH); } - bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag = nullptr) const; + [[nodiscard]] bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag = nullptr) const; FORCEINLINE bool operator<(const Wallet& w) const { return (m_spendPublicKey < w.m_spendPublicKey) || ((m_spendPublicKey == w.m_spendPublicKey) && (m_viewPublicKey < w.m_viewPublicKey)); } FORCEINLINE bool operator==(const Wallet& w) const { return (m_spendPublicKey == w.m_spendPublicKey) && (m_viewPublicKey == w.m_viewPublicKey); } - FORCEINLINE uint64_t prefix() const { return m_prefix; } - FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; } - FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; } - FORCEINLINE uint32_t checksum() const { return m_checksum; } - FORCEINLINE NetworkType type() const { return m_type; } - FORCEINLINE bool is_subaddress() const { return m_subaddress; } + [[nodiscard]] FORCEINLINE uint64_t prefix() const { return m_prefix; } + [[nodiscard]] FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; } + [[nodiscard]] FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; } + [[nodiscard]] FORCEINLINE uint32_t checksum() const { return m_checksum; } + [[nodiscard]] FORCEINLINE NetworkType type() const { return m_type; } + [[nodiscard]] FORCEINLINE bool is_subaddress() const { return m_subaddress; } + [[nodiscard]] bool torsion_check() const; private: uint64_t m_prefix; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e677c40..78a140d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -63,6 +63,7 @@ set(SOURCES ../external/src/crypto/sha256.c ../external/src/cryptonote/crypto-ops-data.c ../external/src/cryptonote/crypto-ops.c + ../external/src/cryptonote/fcmp_pp_crypto.cpp ../external/src/hardforks/hardforks.cpp ../src/block_cache.cpp ../src/block_template.cpp diff --git a/tests/src/crypto_tests.cpp b/tests/src/crypto_tests.cpp index 879c99a..cb33056 100644 --- a/tests/src/crypto_tests.cpp +++ b/tests/src/crypto_tests.cpp @@ -18,12 +18,17 @@ #include "common.h" #include "crypto.h" #include "util.h" +extern "C" { +#include "crypto-ops.h" +} +#include "fcmp_pp_crypto.h" + #include "gtest/gtest.h" #include namespace p2pool { -TEST(crypto, derivation) +TEST(crypto, ops) { init_crypto_cache(); { @@ -103,6 +108,28 @@ TEST(crypto, derivation) ASSERT_EQ(pub, pub_check); ASSERT_EQ(sec, sec_check); } + else if (name == "check_key") { + hash pub_key; + std::string result_str; + + f >> pub_key >> result_str; + + ge_p3 p; + ASSERT_EQ(ge_frombytes_vartime(&p, pub_key.h) == 0, result_str == "true"); + } + else if (name == "check_torsion") { + hash pub_key; + std::string result_str; + + f >> pub_key >> result_str; + + ge_p3 p; + ASSERT_EQ( + (ge_frombytes_vartime(&p, pub_key.h) == 0) + && !fcmp_pp::mul8_is_identity(p) + && fcmp_pp::torsion_check_vartime(p) + , result_str == "true"); + } } while (!f.eof()); } } diff --git a/tests/src/crypto_tests.txt b/tests/src/crypto_tests.txt index 2176456..3097b18 100644 --- a/tests/src/crypto_tests.txt +++ b/tests/src/crypto_tests.txt @@ -712,3 +712,404 @@ get_tx_keys 87f91cbbb4409ecb9e8fb7e4777ca92a4ffcd001e6254d5e39d9f05dce677775 988 get_tx_keys e3542c51b61a3d9eb0773335582a8d6dd24c32413a969d5cb9629ddc77a8e49f c7d7ce6d43ec125071b8c5115f010164b89a33eb4aedf4ca31700a975177c617 df5476bf0fa51deeda6e0ac0c48e0fd64e8d3abf9b0e60572c757afebe2bb71a ef2c6830783a181234a503da9308a158c47f97d0da0c5b612a8ea67b4ec3e708 get_tx_keys dfffcad8d938c771497214aad51e5f8e307b9cfb5bf7f37cfcf73ae19344aa50 1e117af98c71f5443715d961d0d0d4436e2451be401db8ad40e2a66de65dbc0d a165ebbedf785963098865897d62821c72e0d89afbeffad3b54012745be4cd78 3ae76a2dfd4acc0e7bb7cfbdd516e269a2294005b1bc877f2feeaf52ac2aa70c get_tx_keys cabfa01f1fed98842b2a30a547d34c5be1fbaa1c5753d83519b59f79239956c7 0faf698d4917c7edccb1dc401e9b33d775d6d699eff51ff6a29ca5da2d9df969 4021cad4e8a45714bc893475487d8b07c868bbddd7086f5befaadc92fca9e892 2417b7887c6f287775d736d0fbe1544d53e2ed33adc948ceca8c16f0b14b2d05 +check_key c2cb3cf3840aa9893e00ec77093d3d44dba7da840b51c48462072d58d8efd183 false +check_key bd85a61bae0c101d826cbed54b1290f941d26e70607a07fc6f0ad611eb8f70a6 true +check_key 328f81cad4eba24ab2bad7c0e56b1e2e7346e625bcb06ae649aef3ffa0b8bef3 false +check_key 6016a5463b9e5a58c3410d3f892b76278883473c3f0b69459172d3de49e85abe true +check_key 4c71282b2add07cdc6898a2622553f1ca4eb851e5cb121181628be5f3814c5b1 false +check_key 69393c25c3b50e177f81f20f852dd604e768eb30052e23108b3cfa1a73f2736e true +check_key 3d5a89b676cb84c2be3428d20a660dc6a37cae13912e127888a5132e8bac2163 true +check_key 78cd665deb28cebc6208f307734c56fccdf5fa7e2933fadfcdd2b6246e9ae95c false +check_key e03b2414e260580f86ee294cd4c636a5b153e617f704e81dad248fbf715b2ee4 true +check_key 28c3503ce82d7cdc8e0d96c4553bcf0352bbcfc73925495dbe541e7e1df105fc false +check_key 06855c3c3e0d03fec354059bda319b39916bdc10b6581e3f41b335ee7b014fd5 false +check_key 556381485df0d7d5a268ab5ecfb2984b060acc63471183fcf538bf273b0c0cb5 true +check_key c7f76d82ac64b1e7fdc32761ff00d6f0f7ada4cf223aa5a11187e3a02e1d5319 true +check_key cfa85d8bdb6f633fcf031adee3a299ac42eeb6bd707744049f652f6322f5aa47 true +check_key 91e9b63ced2b08979fee713365464cc3417c4f238f9bdd3396efbb3c58e195ee true +check_key 7b56e76fe94bd30b3b2f2c4ba5fe4c504821753a8965eb1cbcf8896e2d6aba19 true +check_key 7338df494bc416cf5edcc02069e067f39cb269ce67bd9faba956021ce3b3de3a false +check_key f9a1f27b1618342a558379f4815fa5039a8fe9d98a09f45c1af857ba99231dc1 false +check_key b2a1f37718180d4448a7fcb5f788048b1a7132dde1cfd25f0b9b01776a21c687 true +check_key 0d3a0f9443a8b24510ad1e76a8117cca03bce416edfe35e3c2a2c2712454f8dc false +check_key d8d3d806a76f120c4027dc9c9d741ad32e06861b9cfbc4ce39289c04e251bb3c false +check_key 1e9e3ba7bc536cd113606842835d1f05b4b9e65875742f3a35bfb2d63164b5d5 true +check_key 5c52d0087997a2cdf1d01ed0560d94b4bfd328cb741cb9a8d46ff50374b35a57 true +check_key bb669d4d7ffc4b91a14defedcdbd96b330108b01adc63aa685e2165284c0033b false +check_key d2709ae751a0a6fd796c98456fa95a7b64b75a3434f1caa3496eeaf5c14109b4 true +check_key e0c238cba781684e655b10a7d4af04ab7ff2e7022182d7ed2279d6adf36b3e7a false +check_key 34ebb4bf871572cee5c6935716fab8c8ec28feef4f039763d8f039b84a50bf4c false +check_key 4730d4f38ec3f3b83e32e6335d2506df4ee39858848842c5a0184417fcc639e4 true +check_key d42cf7fdf5e17e0a8a7f88505a2b7a3d297113bd93d3c20fa87e11509ec905a2 true +check_key b757c95059cefabb0080d3a8ebca82e46efecfd29881be3121857f9d915e388c false +check_key bbe777aaf04d02b96c0632f4b1c6f35f1c7bcbc5f22af192f92c077709a2b50b false +check_key 73518522aabd28566f858c33fccb34b7a4de0e283f6f783f625604ee647afad9 true +check_key f230622c4a8f6e516590466bd10f86b64fbef61695f6a054d37604e0b024d5af false +check_key bc6b9a8379fd6c369f7c3bd9ddce58db6b78f27a41d798bb865c3920824d0943 false +check_key 45a4f87c25898cd6be105fa1602b85c4d862782adaac8b85c996c4a2bcd8af47 true +check_key eb4ad3561d21c4311affbd7cc2c7ff5fd509f72f88ba67dc097a75c31fdbd990 false +check_key 2f34f4630c09a23b7ecc19f02b4190a26df69e07e13de8069ae5ff80d23762fc true +check_key 2ea4e4fb5085eb5c8adee0d5ab7d35c67d74d343bd816cd13924536cffc2527c true +check_key 5d35467ee6705a0d35818aa9ae94e4603c3e5500bfc4cf4c4f77a7160a597aa6 true +check_key 8ff42bc76796e20c99b6e879369bd4b46a256db1366416291de9166e39d5a093 true +check_key 0262ba718850df6c621e8a24cd9e4831c047e38818a89e15c7a06a489a4558e1 false +check_key 58b29b2ba238b534b08fb46f05f430e61cb77dc251b0bb50afec1b6061fd9247 false +check_key 153170e3dc2b0e1b368fc0d0e31053e872f094cdace9a2846367f0d9245a109b false +check_key 40419d309d07522d493bb047ca9b5fb6c401aae226eefae6fd395f5bb9114200 true +check_key 713068818d256ef69c78cd6082492013fbd48de3c9e7e076415dd0a692994504 true +check_key a7218ee08e50781b0c87312d5e0031467e863c10081668e3792d96cbcee4e474 true +check_key 356ce516b00e674ef1729c75b0a68090e7265cef675bbf32bf809495b67e9342 false +check_key 52a5c053293675e3efd2c585047002ea6d77931cbf38f541b9070d319dc0d237 false +check_key 77c0080bf157e069b18c4c604cc9505c5ec6f0f9930e087592d70507ca1b5534 false +check_key e733bc41f880a4cfb1ca6f397916504130807289cacfca10b15f5b8d058ed1bf false +check_key c4f1d3c884908a574ecea8be10e02277de35ef84a1d10f105f2be996f285161f true +check_key aed677f7f69e146aa0863606ac580fc0bbdc22a88c4b4386abaa4bdfff66bcc9 false +check_key 6ad0edf59769599af8caa986f502afc67aecbebb8107aaf5e7d3ae51d5cf8dd8 false +check_key 64a0a70e99be1f775c222ee9cd6f1bee6f632cb9417899af398ff9aff70661c6 true +check_key c63afaa03bb5c4ed7bc77aac175dbfb73f904440b2e3056a65850ac1bd261332 false +check_key a4e89cd2471c26951513b1cfbdcf053a86575e095af52495276aa56ede8ce344 false +check_key 2ce935d97f7c3ddb973de685d20f58ee39938fe557216328045ec2b83f3132be true +check_key 3e3d38b1fca93c1559ac030d586616354c668aa76245a09e3fa6de55ac730973 true +check_key 8b81b9681f76a4254007fd07ed1ded25fc675973ccb23afd06074805194733a4 false +check_key 26d1c15dfc371489439e29bcef2afcf7ed01fac24960fdc2e7c20847a8067588 true +check_key 85c1199b5a4591fc4cc36d23660648c1b9cfbb0e9c47199fa3eea33299a3dcec false +check_key 60830ba5449c1f04ac54675dfc7cac7510106c4b7549852551f8fe65971123e2 false +check_key 3e43c28c024597b3b836e4bc16905047cbf6e841b80e0b8cd6a325049070c2a5 false +check_key 474792c16a0032343a6f28f4cb564747c3b1ea0b6a6b9a42f7c71d7cc3dd3b44 true +check_key c8ec5e67cb5786673085191881950a3ca20dde88f46851b01dd91c695cfbad16 true +check_key 861c4b24b24a87b8559e0bb665f84dcc506c147a909f335ae4573b92299f042f false +check_key 2c9e0fe3e4983d79f86c8c36928528f1bc90d94352ce427032cdef6906d84d0b true +check_key 9293742822c2dff63fdc1bf6645c864fd527cea2ddba6d4f3048d202fc340c9a true +check_key 3956422ad380ef19cb9fe360ef09cc7aaec7163eea4114392a7a0b2e2671914e true +check_key 5ae8e72cadda85e525922fec11bd53a261cf26ee230fe85a1187f831b1b2c258 false +check_key 973feca43a0baf450c30ace5dc19015e19400f0898316e28d9f3c631da31f99a true +check_key dd946c91a2077f45c5c16939e53859d9beabaf065e7b1b993d5e5cd385f8716e true +check_key b3928f2d67e47f6bd6da81f72e64908d8ff391af5689f0202c4c6fec7666ffe8 true +check_key 313382e82083697d7f9d256c3b3800b099b56c3ef33cacdccbd40a65622e25fc false +check_key 7d65380c12144802d39ed9306eed79fe165854273700437c0b4b50559800c058 true +check_key 4db5c20a49422fd27739c9ca80e2271a8a125dfcead22cb8f035d0e1b7b163be true +check_key dd76a9f565ef0e44d1531349ec4c5f7c3c387c2f5823e693b4952f4b0b70808c true +check_key 66430bf628eae23918c3ed17b42138db1f98c24819e55fc4a07452d0c85603eb true +check_key 9f0b677830c3f089c27daf724bb10be848537f8285de83ab0292d35afb617f77 false +check_key cbf98287391fb00b1e68ad64e9fb10198025864c099b8b9334d840457e673874 true +check_key a42552e9446e49a83aed9e3370506671216b2d1471392293b8fc2b81c81a73ee false +check_key fb3de55ac81a923d506a514602d65d004ec9d13e8b47e82d73af06da73006673 false +check_key e17abb78e58a4b72ff4ad7387b290f2811be880b394b8bcaae7748ac09930169 false +check_key 9ffbda7ace69753761cdb5eb01f75433efa5cdb6a4f1b664874182c6a95adcba true +check_key 507123c979179ea0a3f7f67fb485f71c8636ec4ec70aa47b92f3c707e7541a54 false +check_key f1d0b156571994ef578c61cb6545d34f834eb30e4357539a5633c862d4dffa91 false +check_key 3de62311ec14f9ee95828c190b2dc3f03059d6119e8dfccb7323efc640e07c75 false +check_key 5e50bb48bc9f6dd11d52c1f0d10d8ae5674d7a4af89cbbce178dafc8a562e5fe false +check_key 20b2c16497be101995391ceefb979814b0ea76f1ed5b6987985bcdcd17b36a81 false +check_key d63bff73b914ce791c840e99bfae0d47afdb99c2375e33c8f149d0df03d97873 false +check_key 3f24b3d94b5ddd244e4c4e67a6d9f533f0396ca30454aa0ca799f21328b81d47 true +check_key 6a44c016f09225a6d2e830290719d33eb29b53b553eea7737ed3a6e297b2e7d2 true +check_key ff0f34df0c76c207b8340be2009db72f730c69c2bbfeea2013105eaccf1d1f8e true +check_key 4baf559869fe4e915e219c3c8d9a2330fc91e542a5a2a7311d4d59fee996f807 true +check_key 1632207dfef26e97d13b0d0035ea9468fc5a8a89b0990fce77bb143c9d7f3b67 true +check_key fcb3dee3993d1a47630f29410903dd03706bd5e81c5802e6f1b9095cbdb404d3 true +check_key fb527092b9809e3d27d7588c7ef89915a769b99c1e03e7f72bbead9ed837daae false +check_key 902b118d27d40ab9cbd55edd375801ce302cdb59e09c8659a3ea1401918d8bba false +check_key 4d6fbf25ca51e263a700f1abf84f758dde3d11b632e908b3093d64fe2e70ea0a true +check_key f4c3211ec70affc1c9a94a6589460ee8360dad5f8c679152f16994038532e3fc true +check_key c2b3d73ac14956d7fdf12fa92235af1bb09e1566a6a6ffd0025682c750abdd69 false +check_key b7e68c12207d2e2104fb2ca224829b6fccc1c0e2154e8a931e3c837a945f4430 false +check_key 56ca0ca227708f1099bda1463db9559541c8c11ffad7b3d95c717471f25a01bf true +check_key 3eef3a46833e4d851671182a682e344e36bea7211a001f3b8af1093a9c83f1b2 true +check_key bd1f4a4f26cab7c1cbc0e17049b90854d6d28d2d55181e1b5f7a8045fcdfa06e true +check_key 8537b01c87e7c184d9555e8d93363dcd9b60a8acc94cd3e41eb7525fd3e1d35a false +check_key 68ace49179d549bad391d98ab2cc8afee65f98ce14955c3c1b16e850fabec231 true +check_key f9922f8a660e7c3e4f3735a817d18b72f59166a0be2d99795f953cf233a27e24 true +check_key 036b6be3da26e80508d5a5a6a5999a1fe0db1ac4e9ade8f1ea2eaf2ea9b1a70e true +check_key 5e595e886ce16b5ea31f53bcb619f16c8437276618c595739fece6339731feb0 false +check_key 4ee2cebae3476ed2eeb7efef9d20958538b3642f938403302682a04115c0f8ed false +check_key 519eedbd0da8676063ce7d5a605b3fc27afeecded857afa24b894ad248c87b5d false +check_key ce2b627c0accf4a3105796680c37792b30c6337d2d4fea11678282455ff82ff7 false +check_key aa26ed99071a8416215e8e7ded784aa7c2b303aab67e66f7539905d7e922eb4d false +check_key 435ae49c9ca26758aa103bdcca8d51393b1906fe27a61c5245361e554f335ec2 true +check_key 42568af395bd30024f6ccc95205c0e11a6ad1a7ee100f0ec46fcdf0af88e91fb false +check_key 0b4a78d1fde56181445f04ca4780f0725daa9c375b496fab6c037d6b2c2275db true +check_key 2f82d2a3c8ce801e1ad334f9e074a4fbf76ffac4080a7331dc1359c2b4f674a4 false +check_key 24297d8832d733ed052dd102d4c40e813f702006f325644ccf0cb2c31f77953f false +check_key 5231a53f6bea7c75b273bde4a9f673044ed87796f20e0909978f29d98fc8d4f0 true +check_key 94b5affcf78be5cf62765c32a0794bc06b4900e8a47ddba0e166ec20cec05935 true +check_key c14b4d846ea52ffbbb36aa62f059453af3cfae306280dada185d2d385ef8f317 true +check_key cceb34fddf01a6182deb79c6000a998742d4800d23d1d8472e3f43cd61f94508 true +check_key 1faffa33407fba1634d4136cf9447896776c16293b033c6794f06774b514744c true +check_key faaac98f644a2b77fb09ba0ebf5fcddf3ff55f6604c0e9e77f0278063e25113a true +check_key 09e8525b00bea395978279ca979247a76f38f86dce4465eb76c140a7f904c109 true +check_key 2d797fc725e7fb6d3b412694e7386040effe4823cdf01f6ec7edea4bc0e77e20 false +check_key bbb74dabee651a65f46bca472df6a8a749cc4ba5ca35078df5f6d27a772f922a false +check_key 77513ca00f3866607c3eff5c2c011beffa775c0022c5a4e7de1120a27e6687fd true +check_key 10064c14ace2a998fc2843eeeb62884fe3f7ab331ca70613d6a978f44d9868eb false +check_key 026ae84beb5e54c62629a7b63702e85044e38cadfc9a1fcabee6099ba185005c false +check_key aef91536292b7ba34a3e787fb019523c2fa7a0d56fca069cc82ccb6b02a45b14 false +check_key 147bb1a82c623c722540feaad82b7adf4b85c6ec0cbcef3ca52906f3e85617ac true +check_key fc9fb281a0847d58dc9340ef35ef02f7d20671142f12bdd1bfb324ab61d03911 false +check_key b739801b9455ac617ca4a7190e2806669f638d4b2f9288171afb55e1542c8d71 false +check_key 494cc1e2ee997eb1eb051f83c4c89968116714ddf74e460d4fa1c6e7c72e3eb3 true +check_key ed2fbdf2b727ed9284db90ec900a942224787a880bc41d95c4bc4cf136260fd7 true +check_key 02843d3e6fc6835ad03983670a592361a26948eb3e31648d572416a944d4909e true +check_key c14fea556a7e1b6b6c3d4e2e38a4e7e95d834220ff0140d3f7f561a34e460801 true +check_key 5f8f82a35452d0b0d09ffb40a1154641916c31e161ad1a6ab8cfddc2004efdf6 false +check_key 7b93d72429fab07b49956007eba335bb8c5629fbf9e7a601eaa030f196934a56 true +check_key 6a63ed96d2e46c2874beaf82344065d94b1e5c04406997f94caf4ccd97cfbab9 false +check_key c915f409e1e0f776d1f440aa6969cfec97559ef864b07d8c0d7c1163871b4603 true +check_key d06bc33630fc94303c2c369481308f805f5ce53c40141160aa4a1f072967617e false +check_key 1aafb14ca15043c2589bcd32c7c5f29479216a1980e127e9536729faf1c40266 true +check_key 58c115624a20f4b0c152ccd048c54a28a938556863ab8521b154d3165d3649cd false +check_key 9001ba086e8aa8a67e128f36d700cc641071556306db7ec9b8ac12a6256b27b7 false +check_key 898c468541634fb0def11f82c781341fce0def7b15695af4e642e397218c730c true +check_key 47ea6539e65b7b611b0e1ae9ee170adf7c31581ca9f78796d8ebbcc5cd74b712 false +check_key 0c60952a64eeac446652f5d3c136fd36966cf66310c15ee6ab2ecbf981461257 false +check_key 682264c4686dc7736b6e46bdc8ab231239bc5dac3f5cb9681a1e97a527945e8e true +check_key 276006845ca0ea4238b231434e20ad8b8b2a36876effbe1d1e3ffb1f14973397 true +check_key eecd3a49e55e32446f86c045dce123ef6fe2e5c57db1d850644b3c56ec689fce true +check_key a4dced63589118db3d5aebf6b5670e71250f07485ca4bb6dddf9cce3e4c227a1 false +check_key b8ade608ba43d55db7ab481da88b74a9be513fca651c03e04d30cc79f50e0276 false +check_key 0d91de88d007a03fe782f904808b036ff63dec6b73ce080c55231afd4ed261c3 true +check_key 87c59becb52dd16501edadbb0e06b0406d69541c4d46115351e79951a8dd9c28 true +check_key 9aee723be2265171fe10a86d1d3e9cf5a4e46178e859db83f86d1c6db104a247 false +check_key 509d34ae5bf56db011845b8cdf0cc7729ed602fce765e9564cb433b4d4421a43 false +check_key 06e766d9a6640558767c2aab29f73199130bfdc07fd858a73e6ae8e7b7ba23ba false +check_key 801c4fe5ab3e7cf13f7aa2ca3bc57cc8eba587d21f8bc4cd40b1e98db7aec8d9 false +check_key d85ad63aeb7d2faa22e5c9b87cd27f45b01e6d0fdc4c3ddf105584ac0a021465 false +check_key a7ca13051eb2baeb5befa5e236e482e0bb71803ad06a6eae3ae48742393329d2 true +check_key 5a9ba3ec20f116173d933bf5cf35c320ed3751432f3ab453e4a6c51c1d243257 false +check_key a4091add8a6710c03285a422d6e67863a48b818f61c62e989b1e9b2ace240a87 false +check_key bdee0c6442e6808f25bb18e21b19032cf93a55a5f5c6426fba2227a41c748684 true +check_key d4aeb6cdad9667ec3b65c7fbc5bfd1b82bba1939c6bb448a86e40aec42be5f25 false +check_key 73525b30a77f1212f7e339ec11f48c453e476f3669e6e70bebabc2fe9e37c160 true +check_key 45501f2dc4d0a3131f9e0fe37a51c14869ab610abd8bf0158111617924953629 false +check_key 07d0e4c592aa3676adf81cca31a95d50c8c269d995a78cde27b2a9a7a93083a6 false +check_key a1797d6178c18add443d22fdbf45ca5e49ead2f78b70bdf1500f570ee90adca5 true +check_key 0961e82e6e7855d7b7bf96777e14ae729f91c5bbd20f805bd7daac5ccbec4bab false +check_key 57f5ba0ad36e997a4fb585cd2fc81b9cc5418db702c4d1e366639bb432d37c73 true +check_key 82b005be61580856841e042ee8be74ae4ca66bb6733478e81ca1e56213de5c05 false +check_key d7733dcae1874c93e9a2bd46385f720801f913744d60479930dad7d56c767cdc false +check_key b8b8b698609ac3f1bd8f4965151b43b362e6c5e3d1c1feae312c1d43976d59ab true +check_key 4bba7815a9a1b86a5b80b17ac0b514e2faa7a24024f269b330e5b7032ae8c04e true +check_key 0f70da8f8266b58acda259935ef1a947c923f8698622c5503520ff31162e877b false +check_key 233eaa3db80f314c6c895d1328a658a9175158fa2483ed216670c288a04b27bc false +check_key a889f124fabfd7a1e2d176f485be0cbd8b3eeaafeee4f40e99e2a56befb665be true +check_key 2b7b8abc198b11cf7efa21bc63ec436f790fe1f9b8c044440f183ab291af61d6 true +check_key 2491804714f7938cf501fb2adf07597b4899b919cabbaab49518b8f8767fdc6a true +check_key 52744a54fcb00dc930a5d7c2bc866cbfc1e75dd38b38021fd792bb0ca9f43164 true +check_key e42cbf70b81ba318419104dffbb0cdc3b7e7d4698e422206b753a4e2e6fc69bb false +check_key 2faff73e4fed62965f3dbf2e6446b5fea0364666cc8c9450b6ed63bbb6f5f0e7 true +check_key 8b963928d75be661c3c18ddd4f4d1f37ebc095ce1edc13fe8b23784c8f416dfd false +check_key b1162f952808434e4d2562ffda98bd311613d655d8cf85dc86e0a6c59f7158bc true +check_key 5a69adcd9e4f5b0020467e968d85877cb3aa04fa86088d4499b57ca65a665836 true +check_key 61ab47da432c829d0bc9d4fdb59520b135428eec665ad509678188b81c7adf49 false +check_key 154bb547f22f65a87c0c3f56294f5791d04a3c14c8125d256aeed8ec54c4a06e true +check_key 0a78197861c30fd3547b5f2eabd96d3ac22ac0632f03b7afd9d5d2bfc2db352f true +check_key 8bdeadcca1f1f8a4a67b01ed2f10ef31aba7b034e8d1df3a69fe9aebf32454e0 false +check_key f4b17dfca559be7d5cea500ac01e834624fed9befae3af746b39073d5f63190d true +check_key 622c52821e16ddc63b58f3ec2b959fe8c6ea6b1a596d9a58fd81178963f41c01 true +check_key 07bedd5d55c937ef5e23a56c6e58f31adb91224d985285d7fef39ede3a9efb17 false +check_key 5179bf3b7458648e57dc20f003c6bbfd55e8cd7c0a6e90df6ef8e8183b46f99d true +check_key 683c80c3f304f10fdd53a84813b5c25b1627ebd14eb29b258b41cd14396ef41f true +check_key c266244ed597c438170875fe7874f81258a830105ca1108131e6b8fea95eb8ba true +check_key 0c1cdc693df29c2d1e66b2ce3747e34a30287d5eb6c302495634ec856593fe8e true +check_key 28950f508f6a0d4c20ab5e4d55b80565a6a539092e72b7eb0ed9fa5017ecef88 false +check_key 8328a2a5fcfc4433b1c283539a8943e6eb8cc16c59f29dedc3af2c77cfd56f25 true +check_key 5d0f82319676d4d3636ff5dc2a38ea5ec8aeaac4835fdcab983ab35d76b7967b false +check_key cafcc75e94a014115f25c23aaae86e67352f928f468d4312b92240ff0f3a4481 false +check_key 3e5fdd8072574218f389d018e959669e8ca4ef20b114ea7dce7bfb32339f9f42 true +check_key 591763e3390a78ccb529ceea3d3a97165878b179ad2edaa166fd3c78ec69d391 true +check_key 7a0a196935bf79dc2b1c3050e8f2bf0665f7773fc07511b828ec1c4b1451d317 false +check_key 9cf0c034162131fbaa94a608f58546d0acbcc2e67b62a0b2be2ce75fc8c25b9a false +check_key e3840846e3d32644d45654b96def09a5d6968caca9048c13fcaab7ae8851c316 false +check_key a4e330253739af588d70fbda23543f6df7d76d894a486d169e5fedf7ed32d2e2 false +check_key cfb41db7091223865f7ecbdda92b9a6fb08887827831451de5bcb3165395d95d true +check_key 3d10bd023cef8ae30229fdbfa7446a3c218423d00f330857ff6adde080749015 false +check_key 4403b53b8d4112bb1727bb8b5fd63d1f79f107705ffe17867704e70a61875328 false +check_key 121ef0813a9f76b7a9c045058557c5072de6a102f06a9b103ead6af079420c29 true +check_key 386204cf473caf3854351dda55844a41162eb9ce4740e1e31cfef037b41bc56e false +check_key eb5872300dc658161df469364283e4658f37f6a1349976f8973bd6b5d1d57a39 true +check_key b8f32188f0fc62eeb38a561ff7b7f3c94440e6d366a05ef7636958bc97834d02 false +check_key a817f129a8292df79eef8531736fdebb2e985304653e7ef286574d0703b40fb4 false +check_key 2c06595bc103447b9c20a71cd358c704cb43b0b34c23fb768e6730ac9494f39e true +check_key dd84bc4c366ced4f65c50c26beb8a9bc26c88b7d4a77effbb0f7af1b28e25734 false +check_key 76b4d33810eed637f90d49a530ac5415df97cafdac6f17eda1ba7eb9a14e5886 true +check_key 926ce5161c4c92d90ec4efc58e5f449a2c385766c42d2e60af16b7362097aef5 false +check_key 20c661f1e95e94a745eb9ec7a4fa719eff2f64052968e448d4734f90952aefee false +check_key 671b50abbd119c756010416e15fcdcc9a8e92eed0f67cbca240c3a9154db55c0 false +check_key df7aeee8458433e5c68253b8ef006a1c74ce3aef8951056f1fa918a8eb855213 false +check_key 70c81a38b92849cf547e3d5a6570d78e5228d4eaf9c8fdd15959edc9eb750daf false +check_key 55a512100b72d4ae0cfc16c75566fcaa3a7bb9116840db1559c71fd0e961cc36 false +check_key dbfbec4d0d2433a794ad40dc0aea965b6582875805c9a7351b47377403296acd true +check_key 0a7fe09eb9342214f98b38964f72ae3c787c19e5d7e256af9216f108f88b00a3 true +check_key a82e54681475f53ced9730ee9e3a607e341014d9403f5a42f3dbdbe8fc52e842 true +check_key 4d1f90059f7895a3f89abf16162e8d69b399c417f515ccb43b83144bbe8105f6 true +check_key 94e5c5b8486b1f2ff4e98ddf3b9295787eb252ba9b408ca4d7724595861da834 false +check_key d16e3e8dfa6d33d1d2db21c651006ccddbf4ce2e556594de5a22ae433e774ae6 false +check_key a1b203ec5e36098a3af08d6077068fec57eab3a754cbb5f8192983f37191c2df false +check_key 5378bb3ec8b4e49849bd7477356ed86f40757dd1ea3cee1e5183c7e7be4c3406 false +check_key 541a4162edeb57130295441dc1cb604072d7323b6c7dffa02ea5e4fed1d2ee9e true +check_key d8e86e189edcc4b5c262c26004691edd7bd909090997f886b00ed4b6af64d547 false +check_key 18a8731d1983d1df2ce2703b4c85e7357b6356634ac1412e6c2ac33ad35f8364 false +check_key b21212eac1eb11e811022514c5041233c4a07083a5b20acd7d632a938dc627de true +check_key 50efcfac1a55e9829d89334513d6d921abeb237594174015d154512054e4f9d1 true +check_key 9c44e8bcba31ddb4e67808422e42062540742ebd73439da0ba7837bf26649ec4 true +check_key b068a4f90d5bd78fd350daa129de35e5297b0ad6be9c85c7a6f129e3760a1482 false +check_key e9df93932f0096fcf2055564457c6dc685051673a4a6cd87779924be5c4abead true +check_key eddab2fc52dac8ed12914d1eb5b0da9978662c4d35b388d64ddf8f065606acaf true +check_key 54d3e6b3f2143d9083b4c98e4c22d98f99d274228050b2dc11695bf86631e89f true +check_key 6da1d5ef1827de8bbf886623561b058032e196d17f983cbc52199b31b2acc75b true +check_key e2a2df18e2235ebd743c9714e334f415d4ca4baf7ad1b335fb45021353d5117f true +check_key f34cb7d6e861c8bfe6e15ac19de68e74ccc9b345a7b751a10a5c7f85a99dfeb6 false +check_key f36e2f5967eb56244f9e4981a831f4d19c805e31983662641fe384e68176604a true +check_key c7e2dc9e8aa6f9c23d379e0f5e3057a69b931b886bbb74ded9f660c06d457463 true +check_key b97324364941e06f2ab4f5153a368f9b07c524a89e246720099042ad9e8c1c5b false +check_key eff75c70d425f5bba0eef426e116a4697e54feefac870660d9cf24c685078d75 false +check_key 161f3cd1a5873788755437e399136bcbf51ff5534700b3a8064f822995a15d24 false +check_key 63d6d3d2c21e88b06c9ff856809572024d86c85d85d6d62a52105c0672d92e66 false +check_key 1dc19b610b293de602f43dca6c204ce304702e6dc15d2a9337da55961bd26834 false +check_key 28a16d02405f509e1cfef5236c0c5f73c3bcadcd23c8eff377253941f82769db true +check_key 682d9cc3b65d149b8c2e54d6e20101e12b7cf96be90c9458e7a69699ec0c8ed7 false +check_key 0000000000000000000000000000000000000000000000000000000000000000 true +check_key 0000000000000000000000000000000000000000000000000000000000000080 true +check_key 0100000000000000000000000000000000000000000000000000000000000000 true +check_key 0100000000000000000000000000000000000000000000000000000000000080 false +check_key 0200000000000000000000000000000000000000000000000000000000000000 false +check_key 0200000000000000000000000000000000000000000000000000000000000080 false +check_key 0300000000000000000000000000000000000000000000000000000000000000 true +check_key 0300000000000000000000000000000000000000000000000000000000000080 true +check_key 0400000000000000000000000000000000000000000000000000000000000000 true +check_key 0400000000000000000000000000000000000000000000000000000000000080 true +check_key 0500000000000000000000000000000000000000000000000000000000000000 true +check_key 0500000000000000000000000000000000000000000000000000000000000080 true +check_key 0600000000000000000000000000000000000000000000000000000000000000 true +check_key 0600000000000000000000000000000000000000000000000000000000000080 true +check_key 0700000000000000000000000000000000000000000000000000000000000000 false +check_key 0700000000000000000000000000000000000000000000000000000000000080 false +check_key 0800000000000000000000000000000000000000000000000000000000000000 false +check_key 0800000000000000000000000000000000000000000000000000000000000080 false +check_key 0900000000000000000000000000000000000000000000000000000000000000 true +check_key 0900000000000000000000000000000000000000000000000000000000000080 true +check_key 0a00000000000000000000000000000000000000000000000000000000000000 true +check_key 0a00000000000000000000000000000000000000000000000000000000000080 true +check_key 0b00000000000000000000000000000000000000000000000000000000000000 false +check_key 0b00000000000000000000000000000000000000000000000000000000000080 false +check_key 0c00000000000000000000000000000000000000000000000000000000000000 false +check_key 0c00000000000000000000000000000000000000000000000000000000000080 false +check_key 0d00000000000000000000000000000000000000000000000000000000000000 false +check_key 0d00000000000000000000000000000000000000000000000000000000000080 false +check_key 0e00000000000000000000000000000000000000000000000000000000000000 true +check_key 0e00000000000000000000000000000000000000000000000000000000000080 true +check_key 0f00000000000000000000000000000000000000000000000000000000000000 true +check_key 0f00000000000000000000000000000000000000000000000000000000000080 true +check_key 1000000000000000000000000000000000000000000000000000000000000000 true +check_key 1000000000000000000000000000000000000000000000000000000000000080 true +check_key 1100000000000000000000000000000000000000000000000000000000000000 false +check_key 1100000000000000000000000000000000000000000000000000000000000080 false +check_key 1200000000000000000000000000000000000000000000000000000000000000 true +check_key 1200000000000000000000000000000000000000000000000000000000000080 true +check_key 1300000000000000000000000000000000000000000000000000000000000000 true +check_key 1300000000000000000000000000000000000000000000000000000000000080 true +check_key daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key dcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key dcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key ddffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key ddffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key deffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key deffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key dfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key dfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key e0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key e0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key e1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key e1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key e2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key e2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key e4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key e4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key e5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key e5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key e6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key e7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key e7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key e8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key e8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key e9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key e9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key eaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key eaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff true +check_key ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f true +check_key ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key faffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key faffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_key ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_key ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false +check_torsion 0100000000000000000000000000000000000000000000000000000000000000 false +check_torsion 5866666666666666666666666666666666666666666666666666666666666666 true +check_torsion c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a false +check_torsion 0000000000000000000000000000000000000000000000000000000000000080 false +check_torsion 26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05 false +check_torsion ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f false +check_torsion 26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85 false +check_torsion c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa false +check_torsion 98519eadf35b995233b51b5cd23e9cc5a28b639b5a4af0ec903cb960d81b7819 false +check_torsion 9bad33f580df7ecc49df5342bac8145d5bedc40f573d1b067f3c4ce449689a15 false +check_torsion da99e28ba529cdde35a25fba9059e78ecaee239f99755b9b1aa4f65df00803e2 false +check_torsion 9599999999999999999999999999999999999999999999999999999999999999 false +check_torsion 55ae61520ca466adcc4ae4a32dc1633a5d749c64a5b50f136fc3469f27e487e6 false +check_torsion 5252cc0a7f208133b620acbd4537eba2a4123bf0a8c2e4f980c3b31bb69765ea false +check_torsion 13661d745ad63221ca5da0456fa618713511dc60668aa464e55b09a20ff7fc1d false +check_torsion 0e7ea1fa7226d13cecabc4455bcfacbd7161d3500f7fc035d88fdf87810ff591 true +check_torsion 29facca6fa66c12ba855e8a2b1dc3f159618b57d002a9b86293665c77bfb0063 false +check_torsion 7bc3a7ddea737685757605ca318465ea96c443a67ac6023a0dabf3b6138cabde true +check_torsion e3175b0d896b8b3f6abaf459e5152c57334f14cb1d16d84be35ae37eb95b3e42 false +check_torsion 7173c529b1dbfc197c0c78f49c73f244b84a6296e8d23622de112dcd83bf413a true +check_torsion 8e4d96422e571626a9b0fc6d19a42d0272ab6910657d5fb0603b7c0f83a17e3f false +check_torsion e26bb95a4012a6c08207e01242dbb270a5c48949f4ef998d6604e9bb869cb830 true +check_torsion 0b9446a5bfed593f7df81fedbd244d8f5a3b76b60b10667299fb1644796347cf false +check_torsion 04e1c538fb0245416b852a40a2f6269b4dd3eaf3e6fffbb817c344d9916d389a true +check_torsion 308538b9a3aa2c7ed290f507b8802f016f4747baa3ae3cbeb21b85689af1bad9 false +check_torsion 7e6929588dcc0eed55c145f6a02116c11237b10dac6ad0f6a5f6e291f72bb2ca true +check_torsion b808a048cb8674bf98a0adbc80f58fd961ff1a5d913396ec7fa3561789b22cb4 false +check_torsion c54fa69bd6b14385dea0d851cf174f573ed8d42dc38149289b3ac974979a64c0 true +check_torsion 46bf8c6159f85f60a890057a804b5bdd1b9c78d292a6cf6d936462e746496fd2 false