diff --git a/src/common/pre_download.h b/src/common/pre_download.h index 27ec9f84..6ea723b0 100644 --- a/src/common/pre_download.h +++ b/src/common/pre_download.h @@ -21,8 +21,8 @@ namespace tools }; #ifndef TESTNET - static constexpr pre_download_entry c_pre_download_mdbx = { "http://95.217.43.225/pre-download/zano_mdbx_95_900000.pak", "1c748d0f90fb1ed0af0ffe59d4b8f6046b2d0f92a8b8fe21932208829733f053", 1097493715, 2147450880 }; - static constexpr pre_download_entry c_pre_download_lmdb = { "http://95.217.43.225/pre-download/zano_lmdb_95_900000.pak", "f2d498ed7abf641824eca4ce584c756d6138d670980c1abdddcdf07343f10bfc", 1406827811, 2079617024 }; + static constexpr pre_download_entry c_pre_download_mdbx = { "http://95.217.43.225/pre-download/zano_mdbx_95_1000000.pak", "6b0bbba85bc420eaae5ec68373e528f70bffaa17fb111c796e951d06ad71e4fe", 1104150892, 2147450880 }; + static constexpr pre_download_entry c_pre_download_lmdb = { "http://95.217.43.225/pre-download/zano_lmdb_95_1000000.pak", "b4d45c727dbf1b92671f9fd1a9624e79019e890bd3d33cb71e011ab4bcb0d21e", 1450748151, 2114449408 }; #else static constexpr pre_download_entry c_pre_download_mdbx = { "", "", 0, 0 }; static constexpr pre_download_entry c_pre_download_lmdb = { "", "", 0, 0 }; diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 7b4e50dd..2ab08dc2 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,5 @@ -// Copyright (c) 2018-2020 Zano Project +// Copyright (c) 2018-2021 Zano Project +// Copyright (c) 2020-2021 sowle (val@zano.org, crypto.sowle@gmail.com) // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -341,6 +342,16 @@ int fe_isnonzero(const fe f) { s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; } +int fe_cmp(const fe a, const fe b) +{ + for (size_t i = 9; i != SIZE_MAX; --i) + { + if ((const uint32_t)a[i] < (const uint32_t)b[i]) return -1; + if ((const uint32_t)a[i] > (const uint32_t)b[i]) return 1; + } + return 0; +} + /* From fe_mul.c */ /* @@ -1234,6 +1245,96 @@ void ge_double_scalarmult_base_vartime(ge_p2 *r, const unsigned char *a, const g } } +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_base_vartime_p3(ge_p3 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + ge_p1p1 t; + ge_p3 u; + ge_p2 r_p2; + int i; + + slide(aslide, a); + slide(bslide, b); + ge_dsm_precomp(Ai, A); + + ge_p2_0(&r_p2); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) break; + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, &r_p2); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &ge_Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &ge_Bi[(-bslide[i])/2]); + } + + if (i != 0) + ge_p1p1_to_p2(&r_p2, &t); + else + ge_p1p1_to_p3(r, &t); // last step + } +} + +void ge_scalarmult_vartime_p3(ge_p3 *r, const unsigned char *a, const ge_p3 *A) { + signed char aslide[256]; + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + ge_p1p1 t; + ge_p3 u; + ge_p2 r_p2; + int i; + + slide(aslide, a); + ge_dsm_precomp(Ai, A); + + ge_p2_0(&r_p2); + ge_p3_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i]) break; + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, &r_p2); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i] / 2]); + } + else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (i != 0) + ge_p1p1_to_p2(&r_p2, &t); + else + ge_p1p1_to_p3(r, &t); // last step + } +} + + /* From ge_frombytes.c, modified */ int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) { @@ -2168,17 +2269,17 @@ void ge_fromfe_frombytes_vartime(ge_p2 *r, const unsigned char *s) { /* End fe_frombytes.c */ - fe_sq2(v, u); /* 2 * u^2 */ - fe_1(w); - fe_add(w, v, w); /* w = 2 * u^2 + 1 */ - fe_sq(x, w); /* w^2 */ - fe_mul(y, fe_ma2, v); /* -2 * A^2 * u^2 */ - fe_add(x, x, y); /* x = w^2 - 2 * A^2 * u^2 */ - fe_divpowm1(r->X, w, x); /* (w / x)^(m + 1) */ - fe_sq(y, r->X); - fe_mul(x, y, x); - fe_sub(y, w, x); - fe_copy(z, fe_ma); + fe_sq2(v, u); /* v = 2 * u^2 */ + fe_1(w); /* w = 1 */ + fe_add(w, v, w); /* w = 2 * u^2 + 1 */ + fe_sq(x, w); /* x = w^2 */ + fe_mul(y, fe_ma2, v); /* y = -A^2 * (2 * u^2) = -2 * A^2 * u^2 n.b. A = 2 * (1 - d) / (1 + d) = 486662*/ + fe_add(x, x, y); /* x = w^2 - 2 * A^2 * u^2 */ + fe_divpowm1(r->X, w, x); /* r->X = (w / x)^(m + 1) */ + fe_sq(y, r->X); /* y = (w / x)^(2*(m + 1)) */ + fe_mul(x, y, x); /* x = (w / x)^(2*(m + 1)) * (w^2 - 2 * A^2 * u^2) */ + fe_sub(y, w, x); /* y = 2 * u^2 + 1 - (w / x)^(2*(m + 1)) * (w^2 - 2 * A^2 * u^2) */ + fe_copy(z, fe_ma); /* z = -A */ if (fe_isnonzero(y)) { fe_add(y, w, x); if (fe_isnonzero(y)) { @@ -3522,6 +3623,494 @@ void sc_mul(unsigned char* s, const unsigned char* a, const unsigned char* b) s[31] = s11 >> 17; } +/* libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c */ +/* + Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + * + Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. + */ +void sc_muladd(unsigned char* s, const unsigned char* a, + const unsigned char* b, const unsigned char* c) +{ + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + + 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; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + + a10 * b5 + a11 * b4; + s16 = + a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + + carry0 = (s0 + (int64_t)(1L << 20)) >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t)1L << 21); + carry2 = (s2 + (int64_t)(1L << 20)) >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t)1L << 21); + carry4 = (s4 + (int64_t)(1L << 20)) >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t)1L << 21); + carry6 = (s6 + (int64_t)(1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t)1L << 21); + carry8 = (s8 + (int64_t)(1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t)1L << 21); + carry10 = (s10 + (int64_t)(1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t)1L << 21); + carry12 = (s12 + (int64_t)(1L << 20)) >> 21; + s13 += carry12; + s12 -= carry12 * ((uint64_t)1L << 21); + carry14 = (s14 + (int64_t)(1L << 20)) >> 21; + s15 += carry14; + s14 -= carry14 * ((uint64_t)1L << 21); + carry16 = (s16 + (int64_t)(1L << 20)) >> 21; + s17 += carry16; + s16 -= carry16 * ((uint64_t)1L << 21); + carry18 = (s18 + (int64_t)(1L << 20)) >> 21; + s19 += carry18; + s18 -= carry18 * ((uint64_t)1L << 21); + carry20 = (s20 + (int64_t)(1L << 20)) >> 21; + s21 += carry20; + s20 -= carry20 * ((uint64_t)1L << 21); + carry22 = (s22 + (int64_t)(1L << 20)) >> 21; + s23 += carry22; + s22 -= carry22 * ((uint64_t)1L << 21); + + carry1 = (s1 + (int64_t)(1L << 20)) >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t)1L << 21); + carry3 = (s3 + (int64_t)(1L << 20)) >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t)1L << 21); + carry5 = (s5 + (int64_t)(1L << 20)) >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t)1L << 21); + carry7 = (s7 + (int64_t)(1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t)1L << 21); + carry9 = (s9 + (int64_t)(1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t)1L << 21); + carry11 = (s11 + (int64_t)(1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t)1L << 21); + carry13 = (s13 + (int64_t)(1L << 20)) >> 21; + s14 += carry13; + s13 -= carry13 * ((uint64_t)1L << 21); + carry15 = (s15 + (int64_t)(1L << 20)) >> 21; + s16 += carry15; + s15 -= carry15 * ((uint64_t)1L << 21); + carry17 = (s17 + (int64_t)(1L << 20)) >> 21; + s18 += carry17; + s17 -= carry17 * ((uint64_t)1L << 21); + carry19 = (s19 + (int64_t)(1L << 20)) >> 21; + s20 += carry19; + s19 -= carry19 * ((uint64_t)1L << 21); + carry21 = (s21 + (int64_t)(1L << 20)) >> 21; + s22 += carry21; + s21 -= carry21 * ((uint64_t)1L << 21); + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (int64_t)(1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t)1L << 21); + carry8 = (s8 + (int64_t)(1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t)1L << 21); + carry10 = (s10 + (int64_t)(1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t)1L << 21); + carry12 = (s12 + (int64_t)(1L << 20)) >> 21; + s13 += carry12; + s12 -= carry12 * ((uint64_t)1L << 21); + carry14 = (s14 + (int64_t)(1L << 20)) >> 21; + s15 += carry14; + s14 -= carry14 * ((uint64_t)1L << 21); + carry16 = (s16 + (int64_t)(1L << 20)) >> 21; + s17 += carry16; + s16 -= carry16 * ((uint64_t)1L << 21); + + carry7 = (s7 + (int64_t)(1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t)1L << 21); + carry9 = (s9 + (int64_t)(1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t)1L << 21); + carry11 = (s11 + (int64_t)(1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t)1L << 21); + carry13 = (s13 + (int64_t)(1L << 20)) >> 21; + s14 += carry13; + s13 -= carry13 * ((uint64_t)1L << 21); + carry15 = (s15 + (int64_t)(1L << 20)) >> 21; + s16 += carry15; + s15 -= carry15 * ((uint64_t)1L << 21); + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (int64_t)(1L << 20)) >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t)1L << 21); + carry2 = (s2 + (int64_t)(1L << 20)) >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t)1L << 21); + carry4 = (s4 + (int64_t)(1L << 20)) >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t)1L << 21); + carry6 = (s6 + (int64_t)(1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t)1L << 21); + carry8 = (s8 + (int64_t)(1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t)1L << 21); + carry10 = (s10 + (int64_t)(1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t)1L << 21); + + carry1 = (s1 + (int64_t)(1L << 20)) >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t)1L << 21); + carry3 = (s3 + (int64_t)(1L << 20)) >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t)1L << 21); + carry5 = (s5 + (int64_t)(1L << 20)) >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t)1L << 21); + carry7 = (s7 + (int64_t)(1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t)1L << 21); + carry9 = (s9 + (int64_t)(1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t)1L << 21); + carry11 = (s11 + (int64_t)(1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t)1L << 21); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t)1L << 21); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t)1L << 21); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t)1L << 21); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t)1L << 21); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t)1L << 21); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t)1L << 21); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t)1L << 21); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t)1L << 21); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t)1L << 21); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t)1L << 21); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t)1L << 21); + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t)1L << 21); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t)1L << 21); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t)1L << 21); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t)1L << 21); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t)1L << 21); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t)1L << 21); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t)1L << 21); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t)1L << 21); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t)1L << 21); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t)1L << 21); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t)1L << 21); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t)1L << 21); + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 * ((uint64_t)1 << 5)); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 * ((uint64_t)1 << 2)); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 * ((uint64_t)1 << 7)); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 * ((uint64_t)1 << 4)); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 * ((uint64_t)1 << 1)); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 * ((uint64_t)1 << 6)); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 * ((uint64_t)1 << 3)); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 * ((uint64_t)1 << 5)); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 * ((uint64_t)1 << 2)); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 * ((uint64_t)1 << 7)); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + + // out = z ^ -1 (= z ^ (L - 2) according to Fermat little theorem) void sc_invert(unsigned char* out, const unsigned char* z) { @@ -3744,19 +4333,123 @@ void ge_p2_to_p3(ge_p3 *r, const ge_p2 *t) /* - In: ge_bytes -- points to 32 bytes of data - Out: res = Hp(ge_bytes) - where Hp = 8 * ge_fromfe_frombytes_vartime(cn_fast_hash(ge_bytes)) + In: data -- points to 'size' bytes of data + Out: res = Hp(data) + where Hp = 8 * ge_fromfe_frombytes_vartime(cn_fast_hash(data)) */ -void ge_bytes_hash_to_ec(ge_p3 *res, const unsigned char *ge_bytes) +void ge_bytes_hash_to_ec(ge_p3 *res, const void *data, size_t size) { unsigned char h[HASH_SIZE]; ge_p2 point; ge_p1p1 point2; - cn_fast_hash(ge_bytes, 32, (char*)h); + cn_fast_hash(data, size, (char*)h); ge_fromfe_frombytes_vartime(&point, &h[0]); /*ge_p2_to_p3(res, &point); -- can be used to avoid multiplication by 8 for debugging */ ge_mul8(&point2, &point); ge_p1p1_to_p3(res, &point2); } + +void ge_bytes_hash_to_ec_32(ge_p3 *res, const unsigned char *ge_bytes) +{ + ge_bytes_hash_to_ec(res, ge_bytes, 32); +} + +void ge_mul8_p3(ge_p3 *r, const ge_p3 *t) +{ + ge_p1p1 p1; + ge_p2 p2; + + // TODO: consider removing the following copy, replace &p2 by (ge_p2*)t as it's ugly but possible + ge_p3_to_p2(&p2, t); // copying + + ge_p2_dbl(&p1, &p2); // 3 fe_sq, 1 fe_sq2, 5 fe_add/sub + ge_p1p1_to_p2(&p2, &p1); // 3 fe_mul + ge_p2_dbl(&p1, &p2); // 3 fe_sq, 1 fe_sq2, 5 fe_add/sub + ge_p1p1_to_p2(&p2, &p1); // 3 fe_mul + ge_p2_dbl(&p1, &p2); // 3 fe_sq, 1 fe_sq2, 5 fe_add/sub + + ge_p1p1_to_p3(r, &p1); // 4 fe_mul +} + + +// returns the most non-zero index of r +int slide_v2(signed char *r, const unsigned char *a) +{ + int i; + int b; + int k; + int nzi = 0; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) { + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; r[i + b] = 0; + } + else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } + else + break; + } + } + if (r[i]) + nzi = i; + } + } + + return nzi; +} + +void ge_scalarmult_vartime_p3_v2(ge_p3 *r, const unsigned char *a, const ge_p3 *A) +{ + signed char aslide[256]; + ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */ + ge_p1p1 t; + ge_p3 u; + ge_p2 r_p2; + int i; + + i = slide_v2(aslide, a); + + if (i == 0) + { + ge_p3_0(r); + return; + } + + ge_dsm_precomp(Ai, A); + ge_p2_0(&r_p2); + + for (; i >= 0; --i) + { + ge_p2_dbl(&t, &r_p2); + if (aslide[i] > 0) + { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i] / 2]); + } + else if (aslide[i] < 0) + { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + if (i != 0) + ge_p1p1_to_p2(&r_p2, &t); + else + ge_p1p1_to_p3(r, &t); + } +} diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index e01735dd..6fb6917c 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,8 +1,10 @@ +// Copyright (c) 2018-2021 Zano Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once +#include // size_t /* From fe.h */ @@ -104,11 +106,17 @@ void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *); void ge_scalarmult_p3(ge_p3 *, const unsigned char *, const ge_p3 *); void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp); void ge_mul8(ge_p1p1 *, const ge_p2 *); +void ge_mul8_p3(ge_p3 *, const ge_p3 *); void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *); -void ge_bytes_hash_to_ec(ge_p3 *, const unsigned char *); +void ge_p2_to_p3(ge_p3 *r, const ge_p2 *t); +void ge_bytes_hash_to_ec(ge_p3 *, const void *, size_t); +void ge_bytes_hash_to_ec_32(ge_p3 *, const unsigned char *); void ge_p3_0(ge_p3 *h); void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *); +void ge_double_scalarmult_base_vartime_p3(ge_p3 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); +void ge_scalarmult_vartime_p3(ge_p3 *r, const unsigned char *a, const ge_p3 *A); +void ge_scalarmult_vartime_p3_v2(ge_p3 *r, const unsigned char *a, const ge_p3 *A); extern const fe fe_ma2; extern const fe fe_ma; @@ -123,12 +131,14 @@ void sc_add(unsigned char *, const unsigned char *, const unsigned char *); void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); void sc_mul(unsigned char *, const unsigned char *, const unsigned char *); +void sc_muladd(unsigned char* s, const unsigned char* a, const unsigned char* b, const unsigned char* c); int sc_check(const unsigned char *); int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ void sc_invert(unsigned char*, const unsigned char*); void fe_sq(fe h, const fe f); int fe_isnonzero(const fe f); +int fe_cmp(const fe a, const fe b); void fe_mul(fe, const fe, const fe); void fe_frombytes(fe h, const unsigned char *s); void fe_invert(fe out, const fe z); diff --git a/src/crypto/crypto-sugar.cpp b/src/crypto/crypto-sugar.cpp new file mode 100644 index 00000000..0e923359 --- /dev/null +++ b/src/crypto/crypto-sugar.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2020-2021 Zano Project +// Copyright (c) 2020-2021 sowle (val@zano.org, crypto.sowle@gmail.com) +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +// Note: This file originates from tests/functional_tests/crypto_tests.cpp + +#include "crypto-sugar.h" + +namespace crypto +{ + + const point_g_t c_point_G; + + const scalar_t c_scalar_1 = { 1 }; + const scalar_t c_scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; + const scalar_t c_scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; + const scalar_t c_scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; + const scalar_t c_scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; + const scalar_t c_scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff }; + const scalar_t c_scalar_1div8 = { 0x6106e529e2dc2f79, 0x7d39db37d1cdad0, 0x0, 0x600000000000000 }; + + const point_t c_point_H = { 0x05087c1f5b9b32d6, 0x00547595f445c3b5, 0x764df64578552f2a, 0x8a49a651e0e0da45 }; // == Hp(G), this is being checked in bpp_basics + const point_t c_point_0 = point_t(point_t::tag_zero()); + +} // namespace crypto diff --git a/src/crypto/crypto-sugar.h b/src/crypto/crypto-sugar.h new file mode 100644 index 00000000..14c168e8 --- /dev/null +++ b/src/crypto/crypto-sugar.h @@ -0,0 +1,821 @@ +// Copyright (c) 2020-2021 Zano Project +// Copyright (c) 2020-2021 sowle (val@zano.org, crypto.sowle@gmail.com) +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +// Note: This file originates from tests/functional_tests/crypto_tests.cpp +#pragma once +#include +#include +#include "crypto.h" +#include "epee/include/string_tools.h" + +namespace crypto +{ + extern "C" + { +#include "crypto/crypto-ops.h" + } // extern "C" + + + + // + // Helpers + // + + template + std::string pod_to_hex_reversed(const pod_t &h) + { + constexpr char hexmap[] = "0123456789abcdef"; + const unsigned char* data = reinterpret_cast(&h); + size_t len = sizeof h; + + std::string s(len * 2, ' '); + for (size_t i = 0; i < len; ++i) { + s[2 * i] = hexmap[data[len - 1 - i] >> 4]; + s[2 * i + 1] = hexmap[data[len - 1 - i] & 0x0F]; + } + + return s; + } + + template + std::string pod_to_hex(const pod_t &h) + { + constexpr char hexmap[] = "0123456789abcdef"; + const unsigned char* data = reinterpret_cast(&h); + size_t len = sizeof h; + + std::string s(len * 2, ' '); + for (size_t i = 0; i < len; ++i) { + s[2 * i] = hexmap[data[i] >> 4]; + s[2 * i + 1] = hexmap[data[i] & 0x0F]; + } + + return s; + } + + template + std::string pod_to_hex_comma_separated_bytes(const pod_t &h) + { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + size_t len = sizeof h; + const unsigned char* p = (const unsigned char*)&h; + for (size_t i = 0; i < len; ++i) + { + ss << "0x" << std::setw(2) << static_cast(p[i]); + if (i + 1 != len) + ss << ", "; + } + return ss.str(); + } + + template + std::string pod_to_hex_comma_separated_uint64(const pod_t &h) + { + static_assert((sizeof h) % 8 == 0, "size of h should be a multiple of 64 bit"); + size_t len = (sizeof h) / 8; + std::stringstream ss; + ss << std::hex << std::setfill('0'); + const uint64_t* p = (const uint64_t*)&h; + for (size_t i = 0; i < len; ++i) + { + ss << "0x" << std::setw(16) << static_cast(p[i]); + if (i + 1 != len) + ss << ", "; + } + return ss.str(); + } + + + // + // scalar_t - holds a 256-bit scalar, normally in [0..L-1] + // + struct alignas(32) scalar_t + { + union + { + uint64_t m_u64[4]; + unsigned char m_s[32]; + }; + + scalar_t() + {} + + // won't check scalar range validity (< L) + scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) + { + m_u64[0] = a0; + m_u64[1] = a1; + m_u64[2] = a2; + m_u64[3] = a3; + } + + // won't check scalar range validity (< L) + scalar_t(const unsigned char(&v)[32]) + { + memcpy(m_s, v, 32); + } + + // won't check secret key validity (sk < L) + scalar_t(const crypto::secret_key& sk) + { + from_secret_key(sk); + } + + // copy data and reduce + scalar_t(const crypto::hash& hash) + { + m_u64[0] = ((uint64_t*)&hash)[0]; + m_u64[1] = ((uint64_t*)&hash)[1]; + m_u64[2] = ((uint64_t*)&hash)[2]; + m_u64[3] = ((uint64_t*)&hash)[3]; + sc_reduce32(&m_s[0]); + } + + scalar_t(uint64_t v) + { + zero(); + m_u64[0] = v; + // do not need to call reduce as 2^64 < L + } + + // copy at most 256 bits (32 bytes) and reduce + template + explicit scalar_t(const boost::multiprecision::number& bigint) + { + zero(); + unsigned int bytes_to_copy = bigint.backend().size() * bigint.backend().limb_bits / 8; + if (bytes_to_copy > sizeof *this) + bytes_to_copy = sizeof *this; + memcpy(&m_s[0], bigint.backend().limbs(), bytes_to_copy); + sc_reduce32(&m_s[0]); + } + + unsigned char* data() + { + return &m_s[0]; + } + + const unsigned char* data() const + { + return &m_s[0]; + } + + crypto::secret_key &as_secret_key() + { + return *(crypto::secret_key*)&m_s[0]; + } + + const crypto::secret_key& as_secret_key() const + { + return *(const crypto::secret_key*)&m_s[0]; + } + + operator crypto::secret_key() const + { + crypto::secret_key result; + memcpy(result.data, &m_s, sizeof result.data); + return result; + } + + void from_secret_key(const crypto::secret_key& sk) + { + uint64_t *p_sk64 = (uint64_t*)&sk; + m_u64[0] = p_sk64[0]; + m_u64[1] = p_sk64[1]; + m_u64[2] = p_sk64[2]; + m_u64[3] = p_sk64[3]; + // assuming secret key is correct (< L), so we don't need to call reduce here + } + + void zero() + { + m_u64[0] = 0; + m_u64[1] = 0; + m_u64[2] = 0; + m_u64[3] = 0; + } + + // genrate 0 <= x < L + static scalar_t random() + { + scalar_t result; + result.make_random(); + return result; + } + + // genrate 0 <= x < L + void make_random() + { + unsigned char tmp[64]; + crypto::generate_random_bytes(64, tmp); + sc_reduce(tmp); + memcpy(&m_s, tmp, sizeof m_s); + + /* // for tests + int x[8] = { rand() }; + crypto::cn_fast_hash(&x, sizeof x, *(crypto::hash*)this); + sc_reduce32(m_s); + */ + } + + bool is_zero() const + { + return sc_isnonzero(&m_s[0]) == 0; + } + + bool is_reduced() const + { + return sc_check(&m_s[0]) == 0; + } + + scalar_t operator+(const scalar_t& v) const + { + scalar_t result; + sc_add(&result.m_s[0], &m_s[0], &v.m_s[0]); + return result; + } + + scalar_t& operator+=(const scalar_t& v) + { + sc_add(&m_s[0], &m_s[0], &v.m_s[0]); + return *this; + } + + scalar_t operator-(const scalar_t& v) const + { + scalar_t result; + sc_sub(&result.m_s[0], &m_s[0], &v.m_s[0]); + return result; + } + + scalar_t& operator-=(const scalar_t& v) + { + sc_sub(&m_s[0], &m_s[0], &v.m_s[0]); + return *this; + } + + scalar_t operator*(const scalar_t& v) const + { + scalar_t result; + sc_mul(result.m_s, m_s, v.m_s); + return result; + } + + scalar_t& operator*=(const scalar_t& v) + { + sc_mul(m_s, m_s, v.m_s); + return *this; + } + + /* + I think it has bad symantic (operator-like), consider rename/reimplement -- sowle + */ + // returns this * b + c + scalar_t muladd(const scalar_t& b, const scalar_t& c) const + { + scalar_t result; + sc_muladd(result.m_s, m_s, b.m_s, c.m_s); + return result; + } + + // returns this = a * b + c + scalar_t& assign_muladd(const scalar_t& a, const scalar_t& b, const scalar_t& c) + { + sc_muladd(m_s, a.m_s, b.m_s, c.m_s); + return *this; + } + + scalar_t reciprocal() const + { + scalar_t result; + sc_invert(result.m_s, m_s); + return result; + } + + scalar_t operator/(const scalar_t& v) const + { + return operator*(v.reciprocal()); + } + + scalar_t& operator/=(const scalar_t& v) + { + scalar_t reciprocal; + sc_invert(&reciprocal.m_s[0], &v.m_s[0]); + sc_mul(&m_s[0], &m_s[0], &reciprocal.m_s[0]); + return *this; + } + + bool operator==(const scalar_t& rhs) const + { + return + m_u64[0] == rhs.m_u64[0] && + m_u64[1] == rhs.m_u64[1] && + m_u64[2] == rhs.m_u64[2] && + m_u64[3] == rhs.m_u64[3]; + } + + bool operator!=(const scalar_t& rhs) const + { + return + m_u64[0] != rhs.m_u64[0] || + m_u64[1] != rhs.m_u64[1] || + m_u64[2] != rhs.m_u64[2] || + m_u64[3] != rhs.m_u64[3]; + } + + bool operator<(const scalar_t& rhs) const + { + if (m_u64[3] < rhs.m_u64[3]) return true; + if (m_u64[3] > rhs.m_u64[3]) return false; + if (m_u64[2] < rhs.m_u64[2]) return true; + if (m_u64[2] > rhs.m_u64[2]) return false; + if (m_u64[1] < rhs.m_u64[1]) return true; + if (m_u64[1] > rhs.m_u64[1]) return false; + if (m_u64[0] < rhs.m_u64[0]) return true; + if (m_u64[0] > rhs.m_u64[0]) return false; + return false; + } + + bool operator>(const scalar_t& rhs) const + { + if (m_u64[3] < rhs.m_u64[3]) return false; + if (m_u64[3] > rhs.m_u64[3]) return true; + if (m_u64[2] < rhs.m_u64[2]) return false; + if (m_u64[2] > rhs.m_u64[2]) return true; + if (m_u64[1] < rhs.m_u64[1]) return false; + if (m_u64[1] > rhs.m_u64[1]) return true; + if (m_u64[0] < rhs.m_u64[0]) return false; + if (m_u64[0] > rhs.m_u64[0]) return true; + return false; + } + + friend std::ostream& operator<<(std::ostream& ss, const scalar_t &v) + { + return ss << "0x" << pod_to_hex_reversed(v); + } + + std::string to_string_as_hex_number() const + { + return pod_to_hex_reversed(*this); + } + + std::string to_string_as_secret_key() const + { + return epee::string_tools::pod_to_hex(*this); + } + + template + MP_type as_boost_mp_type() const + { + MP_type result = 0; + static_assert(sizeof result >= sizeof *this, "size missmatch"); // to avoid using types less than uint256_t + unsigned int sz = sizeof *this / sizeof(boost::multiprecision::limb_type); + result.backend().resize(sz, sz); + memcpy(result.backend().limbs(), &m_s[0], sizeof *this); + result.backend().normalize(); + return result; + } + + }; // struct scalar_t + + + // + // + // + struct point_t + { + struct tag_zero {}; + + // A point(x, y) is represented in extended homogeneous coordinates (X, Y, Z, T) + // with x = X / Z, y = Y / Z, x * y = T / Z. + ge_p3 m_p3; + + point_t() + { + } + + explicit point_t(const crypto::public_key& pk) + { + if (!from_public_key(pk)) + zero(); + } + + point_t(const unsigned char(&v)[32]) + { + static_assert(sizeof(crypto::public_key) == sizeof v, "size missmatch"); + if (!from_public_key(*(const crypto::public_key*)v)) + zero(); + } + + point_t(const uint64_t(&v)[4]) + { + static_assert(sizeof(crypto::public_key) == sizeof v, "size missmatch"); + if (!from_public_key(*(const crypto::public_key*)v)) + zero(); + } + + point_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) + { + crypto::public_key pk; + ((uint64_t*)&pk)[0] = a0; + ((uint64_t*)&pk)[1] = a1; + ((uint64_t*)&pk)[2] = a2; + ((uint64_t*)&pk)[3] = a3; + + if (!from_public_key(pk)) + zero(); + } + + explicit point_t(tag_zero&&) + { + zero(); + } + + void zero() + { + ge_p3_0(&m_p3); + } + + bool is_zero() const + { + // (0, 1) ~ (0, z, z, 0) + return fe_isnonzero(m_p3.X) * fe_cmp(m_p3.Y, m_p3.Z) == 0; + } + + bool from_public_key(const crypto::public_key& pk) + { + return ge_frombytes_vartime(&m_p3, reinterpret_cast(&pk)) == 0; + } + + bool from_key_image(const crypto::key_image& ki) + { + return ge_frombytes_vartime(&m_p3, reinterpret_cast(&ki)) == 0; + } + + bool from_string(const std::string& str) + { + crypto::public_key pk; + if (!epee::string_tools::parse_tpod_from_hex_string(str, pk)) + return false; + return from_public_key(pk); + } + + crypto::public_key to_public_key() const + { + crypto::public_key result; + ge_p3_tobytes((unsigned char*)&result, &m_p3); + return result; + } + + crypto::key_image to_key_image() const + { + crypto::key_image result; + ge_p3_tobytes((unsigned char*)&result, &m_p3); + return result; + } + + point_t operator+(const point_t& rhs) const + { + point_t result; + ge_cached rhs_c; + ge_p1p1 t; + ge_p3_to_cached(&rhs_c, &rhs.m_p3); + ge_add(&t, &m_p3, &rhs_c); + ge_p1p1_to_p3(&result.m_p3, &t); + return result; + } + + point_t& operator+=(const point_t& rhs) + { + ge_cached rhs_c; + ge_p1p1 t; + ge_p3_to_cached(&rhs_c, &rhs.m_p3); + ge_add(&t, &m_p3, &rhs_c); + ge_p1p1_to_p3(&m_p3, &t); + return *this; + } + + point_t operator-(const point_t& rhs) const + { + point_t result; + ge_cached rhs_c; + ge_p1p1 t; + ge_p3_to_cached(&rhs_c, &rhs.m_p3); + ge_sub(&t, &m_p3, &rhs_c); + ge_p1p1_to_p3(&result.m_p3, &t); + return result; + } + + point_t& operator-=(const point_t& rhs) + { + ge_cached rhs_c; + ge_p1p1 t; + ge_p3_to_cached(&rhs_c, &rhs.m_p3); + ge_sub(&t, &m_p3, &rhs_c); + ge_p1p1_to_p3(&m_p3, &t); + return *this; + } + + friend point_t operator*(const scalar_t& lhs, const point_t& rhs) + { + point_t result; + ge_scalarmult_p3(&result.m_p3, reinterpret_cast(&lhs), &rhs.m_p3); + return result; + } + + friend point_t operator/(const point_t& lhs, const scalar_t& rhs) + { + point_t result; + scalar_t reciprocal; + sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]); + ge_scalarmult_p3(&result.m_p3, &reciprocal.m_s[0], &lhs.m_p3); + return result; + } + + point_t& modify_mul8() + { + ge_mul8_p3(&m_p3, &m_p3); + return *this; + } + + // returns a * this + G + point_t mul_plus_G(const scalar_t& a) const + { + static const unsigned char one[32] = { 1 }; + static_assert(sizeof one == sizeof(crypto::ec_scalar), "size missmatch"); + + point_t result; + ge_double_scalarmult_base_vartime_p3(&result.m_p3, &a.m_s[0], &m_p3, &one[0]); + return result; + } + + // returns a * this + b * G + point_t mul_plus_G(const scalar_t& a, const scalar_t& b) const + { + point_t result; + ge_double_scalarmult_base_vartime_p3(&result.m_p3, &a.m_s[0], &m_p3, &b.m_s[0]); + return result; + } + + // *this = a * A + b * G + void assign_mul_plus_G(const scalar_t& a, const point_t& A, const scalar_t& b) + { + ge_double_scalarmult_base_vartime_p3(&m_p3, &a.m_s[0], &A.m_p3, &b.m_s[0]); + } + + friend bool operator==(const point_t& lhs, const point_t& rhs) + { + // convert to xy form, then compare components (because (x, y, z, t) representation is not unique) + fe lrecip, lx, ly; + fe rrecip, rx, ry; + + fe_invert(lrecip, lhs.m_p3.Z); + fe_invert(rrecip, rhs.m_p3.Z); + + fe_mul(lx, lhs.m_p3.X, lrecip); + fe_mul(rx, rhs.m_p3.X, rrecip); + if (memcmp(&lx, &rx, sizeof lx) != 0) + return false; + + fe_mul(ly, lhs.m_p3.Y, lrecip); + fe_mul(ry, rhs.m_p3.Y, rrecip); + if (memcmp(&ly, &ry, sizeof ly) != 0) + return false; + + return true; + }; + + friend std::ostream& operator<<(std::ostream& ss, const point_t &v) + { + crypto::public_key pk = v.to_public_key(); + return ss << epee::string_tools::pod_to_hex(pk); + } + + operator std::string() const + { + crypto::public_key pk = to_public_key(); + return epee::string_tools::pod_to_hex(pk); + } + + std::string to_string() const + { + crypto::public_key pk = to_public_key(); + return epee::string_tools::pod_to_hex(pk); + } + + std::string to_hex_comma_separated_bytes_str() const + { + crypto::public_key pk = to_public_key(); + return pod_to_hex_comma_separated_bytes(pk); + } + + std::string to_hex_comma_separated_uint64_str() const + { + crypto::public_key pk = to_public_key(); + return pod_to_hex_comma_separated_uint64(pk); + } + + }; // struct point_t + + + // + // point_g_t -- special type for curve's base point + // + struct point_g_t : public point_t + { + point_g_t() + { + scalar_t one(1); + ge_scalarmult_base(&m_p3, &one.m_s[0]); + } + + friend point_t operator*(const scalar_t& lhs, const point_g_t&) + { + point_t result; + ge_scalarmult_base(&result.m_p3, &lhs.m_s[0]); + return result; + } + + friend point_t operator/(const point_g_t&, const scalar_t& rhs) + { + point_t result; + scalar_t reciprocal; + sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]); + ge_scalarmult_base(&result.m_p3, &reciprocal.m_s[0]); + return result; + } + + static_assert(sizeof(crypto::public_key) == 32, "size error"); + + }; // struct point_g_t + + + // + // Global constants + // + + extern const point_g_t c_point_G; + + extern const scalar_t c_scalar_1; + extern const scalar_t c_scalar_L; + extern const scalar_t c_scalar_Lm1; + extern const scalar_t c_scalar_P; + extern const scalar_t c_scalar_Pm1; + extern const scalar_t c_scalar_256m1; + extern const scalar_t c_scalar_1div8; + + extern const point_t c_point_H; + extern const point_t c_point_0; + + // + // hash functions' helper + // + struct hash_helper_t + { + static scalar_t hs(const scalar_t& s) + { + return scalar_t(crypto::cn_fast_hash(s.data(), sizeof s)); // will reduce mod L + } + + static scalar_t hs(const void* data, size_t size) + { + return scalar_t(crypto::cn_fast_hash(data, size)); // will reduce mod L + } + + static scalar_t hs(const std::string& str) + { + return scalar_t(crypto::cn_fast_hash(str.c_str(), str.size())); // will reduce mod L + } + + struct hs_t + { + hs_t() + { + static_assert(sizeof(scalar_t) == sizeof(crypto::public_key), "unexpected size of data"); + } + + void reserve(size_t elements_count) + { + m_elements.reserve(elements_count); + } + + void clear() + { + m_elements.clear(); + } + + void add_scalar(const scalar_t& scalar) + { + m_elements.emplace_back(scalar); + } + + void add_point(const point_t& point) + { + m_elements.emplace_back(point.to_public_key()); + + // faster? + /* static_assert(sizeof point.m_p3 == 5 * sizeof(item_t), "size missmatch"); + const item_t *p = (item_t*)&point.m_p3; + m_elements.emplace_back(p[0]); + m_elements.emplace_back(p[1]); + m_elements.emplace_back(p[2]); + m_elements.emplace_back(p[3]); + m_elements.emplace_back(p[4]); */ + } + + void add_pub_key(const crypto::public_key& pk) + { + m_elements.emplace_back(pk); + } + + void add_points_array(const std::vector& points_array) + { + for (size_t i = 0, size = points_array.size(); i < size; ++i) + add_point(points_array[i]); + } + + void add_pub_keys_array(const std::vector& pub_keys_array) + { + for (size_t i = 0, size = pub_keys_array.size(); i < size; ++i) + m_elements.emplace_back(pub_keys_array[i]); + } + + void add_key_images_array(const std::vector& key_image_array) + { + for (size_t i = 0, size = key_image_array.size(); i < size; ++i) + m_elements.emplace_back(key_image_array[i]); + } + + scalar_t calc_hash(bool clear = true) + { + size_t data_size_bytes = m_elements.size() * sizeof(item_t); + crypto::hash hash; + crypto::cn_fast_hash(m_elements.data(), data_size_bytes, hash); + if (clear) + this->clear(); + return scalar_t(hash); // this will reduce to L + } + + union item_t + { + item_t(const scalar_t& scalar) : scalar(scalar) {} + item_t(const crypto::public_key& pk) : pk(pk) {} + item_t(const crypto::key_image& ki) : ki(ki) {} + scalar_t scalar; + crypto::public_key pk; + crypto::key_image ki; + }; + + std::vector m_elements; + }; + + static scalar_t hs(const scalar_t& s, const std::vector& ps0, const std::vector& ps1) + { + hs_t hs_calculator; + hs_calculator.add_scalar(s); + hs_calculator.add_points_array(ps0); + hs_calculator.add_points_array(ps1); + return hs_calculator.calc_hash(); + } + + static scalar_t hs(const crypto::hash& s, const std::vector& ps0, const std::vector& ps1) + { + static_assert(sizeof(crypto::hash) == sizeof(scalar_t), "size missmatch"); + hs_t hs_calculator; + hs_calculator.add_scalar(*reinterpret_cast(&s)); + hs_calculator.add_pub_keys_array(ps0); + hs_calculator.add_key_images_array(ps1); + return hs_calculator.calc_hash(); + } + + static scalar_t hs(const std::vector& ps0, const std::vector& ps1) + { + hs_t hs_calculator; + hs_calculator.add_points_array(ps0); + hs_calculator.add_points_array(ps1); + return hs_calculator.calc_hash(); + } + + static point_t hp(const point_t& p) + { + point_t result; + crypto::public_key pk = p.to_public_key(); + + ge_bytes_hash_to_ec_32(&result.m_p3, (const unsigned char*)&pk); + + return result; + } + + static point_t hp(const crypto::public_key& p) + { + point_t result; + ge_bytes_hash_to_ec_32(&result.m_p3, (const unsigned char*)&p); + return result; + } + }; // hash_helper_t struct + + +} // namespace crypto diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 8c51959f..a1e0c64c 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -413,10 +413,10 @@ POP_VS_WARNINGS if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { return false; } - ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); + ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); // L_i = r_i * G + c_i * P_i ge_tobytes(&buf->ab[i].a, &tmp2); hash_to_ec(*pubs[i], tmp3); - ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); + ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); // R_i = r_i * Hp(P_i) + c_i * I ge_tobytes(&buf->ab[i].b, &tmp2); sc_add(&sum, &sum, &sig[i].c); } diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 1f156b1b..fcc06d14 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -52,8 +52,8 @@ void hash_process(union hash_state *state, const uint8_t *buf, size_t count); #define HASH_DATA_AREA 136 +void cn_fast_hash_old(const void *data, size_t length, char *hash); void cn_fast_hash(const void *data, size_t length, char *hash); -//void cn_slow_hash(const void *data, size_t length, char *hash); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 219c060e..e9d35a68 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,3 +1,4 @@ +// Copyright (c) 2020-2021 Zano project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -17,8 +18,14 @@ void hash_process(union hash_state *state, const uint8_t *buf, size_t count) { keccak1600(buf, (int)count, (uint8_t*)state); } -void cn_fast_hash(const void *data, size_t length, char *hash) { +void cn_fast_hash_old(const void *data, size_t length, char *hash) +{ union hash_state state; hash_process(&state, data, length); memcpy(hash, &state, HASH_SIZE); } + +void cn_fast_hash(const void *data, size_t length, char *hash) +{ + keccak(data, (int)length, hash, HASH_SIZE); +} diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index b6ede7e7..dee2a49d 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -42,6 +42,7 @@ public: m_cmd_binder.set_handler("print_bc_outs_stats", boost::bind(&daemon_commands_handler::print_bc_outs_stats, this, _1)); m_cmd_binder.set_handler("print_block", boost::bind(&daemon_commands_handler::print_block, this, _1), "Print block, print_block | "); m_cmd_binder.set_handler("print_block_info", boost::bind(&daemon_commands_handler::print_block_info, this, _1), "Print block info, print_block | "); + m_cmd_binder.set_handler("print_tx_prun_info", boost::bind(&daemon_commands_handler::print_tx_prun_info, this, _1), "Print tx prunning info"); m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_commands_handler::print_tx, this, _1), "Print transaction, print_tx "); m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_commands_handler::start_mining, this, _1), "Start mining for specified address, start_mining [threads=1]"); m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_commands_handler::stop_mining, this, _1), "Stop mining"); @@ -630,6 +631,56 @@ private: return true; } //-------------------------------------------------------------------------------- + bool print_tx_prun_info(const std::vector& arg) + { + currency::blockchain_storage& bcs = m_srv.get_payload_object().get_core().get_blockchain_storage(); + + size_t txs = 0; + size_t pruned_txs = 0; + size_t signatures = 0; + size_t attachments = 0; + size_t blocks = 0; + + uint64_t last_block_height = bcs.get_top_block_height(); + + LOG_PRINT_MAGENTA("start getting stats from 0 to " << last_block_height << " block, please wait ...", LOG_LEVEL_0); + + for (uint64_t height = 0; height <= last_block_height; height++, blocks++) + { + currency::block_extended_info bei = AUTO_VAL_INIT(bei); + bool r = bcs.get_block_extended_info_by_height(height, bei); + if (!r) + { + LOG_PRINT_RED("Failed to get block #" << height, LOG_LEVEL_0); + break; + } + + for (const auto& h : bei.bl.tx_hashes) + { + auto ptx = bcs.get_tx(h); + CHECK_AND_ASSERT_MES(ptx != nullptr, false, "failed to find transaction " << h << " in blockchain index, in block on height = " << height); + + if (ptx->signatures.size() == 0) + pruned_txs += 1; + + txs += 1; + signatures += ptx->signatures.size(); + attachments += ptx->attachment.size(); + } + } + + LOG_PRINT_MAGENTA(ENDL << "blockchain pruning stats:" << ENDL << + " last block height: " << last_block_height << ENDL << + " blocks processed: " << blocks << ENDL << + " total txs: " << txs << ENDL << + " pruned txs: " << pruned_txs << ENDL << + " total signatures: " << signatures << ENDL << + " total attachments: " << attachments << ENDL << + (pruned_txs == 0 ? "*** The database seems to be unpruned!" : "The database contains pruned transactions."), LOG_LEVEL_0); + + return true; + } + //-------------------------------------------------------------------------------- bool print_tx(const std::vector& args) { if (args.empty()) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index b7391f98..8656eb2c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2061,8 +2061,10 @@ int main(int argc, char* argv[]) std::string arg_pos_mining_reward_address_str = command_line::get_arg(vm, arg_pos_mining_reward_address); if (!arg_pos_mining_reward_address_str.empty()) { - r = get_account_address_from_str(miner_address, arg_pos_mining_reward_address_str); + std::string payment_id; + r = wal.get_transfer_address(arg_pos_mining_reward_address_str, miner_address, payment_id); CHECK_AND_ASSERT_MES(r, EXIT_FAILURE, "Failed to parse miner address from string: " << arg_pos_mining_reward_address_str); + CHECK_AND_ASSERT_MES(payment_id.size() == 0, EXIT_FAILURE, "Address for rewards should not be integrated address: " << arg_pos_mining_reward_address_str); LOG_PRINT_YELLOW("PoS reward will be sent to another address: " << arg_pos_mining_reward_address_str, LOG_LEVEL_0); } } diff --git a/tests/functional_tests/L2S.h b/tests/functional_tests/L2S.h index e9071fcc..7eb52893 100644 --- a/tests/functional_tests/L2S.h +++ b/tests/functional_tests/L2S.h @@ -1,6 +1,6 @@ -// Copyright (c) 2020 Zano Project (https://zano.org/) -// Copyright (c) 2020 Locksmith (acmxddk@gmail.com) -// Copyright (c) 2020 sowle (crypto.sowle@gmail.com) +// Copyright (c) 2020-2021 Zano Project (https://zano.org/) +// Copyright (c) 2020-2021 Locksmith (acmxddk@gmail.com) +// Copyright (c) 2020-2021 sowle (val@zano.org, crypto.sowle@gmail.com) // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once @@ -78,6 +78,275 @@ bool is_power_of_2(T v) return true; } +bool ml2s_lnk_sig_gen(const scalar_t& m, const std::vector& B_array, const std::vector& b_array, const std::vector& s_array, ml2s_signature& signature, uint8_t* p_err = nullptr) +{ +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("ml2s_lnk_sig_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) *p_err = err_code; return false; } +#ifndef NDEBUG +# define DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) CHECK_AND_ASSERT_MES_CUSTOM(cond, false, if (p_err) *p_err = err_code, "ml2s_lnk_sig_gen check failed: " << #cond) +#else +# define DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) +#endif + + // check boundaries + size_t L = b_array.size(); + size_t N = 2 * B_array.size(); + size_t n = log2sz(N); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(s_array.size() == L, 0); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(1ull << n == N, 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L > 0, 2); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L <= N / 2, 3); + + // initialize signature + signature.elements.resize(L); + for (size_t i = 0; i < L; ++i) + { + signature.elements[i].H_array.resize(n); + signature.elements[i].r_array.resize(n); + } + + std::vector b_inv_array; + b_inv_array.reserve(L); + for (size_t i = 0; i < L; ++i) + b_inv_array.emplace_back(b_array[i].reciprocal()); + + std::vector I_array; + I_array.reserve(L); + for (size_t i = 0; i < L; ++i) + I_array.emplace_back(b_inv_array[i] * hash_helper_t::hp(b_array[i] * c_point_G)); + + signature.z = hash_helper_t::hs(m, B_array, I_array); + const scalar_t& z = signature.z; + + auto hash_point_lambda = [&z](const point_t& point) { return point + z * hash_helper_t::hp(point); }; + + std::vector A_array; // size == L + A_array.reserve(L); + for (size_t i = 0; i < L; ++i) + A_array.emplace_back(c_point_G + z * I_array[i]); + + std::vector P_array; // size == N // 2 + P_array.reserve(B_array.size()); + for (size_t i = 0; i < B_array.size(); ++i) + P_array.emplace_back(hash_point_lambda(B_array[i])); + + point_t Q_shift = hash_helper_t::hs(A_array, P_array) * c_point_G; + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(P_array.size() * 2 == N, 4); + std::vector X_array(N); + // X_array = { P_array[0], Q_array[0], P_array[1], Q_array[1], etc. } + for (size_t i = 0; i < N; ++i) + { + if (i % 2 == 0) + X_array[i] = P_array[i / 2]; + else + X_array[i] = hash_point_lambda(Q_shift + B_array[i / 2]); + } + + for (size_t i = 0; i < L; ++i) + { + size_t s_idx = s_array[i]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(s_idx < B_array.size() && 2 * s_idx + 1 < X_array.size(), 5); + point_t Ap = b_inv_array[i] * X_array[2 * s_idx]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(Ap == A_array[i], 6); + } + + /// + + struct intermediate_element_t + { + scalar_t f; + scalar_t k0; + scalar_t q; + + size_t M_cnt; + size_t z; + size_t h; + scalar_t a; + scalar_t x; + + std::vector Y_array; + }; + + std::vector interms(L); + + + // challenge c0 + + scalar_t e = hash_helper_t::hs(z); + + hash_helper_t::hs_t hsc; + hsc.add_scalar(e); + hsc.add_points_array(X_array); + + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + sel.Z0 = A_array[i]; // b_inv_array[i] * X_array[2 * s_array[i]] + 0 * X_array[2 * s + 1], as k1 == 0 always + interm.f.make_random(); + sel.Z = interm.f * sel.Z0; + interm.k0 = interm.f * b_inv_array[i]; + interm.q.make_random(); + sel.T0 = interm.q * sel.Z0; + + hsc.add_point(sel.Z0); + hsc.add_point(sel.T0); + hsc.add_point(sel.Z); + } + + scalar_t c0 = hsc.calc_hash(); + + // challenges c11, c13 + + hsc.add_scalar(c0); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + sel.t0 = interm.q - interm.f * c0; + interm.M_cnt = N; + interm.z = 2 * s_array[i]; + interm.h = 2 * s_array[i] + 1; // we already checked s_array elements against X_array.size() above + interm.a = 1; + interm.q.make_random(); // new q + interm.Y_array = X_array; + + sel.H_array[0] = interm.k0 / interm.q * X_array[interm.h]; // H1 + + hsc.add_scalar(sel.t0); + hsc.add_point(sel.H_array[0]); + } + + // challenges c11, c13 + +#ifndef NDEBUG + // these vectors are only needed for self-check in the end + std::vector c1_array(n); // counting from 0, so c11 is c1_array[0], will have n elements + std::vector c3_array(n-1); // the same, will have n - 1 elements +#endif + + scalar_t ci1 = hsc.calc_hash(); + scalar_t ci3 = hash_helper_t::hs(ci1); + + // ci1, ci3 for i in [2; n] -- corresponds c1_array for i in [1; n - 1], c3_array for i in [1; n - 2] + for (size_t idx_n = 0; idx_n < n - 1; ++idx_n) + { +#ifndef NDEBUG + c1_array[idx_n] = ci1; + c3_array[idx_n] = ci3; +#endif + + std::vector c_array = { &c_scalar_1, &ci1, &c_scalar_1, &ci3 }; + + hsc.add_scalar(ci1); + for (size_t idx_L = 0; idx_L < L; ++idx_L) + { + auto& interm = interms[idx_L]; + auto& sel = signature.elements[idx_L]; + + const scalar_t& e_local = *c_array[interm.z % 4]; + const scalar_t& g_local = *c_array[interm.h % 4]; + + sel.r_array[idx_n] = interm.q * g_local / e_local; // r_i + + interm.a *= e_local; + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(is_power_of_2(interm.M_cnt), 200); + interm.M_cnt = interm.M_cnt / 2; + // TODO: check M_scalar is power of 2 + + for (size_t j = 0; j < interm.M_cnt; ++j) + interm.Y_array[j] = (interm.Y_array[2 * j] + *c_array[(2 * j + 1) % 4] * interm.Y_array[2 * j + 1]) / e_local; + + interm.z /= 2; + interm.h = invert_last_bit(interm.z); + interm.q.make_random(); + sel.H_array[idx_n + 1] = interm.k0 / interm.q * interm.Y_array[interm.h]; // H_{i+1} + + hsc.add_scalar(sel.r_array[idx_n]); + hsc.add_point(sel.H_array[idx_n + 1]); + } + + ci1 = hsc.calc_hash(); + ci3 = hash_helper_t::hs(ci1); + } + + // challenge cn +#ifndef NDEBUG + c1_array[n - 1] = ci1; +#endif + + // challenge c + hsc.add_scalar(ci1); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE((interm.z == 0 && interm.h == 1) || (interm.z == 1 && interm.h == 0), 7); + const scalar_t& e_local = interm.z == 0 ? c_scalar_1 : ci1; + const scalar_t& g_local = interm.z == 0 ? ci1 : c_scalar_1; + + sel.r_array[n - 1] = interm.q * g_local / e_local; // r_n + + interm.a *= e_local; + interm.x = interm.a / interm.k0; + + interm.q.make_random(); // qn + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.k0 * X_array[2 * s_array[i]] == sel.Z, 201); + + point_t W = sel.Z; + for (size_t j = 0; j < n; ++j) + W = W + sel.r_array[j] * sel.H_array[j]; + sel.T = interm.q * W; + + hsc.add_scalar(sel.r_array[n - 1]); + hsc.add_point(sel.T); + } + + scalar_t c = hsc.calc_hash(); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + sel.t = interm.q - interm.x * c; + } + + // L2S signature is complete + +#ifndef NDEBUG + // self-check + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + point_t W = sel.Z; + for (size_t j = 0; j < n; ++j) + W = W + sel.r_array[j] * sel.H_array[j]; + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.T == interm.q * W, 230); + + point_t R; + R.zero(); + bool r = ml2s_rsum(n, X_array, c1_array, c3_array, R); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(r, 231); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(R == interm.x * W, 232); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t == interm.q - interm.x * c, 233); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t * W + c * R == sel.T, 234); + } +#endif // #ifndef NDEBUG + + return true; +#undef DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +} // ml2s_lnk_sig_gen + bool ml2s_lnk_sig_verif(const scalar_t& m, const std::vector& B_array, const ml2s_signature& signature, uint8_t* p_err = nullptr, std::vector* p_I_array = nullptr) @@ -217,3 +486,859 @@ bool ml2s_lnk_sig_verif(const scalar_t& m, const std::vector& B_array, return true; #undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE } + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// v 2 +//////////////////////////////////////////////////////////////////////////////////////////////////// +struct ml2s_signature_element_v2 +{ + point_t T0; + scalar_t t0; + point_t Z; + std::vector r_array; // size = n + std::vector H_array; // size = n + point_t T; + scalar_t t; +}; + +struct ml2s_signature_v2 +{ + std::vector elements; // size = L +}; + +bool ml2s_lnk_sig_gen_v2(const scalar_t& m, const std::vector& B_array, const std::vector& b_array, + const std::vector& s_array, const std::vector& I_array, ml2s_signature_v2& signature, uint8_t* p_err = nullptr) +{ +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("ml2s_lnk_sig_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) *p_err = err_code; return false; } +#ifndef NDEBUG +# define DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) CHECK_AND_ASSERT_MES_CUSTOM(cond, false, if (p_err) *p_err = err_code, "ml2s_lnk_sig_gen check failed: " << #cond) +#else +# define DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) +#endif + + // check boundaries + size_t L = b_array.size(); + size_t N = 2 * B_array.size(); + size_t n = log2sz(N); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(s_array.size() == L, 0); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(1ull << n == N, 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L > 0, 2); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L <= N / 2, 3); + + // initialize signature + signature.elements.resize(L); + for (size_t i = 0; i < L; ++i) + { + signature.elements[i].H_array.resize(n); + signature.elements[i].r_array.resize(n); + } + + std::vector b_inv_array; + b_inv_array.reserve(L); + for (size_t i = 0; i < L; ++i) + b_inv_array.emplace_back(b_array[i].reciprocal()); + + const scalar_t z = hash_helper_t::hs(m, B_array, I_array); + + auto hash_point_lambda = [&z](const point_t& point) { return point + z * hash_helper_t::hp(point); }; + + std::vector A_array; // size == L + A_array.reserve(L); + for (size_t i = 0; i < L; ++i) + A_array.emplace_back(c_point_G + z * I_array[i]); + + std::vector P_array; // size == N // 2 + P_array.reserve(B_array.size()); + for (size_t i = 0; i < B_array.size(); ++i) + P_array.emplace_back(hash_point_lambda(B_array[i])); + + point_t I_sum; + I_sum.zero(); + for (size_t i = 0; i < L; ++i) + I_sum = I_sum + I_array[i]; + point_t Q_shift = hash_helper_t::hp(I_sum); + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(P_array.size() * 2 == N, 4); + std::vector X_array(N); + // X_array = { P_array[0], Q_array[0], P_array[1], Q_array[1], etc. } + for (size_t i = 0; i < N; ++i) + { + if (i % 2 == 0) + X_array[i] = P_array[i / 2]; + else + X_array[i] = hash_point_lambda(Q_shift + B_array[i / 2]); + } + + for (size_t i = 0; i < L; ++i) + { + size_t s_idx = s_array[i]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(s_idx < B_array.size() && 2 * s_idx + 1 < X_array.size(), 5); + point_t Ap = b_inv_array[i] * X_array[2 * s_idx]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(Ap == A_array[i], 6); + } + + /// + + struct intermediate_element_t + { + scalar_t f; + scalar_t k0; + scalar_t q; + + size_t M_cnt; + size_t z; + size_t h; + scalar_t a; + scalar_t x; + + std::vector Y_array; + }; + + std::vector interms(L); + + + // challenge c0 + + scalar_t e = hash_helper_t::hs(z); + + hash_helper_t::hs_t hsc; + hsc.add_scalar(e); + hsc.add_points_array(X_array); + + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + const point_t& Z0 = A_array[i]; // b_inv_array[i] * X_array[2 * s_array[i]] + 0 * X_array[2 * s + 1], as k1 == 0 always + interm.f.make_random(); + sel.Z = interm.f * Z0; + interm.k0 = interm.f * b_inv_array[i]; + interm.q.make_random(); + sel.T0 = interm.q * Z0; + + hsc.add_point(sel.T0); + hsc.add_point(sel.Z); + } + + scalar_t c0 = hsc.calc_hash(); + + // challenges c11, c13 + + hsc.add_scalar(c0); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + sel.t0 = interm.q - interm.f * c0; + interm.M_cnt = N; + interm.z = 2 * s_array[i]; + interm.h = 2 * s_array[i] + 1; // we already checked s_array elements against X_array.size() above + interm.a = 1; + interm.q.make_random(); // new q + interm.Y_array = X_array; + + sel.H_array[0] = interm.k0 / interm.q * X_array[interm.h]; // H1 + + hsc.add_scalar(sel.t0); + hsc.add_point(sel.H_array[0]); + } + + // challenges c11, c13 + +#ifndef NDEBUG + // these vectors are only needed for self-check in the end + std::vector c1_array(n); // counting from 0, so c11 is c1_array[0], will have n elements + std::vector c3_array(n - 1); // the same, will have n - 1 elements +#endif + + scalar_t ci1 = hsc.calc_hash(); + scalar_t ci3 = hash_helper_t::hs(ci1); + + // ci1, ci3 for i in [2; n] -- corresponds c1_array for i in [1; n - 1], c3_array for i in [1; n - 2] + for (size_t idx_n = 0; idx_n < n - 1; ++idx_n) + { +#ifndef NDEBUG + c1_array[idx_n] = ci1; + c3_array[idx_n] = ci3; +#endif + + std::vector c_array = { &c_scalar_1, &ci1, &c_scalar_1, &ci3 }; + + hsc.add_scalar(ci1); + for (size_t idx_L = 0; idx_L < L; ++idx_L) + { + auto& interm = interms[idx_L]; + auto& sel = signature.elements[idx_L]; + + const scalar_t& e_local = *c_array[interm.z % 4]; + const scalar_t& g_local = *c_array[interm.h % 4]; + + sel.r_array[idx_n] = interm.q * g_local / e_local; // r_i + + interm.a *= e_local; + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(is_power_of_2(interm.M_cnt), 200); + interm.M_cnt = interm.M_cnt / 2; + // TODO: check M_scalar is power of 2 + + for (size_t j = 0; j < interm.M_cnt; ++j) + interm.Y_array[j] = (interm.Y_array[2 * j] + *c_array[(2 * j + 1) % 4] * interm.Y_array[2 * j + 1]) / e_local; + + interm.z /= 2; + interm.h = invert_last_bit(interm.z); + interm.q.make_random(); + sel.H_array[idx_n + 1] = interm.k0 / interm.q * interm.Y_array[interm.h]; // H_{i+1} + + hsc.add_scalar(sel.r_array[idx_n]); + hsc.add_point(sel.H_array[idx_n + 1]); + } + + ci1 = hsc.calc_hash(); + ci3 = hash_helper_t::hs(ci1); + } + + // challenge cn +#ifndef NDEBUG + c1_array[n - 1] = ci1; +#endif + + // challenge c + hsc.add_scalar(ci1); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE((interm.z == 0 && interm.h == 1) || (interm.z == 1 && interm.h == 0), 7); + const scalar_t& e_local = interm.z == 0 ? c_scalar_1 : ci1; + const scalar_t& g_local = interm.z == 0 ? ci1 : c_scalar_1; + + sel.r_array[n - 1] = interm.q * g_local / e_local; // r_n + + interm.a *= e_local; + interm.x = interm.a / interm.k0; + + interm.q.make_random(); // qn + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.k0 * X_array[2 * s_array[i]] == sel.Z, 201); + + point_t W = sel.Z; + for (size_t j = 0; j < n; ++j) + W = W + sel.r_array[j] * sel.H_array[j]; + sel.T = interm.q * W; + + hsc.add_scalar(sel.r_array[n - 1]); + hsc.add_point(sel.T); + } + + scalar_t c = hsc.calc_hash(); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + sel.t = interm.q - interm.x * c; + } + + // L2S signature is complete + +#ifndef NDEBUG + // self-check + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + point_t W = sel.Z; + for (size_t j = 0; j < n; ++j) + W = W + sel.r_array[j] * sel.H_array[j]; + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.T == interm.q * W, 230); + + point_t R; + R.zero(); + bool r = ml2s_rsum(n, X_array, c1_array, c3_array, R); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(r, 231); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(R == interm.x * W, 232); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t == interm.q - interm.x * c, 233); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t * W + c * R == sel.T, 234); + } +#endif // #ifndef NDEBUG + + return true; +#undef DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +} // ml2s_lnk_sig_gen + + +bool ml2s_lnk_sig_verif_v2(const scalar_t& m, const std::vector& B_array, std::vector& I_array, + const ml2s_signature_v2& signature, uint8_t* p_err = nullptr) +{ +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("ml2s_lnk_sig_verif: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) *p_err = err_code; return false; } + + size_t L = signature.elements.size(); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L > 0, 0); + size_t n = signature.elements[0].r_array.size(); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n < 32, 4); + size_t N = (size_t)1 << n; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(B_array.size() == N / 2, 5); + + const scalar_t z = hash_helper_t::hs(m, B_array, I_array); + + auto hash_point_lambda = [&z](const point_t& point) { return point + z * hash_helper_t::hp(point); }; + + std::vector A_array; + A_array.resize(L); + + for (size_t i = 0; i < L; ++i) + { + A_array[i] = I_array[i].mul_plus_G(z); // = c_point_G + z * I_array[i]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(signature.elements[i].r_array.size() == n, 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(signature.elements[i].H_array.size() == n, 2); + } + + scalar_t e = hash_helper_t::hs(z); + + point_t I_sum; + I_sum.zero(); + for (size_t i = 0; i < L; ++i) + I_sum = I_sum + I_array[i]; + point_t Q_shift = hash_helper_t::hp(I_sum); + + std::vector X_array(N); + for (size_t i = 0; i < N; i += 2) + X_array[i] = B_array[i / 2] + z * hash_helper_t::hp(B_array[i / 2]); + for (size_t i = 1; i < N; i += 2) + X_array[i] = hash_point_lambda(Q_shift + B_array[i / 2]); + + // challenge c0 + hash_helper_t::hs_t hsc; + hsc.reserve(1 + N + 3 * L); + hsc.add_scalar(e); + hsc.add_points_array(X_array); + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + hsc.add_point(sel.T0); + hsc.add_point(sel.Z); + } + e = hsc.calc_hash(); + scalar_t c0 = e; + + // check t0 * Z0 + c0 * Z == T0 + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t0 * A_array[i] + c0 * sel.Z == sel.T0, 7); + } + + // challenges c11, c13 + std::vector c1_array; // counting from 0, so c11 is c1_array[0], will have n elements + std::vector c3_array; // the same, will have n - 1 elements + + hsc.add_scalar(e); + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + hsc.add_scalar(sel.t0); + hsc.add_point(sel.H_array[0]); + } + e = hsc.calc_hash(); + c1_array.emplace_back(e); + c3_array.emplace_back(hash_helper_t::hs(e)); + + // ci1, ci3 for i in [2; n] -- corresponds c1_array for i in [1; n - 1], c3_array for i in [1; n - 2] + for (size_t i = 1; i < n; ++i) + { + hsc.add_scalar(e); + for (size_t j = 0; j < L; ++j) + { + auto& sel = signature.elements[j]; + hsc.add_scalar(sel.r_array[i - 1]); + hsc.add_point(sel.H_array[i]); + } + e = hsc.calc_hash(); + c1_array.emplace_back(e); + if (i != n - 1) + c3_array.emplace_back(hash_helper_t::hs(e)); + } + + // challenge c + hsc.add_scalar(e); + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + hsc.add_scalar(sel.r_array[n - 1]); + hsc.add_point(sel.T); + } + scalar_t c = hsc.calc_hash(); + + // Rsum + point_t R = c_point_G; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ml2s_rsum(n, X_array, c1_array, c3_array, R), 8); + point_t cR = c * R; + + // final checks + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + point_t S = sel.Z; + for (size_t j = 0; j < n; ++j) + { + S = S + sel.r_array[j] * sel.H_array[j]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!S.is_zero(), 9); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.r_array[j] != 0, 10); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!sel.H_array[j].is_zero(), 11); + } + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t * S + cR == sel.T, 12); + } + + return true; +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// v 3 +//////////////////////////////////////////////////////////////////////////////////////////////////// +struct ml2s_signature_element_v3 +{ + crypto::public_key T0; + crypto::secret_key t0; + crypto::public_key Z; + std::vector r_array; // size = n + std::vector H_array; // size = n + crypto::public_key T; + crypto::secret_key t; +}; + +struct ml2s_signature_v3 +{ + std::vector elements; // size = L +}; + +bool ml2s_lnk_sig_gen_v3(const crypto::hash& m, const std::vector& ring_pub_keys, const std::vector& sec_keys, + const std::vector& ring_mapping, const std::vector& key_images, ml2s_signature_v3& signature, uint8_t* p_err = nullptr) +{ +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("ml2s_lnk_sig_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) *p_err = err_code; return false; } +#ifndef NDEBUG +# define DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) CHECK_AND_ASSERT_MES_CUSTOM(cond, false, if (p_err) *p_err = err_code, "ml2s_lnk_sig_gen check failed: " << #cond) +#else +# define DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) +#endif + + // check boundaries + size_t L = sec_keys.size(); + size_t N = 2 * ring_pub_keys.size(); + size_t n = log2sz(N); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ring_mapping.size() == L, 0); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(1ull << n == N, 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L > 0, 2); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L <= N / 2, 3); + + // initialize signature + signature.elements.resize(L); + for (size_t i = 0; i < L; ++i) + { + signature.elements[i].H_array.resize(n); + signature.elements[i].r_array.resize(n); + } + + std::vector b_array(L); + std::vector b_inv_array; + b_inv_array.reserve(L); + std::vector I_array(L); + for (size_t i = 0; i < L; ++i) + { + b_array[i].from_secret_key(sec_keys[i]); + b_inv_array.emplace_back(b_array[i].reciprocal()); + I_array[i].from_key_image(key_images[i]); + } + + const scalar_t z = hash_helper_t::hs(m, ring_pub_keys, key_images); + + auto hash_point_lambda = [&z](const point_t& point) { return point + z * hash_helper_t::hp(point); }; + + std::vector A_array; // size == L + A_array.reserve(L); + for (size_t i = 0; i < L; ++i) + A_array.emplace_back(c_point_G + z * I_array[i]); + + point_t I_sum; + I_sum.zero(); + for (size_t i = 0; i < L; ++i) + I_sum = I_sum + I_array[i]; + point_t Q_shift = hash_helper_t::hp(I_sum); + + std::vector B_array(ring_pub_keys.size()); + for (size_t j = 0; j < ring_pub_keys.size(); ++j) + B_array[j].from_public_key(ring_pub_keys[j]); + + std::vector X_array(N); + for (size_t i = 0; i < N; i += 2) + X_array[i] = B_array[i / 2] + z * hash_helper_t::hp(ring_pub_keys[i / 2]); + for (size_t i = 1; i < N; i += 2) + X_array[i] = hash_point_lambda(Q_shift + B_array[i / 2]); + + + + for (size_t i = 0; i < L; ++i) + { + size_t s_idx = ring_mapping[i]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(s_idx < B_array.size() && 2 * s_idx + 1 < X_array.size(), 5); + point_t Ap = b_inv_array[i] * X_array[2 * s_idx]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(Ap == A_array[i], 6); + } + + /// + + struct intermediate_element_t + { + scalar_t f; + scalar_t k0; + scalar_t q; + + size_t M_cnt; + size_t z; + size_t h; + scalar_t a; + scalar_t x; + + std::vector Y_array; + + // unpacked signature points + point_t Z; + point_t T0; + std::vector H_array; + point_t T; + }; + + std::vector interms(L); + + + // challenge c0 + + scalar_t e = hash_helper_t::hs(z); + + hash_helper_t::hs_t hsc; + hsc.add_scalar(e); + hsc.add_points_array(X_array); + + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + const point_t& Z0 = A_array[i]; // b_inv_array[i] * X_array[2 * s_array[i]] + 0 * X_array[2 * s + 1], as k1 == 0 always + interm.f.make_random(); + interm.Z = interm.f * Z0; + sel.Z = interm.Z.to_public_key(); + interm.k0 = interm.f * b_inv_array[i]; + interm.q.make_random(); + interm.T0 = interm.q * Z0; + sel.T0 = interm.T0.to_public_key(); + + hsc.add_pub_key(sel.T0); + hsc.add_pub_key(sel.Z); + } + + scalar_t c0 = hsc.calc_hash(); + + // challenges c11, c13 + + hsc.add_scalar(c0); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + sel.t0 = interm.q - interm.f * c0; + interm.M_cnt = N; + interm.z = 2 * ring_mapping[i]; + interm.h = 2 * ring_mapping[i] + 1; // we already checked s_array elements against X_array.size() above + interm.a = 1; + interm.q.make_random(); // new q + interm.Y_array = X_array; + + interm.H_array.resize(n); + interm.H_array[0] = interm.k0 / interm.q * X_array[interm.h]; // H1 + sel.H_array[0] = interm.H_array[0].to_public_key(); + + hsc.add_scalar(sel.t0); + hsc.add_pub_key(sel.H_array[0]); + } + + // challenges c11, c13 + +#ifndef NDEBUG + // these vectors are only needed for self-check in the end + std::vector c1_array(n); // counting from 0, so c11 is c1_array[0], will have n elements + std::vector c3_array(n - 1); // the same, will have n - 1 elements +#endif + + scalar_t ci1 = hsc.calc_hash(); + scalar_t ci3 = hash_helper_t::hs(ci1); + + // ci1, ci3 for i in [2; n] -- corresponds c1_array for i in [1; n - 1], c3_array for i in [1; n - 2] + for (size_t idx_n = 0; idx_n < n - 1; ++idx_n) + { +#ifndef NDEBUG + c1_array[idx_n] = ci1; + c3_array[idx_n] = ci3; +#endif + + std::vector c_array = { &c_scalar_1, &ci1, &c_scalar_1, &ci3 }; + + hsc.add_scalar(ci1); + for (size_t idx_L = 0; idx_L < L; ++idx_L) + { + auto& interm = interms[idx_L]; + auto& sel = signature.elements[idx_L]; + + const scalar_t& e_local = *c_array[interm.z % 4]; + const scalar_t& g_local = *c_array[interm.h % 4]; + + sel.r_array[idx_n] = interm.q * g_local / e_local; // r_i + + interm.a *= e_local; + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(is_power_of_2(interm.M_cnt), 200); + interm.M_cnt = interm.M_cnt / 2; + // TODO: check M_scalar is power of 2 + + for (size_t j = 0; j < interm.M_cnt; ++j) + interm.Y_array[j] = (interm.Y_array[2 * j] + *c_array[(2 * j + 1) % 4] * interm.Y_array[2 * j + 1]) / e_local; + + interm.z /= 2; + interm.h = invert_last_bit(interm.z); + interm.q.make_random(); + interm.H_array[idx_n + 1] = interm.k0 / interm.q * interm.Y_array[interm.h]; // H_{i+1} + sel.H_array[idx_n + 1] = interm.H_array[idx_n + 1].to_public_key(); + + hsc.add_scalar(sel.r_array[idx_n]); + hsc.add_pub_key(sel.H_array[idx_n + 1]); + } + + ci1 = hsc.calc_hash(); + ci3 = hash_helper_t::hs(ci1); + } + + // challenge cn +#ifndef NDEBUG + c1_array[n - 1] = ci1; +#endif + + // challenge c + hsc.add_scalar(ci1); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE((interm.z == 0 && interm.h == 1) || (interm.z == 1 && interm.h == 0), 7); + const scalar_t& e_local = interm.z == 0 ? c_scalar_1 : ci1; + const scalar_t& g_local = interm.z == 0 ? ci1 : c_scalar_1; + + sel.r_array[n - 1] = interm.q * g_local / e_local; // r_n + + interm.a *= e_local; + interm.x = interm.a / interm.k0; + + interm.q.make_random(); // qn + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.k0 * X_array[2 * ring_mapping[i]] == interm.Z, 201); + + point_t W = interm.Z; + for (size_t j = 0; j < n; ++j) + W = W + scalar_t(sel.r_array[j]) * interm.H_array[j]; + interm.T = interm.q * W; + sel.T = interm.T.to_public_key(); + + hsc.add_scalar(sel.r_array[n - 1]); + hsc.add_pub_key(sel.T); + } + + scalar_t c = hsc.calc_hash(); + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + sel.t = interm.q - interm.x * c; + } + + // L2S signature is complete + +#ifndef NDEBUG + // self-check + for (size_t i = 0; i < L; ++i) + { + auto& interm = interms[i]; + auto& sel = signature.elements[i]; + + point_t W = interm.Z; + for (size_t j = 0; j < n; ++j) + W = W + scalar_t(sel.r_array[j]) * interm.H_array[j]; + + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(interm.T == interm.q * W, 230); + + point_t R; + R.zero(); + bool r = ml2s_rsum(n, X_array, c1_array, c3_array, R); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(r, 231); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(R == interm.x * W, 232); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t == interm.q - interm.x * c, 233); + DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t * W + c * R == interm.T, 234); + } +#endif // #ifndef NDEBUG + + return true; +#undef DBG_CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +} // ml2s_lnk_sig_gen + + +bool ml2s_lnk_sig_verif_v3(const crypto::hash& m, const std::vector& ring_pub_keys, std::vector& key_images, + const ml2s_signature_v3& signature, uint8_t* p_err = nullptr) +{ +#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \ + if (!(cond)) { LOG_PRINT_RED("ml2s_lnk_sig_verif: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \ + if (p_err) *p_err = err_code; return false; } + + size_t L = signature.elements.size(); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(L > 0, 0); + size_t n = signature.elements[0].r_array.size(); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(n < 32, 4); + size_t N = (size_t)1 << n; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ring_pub_keys.size() == N / 2, 5); + + const scalar_t z = hash_helper_t::hs(m, ring_pub_keys, key_images); + + auto hash_point_lambda = [&z](const point_t& point) { return point + z * hash_helper_t::hp(point); }; + + std::vector A_array(L); + std::vector I_array(L); + for (size_t i = 0; i < L; ++i) + { + I_array[i].from_key_image(key_images[i]); + A_array[i] = I_array[i].mul_plus_G(z); // = c_point_G + z * I_array[i]; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(signature.elements[i].r_array.size() == n, 1); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(signature.elements[i].H_array.size() == n, 2); + } + + scalar_t e = hash_helper_t::hs(z); + + point_t I_sum; + I_sum.zero(); + for (size_t i = 0; i < L; ++i) + I_sum = I_sum + I_array[i]; + point_t Q_shift = hash_helper_t::hp(I_sum); + + std::vector B_array(ring_pub_keys.size()); + for (size_t j = 0; j < B_array.size(); ++j) + B_array[j].from_public_key(ring_pub_keys[j]); + + std::vector X_array(N); + for (size_t i = 0; i < N; i += 2) + X_array[i] = B_array[i / 2] + z * hash_helper_t::hp(ring_pub_keys[i / 2]); + for (size_t i = 1; i < N; i += 2) + X_array[i] = hash_point_lambda(Q_shift + B_array[i / 2]); + + // challenge c0 + hash_helper_t::hs_t hsc; + hsc.reserve(1 + N + 3 * L); + hsc.add_scalar(e); + hsc.add_points_array(X_array); + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + hsc.add_pub_key(sel.T0); + hsc.add_pub_key(sel.Z); + } + e = hsc.calc_hash(); + scalar_t c0 = e; + + std::vector Z_array(L); + // check t0 * Z0 + c0 * Z == T0 + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + Z_array[i].from_public_key(sel.Z); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t0 * A_array[i] + c0 * Z_array[i] == point_t(sel.T0), 7); + } + + // challenges c11, c13 + std::vector c1_array; // counting from 0, so c11 is c1_array[0], will have n elements + std::vector c3_array; // the same, will have n - 1 elements + + hsc.add_scalar(e); + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + hsc.add_scalar(sel.t0); + hsc.add_pub_key(sel.H_array[0]); + } + e = hsc.calc_hash(); + c1_array.emplace_back(e); + c3_array.emplace_back(hash_helper_t::hs(e)); + + // ci1, ci3 for i in [2; n] -- corresponds c1_array for i in [1; n - 1], c3_array for i in [1; n - 2] + for (size_t i = 1; i < n; ++i) + { + hsc.add_scalar(e); + for (size_t j = 0; j < L; ++j) + { + auto& sel = signature.elements[j]; + hsc.add_scalar(sel.r_array[i - 1]); + hsc.add_pub_key(sel.H_array[i]); + } + e = hsc.calc_hash(); + c1_array.emplace_back(e); + if (i != n - 1) + c3_array.emplace_back(hash_helper_t::hs(e)); + } + + // challenge c + hsc.add_scalar(e); + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + hsc.add_scalar(sel.r_array[n - 1]); + hsc.add_pub_key(sel.T); + } + scalar_t c = hsc.calc_hash(); + + // Rsum + point_t R = c_point_G; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ml2s_rsum(n, X_array, c1_array, c3_array, R), 8); + point_t cR = c * R; + + // final checks + for (size_t i = 0; i < L; ++i) + { + auto& sel = signature.elements[i]; + point_t S = Z_array[i]; + for (size_t j = 0; j < n; ++j) + { + point_t H; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(H.from_public_key(sel.H_array[j]), 13); + S = S + sel.r_array[j] * H; + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!S.is_zero(), 9); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(scalar_t(sel.r_array[j]) != 0, 10); + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!H.is_zero(), 11); + } + + CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sel.t * S + cR == point_t(sel.T), 12); + } + + return true; +#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE +} diff --git a/tests/functional_tests/crypto_tests.cpp b/tests/functional_tests/crypto_tests.cpp index ab60725f..25ceb2ee 100644 --- a/tests/functional_tests/crypto_tests.cpp +++ b/tests/functional_tests/crypto_tests.cpp @@ -1,4 +1,5 @@ -// Copyright (c) 2020 Zano Project +// Copyright (c) 2020-2021 Zano Project +// Copyright (c) 2020-2021 sowle (val@zano.org, crypto.sowle@gmail.com) // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,13 +10,14 @@ #include "epee/include/profile_tools.h" #include "include_base_utils.h" #include "common/crypto_stream_operators.h" +#include "common/varint.h" +#include "currency_core/difficulty.h" +#include "crypto/crypto-sugar.h" -extern "C" { -#include "crypto/crypto-ops.h" -} // extern "C" +using namespace crypto; -unsigned char Lm2[32] = { 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10 }; +namespace mp = boost::multiprecision; // out = z ^ s (mod l) void sc_exp(unsigned char* out, const unsigned char* z, const unsigned char* s) @@ -133,26 +135,6 @@ void sc_invert2(unsigned char* recip, const unsigned char* s) extern void *sha3(const void *in, size_t inlen, void *md, int mdlen); -// -// Helpers -// - -template -std::string pod_to_hex_big_endian(const pod_t &h) -{ - constexpr char hexmap[] = "0123456789abcdef"; - const char* data = reinterpret_cast(&h); - size_t len = sizeof h; - - std::string s(len * 2, ' '); - for (size_t i = 0; i < len; ++i) { - s[2 * i] = hexmap[(data[len - 1 - i] & 0xF0) >> 4]; - s[2 * i + 1] = hexmap[(data[len - 1 - i] & 0x0F)]; - } - - return s; -} - uint64_t rand_in_range(uint64_t from_including, uint64_t to_not_including) { uint64_t result = 0; @@ -162,486 +144,13 @@ uint64_t rand_in_range(uint64_t from_including, uint64_t to_not_including) -int fe_cmp(const fe a, const fe b) -{ - for (size_t i = 9; i != SIZE_MAX; --i) - { - if (reinterpret_cast(a[i]) < reinterpret_cast(b[i])) return -1; - if (reinterpret_cast(a[i]) > reinterpret_cast(b[i])) return 1; - } - return 0; -} static const fe scalar_L_fe = { 16110573, 10012311, -6632702, 16062397, 5471207, 0, 0, 0, 0, 4194304 }; -__declspec(align(32)) -struct scalar_t -{ - union - { - uint64_t m_u64[4]; - unsigned char m_s[32]; - }; - // DONE! consider 1) change to aligned array of unsigned chars - // consider 2) add 32 byte after to speed up sc_reduce by decreasing num of copy operations - scalar_t() - {} - // won't check scalar range validity (< L) - scalar_t(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) - { - m_u64[0] = a0; - m_u64[1] = a1; - m_u64[2] = a2; - m_u64[3] = a3; - } - // won't check secret key validity (sk < L) - scalar_t(const crypto::secret_key& sk) - { - from_secret_key(sk); - } - - // copy data and reduce - scalar_t(const crypto::hash& hash) - { - m_u64[0] = ((uint64_t*)&hash)[0]; - m_u64[1] = ((uint64_t*)&hash)[1]; - m_u64[2] = ((uint64_t*)&hash)[2]; - m_u64[3] = ((uint64_t*)&hash)[3]; - sc_reduce32(&m_s[0]); - } - - scalar_t(uint64_t v) - { - zero(); - if (v == 0) - return; - m_u64[0] = v; - // do not need to call reduce as 2^64 < L - } - - unsigned char* data() - { - return &m_s[0]; - } - - const unsigned char* data() const - { - return &m_s[0]; - } - - crypto::secret_key &as_secret_key() - { - return *(crypto::secret_key*)&m_s[0]; - } - - const crypto::secret_key& as_secret_key() const - { - return *(const crypto::secret_key*)&m_s[0]; - } - - operator crypto::secret_key() const - { - crypto::secret_key result; - memcpy(result.data, &m_s, sizeof result.data); - return result; - } - - void from_secret_key(const crypto::secret_key& sk) - { - uint64_t *p_sk64 = (uint64_t*)&sk; - m_u64[0] = p_sk64[0]; - m_u64[1] = p_sk64[1]; - m_u64[2] = p_sk64[2]; - m_u64[3] = p_sk64[3]; - // assuming secret key is correct (< L), so we don't need to call reduce here - } - - void zero() - { - m_u64[0] = 0; - m_u64[1] = 0; - m_u64[2] = 0; - m_u64[3] = 0; - } - - static scalar_t random() - { - unsigned char tmp[64]; - crypto::generate_random_bytes(64, tmp); - sc_reduce(tmp); - scalar_t result; - memcpy(&result.m_s, tmp, sizeof result.m_s); - return result; - } - - void make_random() - { - unsigned char tmp[64]; - crypto::generate_random_bytes(64, tmp); - sc_reduce(tmp); - memcpy(&m_s, tmp, sizeof m_s); - } - - bool is_zero() const - { - return sc_isnonzero(&m_s[0]) == 0; - } - - scalar_t operator+(const scalar_t& v) const - { - scalar_t result; - sc_add(&result.m_s[0], &m_s[0], &v.m_s[0]); - return result; - } - - scalar_t& operator+=(const scalar_t& v) - { - sc_add(&m_s[0], &m_s[0], &v.m_s[0]); - return *this; - } - - scalar_t operator-(const scalar_t& v) const - { - scalar_t result; - sc_sub(&result.m_s[0], &m_s[0], &v.m_s[0]); - return result; - } - - scalar_t& operator-=(const scalar_t& v) - { - sc_sub(&m_s[0], &m_s[0], &v.m_s[0]); - return *this; - } - - scalar_t operator*(const scalar_t& v) const - { - scalar_t result; - sc_mul(&result.m_s[0], &m_s[0], &v.m_s[0]); - return result; - } - - scalar_t& operator*=(const scalar_t& v) - { - sc_mul(&m_s[0], &m_s[0], &v.m_s[0]); - return *this; - } - - scalar_t reciprocal() const - { - scalar_t result; - sc_invert(&result.m_s[0], &m_s[0]); - return result; - } - - scalar_t operator/(const scalar_t& v) const - { - return operator*(v.reciprocal()); - } - - scalar_t& operator/=(const scalar_t& v) - { - scalar_t reciprocal; - sc_invert(&reciprocal.m_s[0], &v.m_s[0]); - sc_mul(&m_s[0], &m_s[0], &reciprocal.m_s[0]); - return *this; - } - - bool operator==(const scalar_t& rhs) const - { - return - m_u64[0] == rhs.m_u64[0] && - m_u64[1] == rhs.m_u64[1] && - m_u64[2] == rhs.m_u64[2] && - m_u64[3] == rhs.m_u64[3]; - } - - bool operator!=(const scalar_t& rhs) const - { - return - m_u64[0] != rhs.m_u64[0] || - m_u64[1] != rhs.m_u64[1] || - m_u64[2] != rhs.m_u64[2] || - m_u64[3] != rhs.m_u64[3]; - } - - bool operator<(const scalar_t& rhs) const - { - if (m_u64[3] < rhs.m_u64[3]) return true; - if (m_u64[3] > rhs.m_u64[3]) return false; - if (m_u64[2] < rhs.m_u64[2]) return true; - if (m_u64[2] > rhs.m_u64[2]) return false; - if (m_u64[1] < rhs.m_u64[1]) return true; - if (m_u64[1] > rhs.m_u64[1]) return false; - if (m_u64[0] < rhs.m_u64[0]) return true; - if (m_u64[0] > rhs.m_u64[0]) return false; - return false; - } - - bool operator>(const scalar_t& rhs) const - { - if (m_u64[3] < rhs.m_u64[3]) return false; - if (m_u64[3] > rhs.m_u64[3]) return true; - if (m_u64[2] < rhs.m_u64[2]) return false; - if (m_u64[2] > rhs.m_u64[2]) return true; - if (m_u64[1] < rhs.m_u64[1]) return false; - if (m_u64[1] > rhs.m_u64[1]) return true; - if (m_u64[0] < rhs.m_u64[0]) return false; - if (m_u64[0] > rhs.m_u64[0]) return true; - return false; - } - - friend std::ostream& operator<<(std::ostream& ss, const scalar_t &v) - { - return ss << "0x" << pod_to_hex_big_endian(v); - } - -}; // struct scalar_t - - -//__declspec(align(32)) -struct point_t -{ - // A point(x, y) is represented in extended homogeneous coordinates (X, Y, Z, T) - // with x = X / Z, y = Y / Z, x * y = T / Z. - ge_p3 m_p3; - - point_t() - { - } - - void zero() - { - ge_p3_0(&m_p3); - } - - bool is_zero() const - { - // (0, 1) ~ (0, z, z, 0) - return fe_isnonzero(m_p3.X) * fe_cmp(m_p3.Y, m_p3.Z) == 0; - } - - bool from_public_key(const crypto::public_key& pk) - { - return ge_frombytes_vartime(&m_p3, reinterpret_cast(&pk)) == 0; - } - - operator crypto::public_key() const - { - crypto::public_key result; - ge_p3_tobytes((unsigned char*)&result, &m_p3); - return result; - } - - point_t operator+(const point_t& rhs) const - { - point_t result; - ge_cached rhs_c; - ge_p1p1 t; - ge_p3_to_cached(&rhs_c, &rhs.m_p3); - ge_add(&t, &m_p3, &rhs_c); - ge_p1p1_to_p3(&result.m_p3, &t); - return result; - } - - point_t operator-(const point_t& rhs) const - { - point_t result; - ge_cached rhs_c; - ge_p1p1 t; - ge_p3_to_cached(&rhs_c, &rhs.m_p3); - ge_sub(&t, &m_p3, &rhs_c); - ge_p1p1_to_p3(&result.m_p3, &t); - return result; - } - - friend point_t operator*(const scalar_t& lhs, const point_t& rhs) - { - point_t result; - ge_scalarmult_p3(&result.m_p3, reinterpret_cast(&lhs), &rhs.m_p3); - return result; - } - - friend point_t operator/(const point_t& lhs, const scalar_t& rhs) - { - point_t result; - scalar_t reciprocal; - sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]); - ge_scalarmult_p3(&result.m_p3, &reciprocal.m_s[0], &lhs.m_p3); - return result; - } - - friend bool operator==(const point_t& lhs, const point_t& rhs) - { - // convert to xy form, then compare components (because (z, y, z, t) representation is not unique) - fe lrecip, lx, ly; - fe rrecip, rx, ry; - - fe_invert(lrecip, lhs.m_p3.Z); - fe_invert(rrecip, rhs.m_p3.Z); - - fe_mul(lx, lhs.m_p3.X, lrecip); - fe_mul(rx, rhs.m_p3.X, rrecip); - if (memcmp(&lx, &rx, sizeof lx) != 0) - return false; - - fe_mul(ly, lhs.m_p3.Y, lrecip); - fe_mul(ry, rhs.m_p3.Y, rrecip); - if (memcmp(&ly, &ry, sizeof ly) != 0) - return false; - - return true; - }; - - friend std::ostream& operator<<(std::ostream& ss, const point_t &v) - { - crypto::public_key pk; - ge_p3_tobytes((unsigned char*)&pk, &v.m_p3); - return ss << epee::string_tools::pod_to_hex(pk); - } -}; // struct point_t - -struct point_g_t : public point_t -{ - point_g_t() - { - scalar_t one(1); - ge_scalarmult_base(&m_p3, &one.m_s[0]); - } - - friend point_t operator*(const scalar_t& lhs, const point_g_t&) - { - point_t result; - ge_scalarmult_base(&result.m_p3, &lhs.m_s[0]); - return result; - } - - friend point_t operator/(const point_g_t&, const scalar_t& rhs) - { - point_t result; - scalar_t reciprocal; - sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]); - ge_scalarmult_base(&result.m_p3, &reciprocal.m_s[0]); - return result; - } - - - - - static_assert(sizeof(crypto::public_key) == 32, "size error"); - -}; // struct point_g_t - -static const point_g_t c_point_G; - -static const scalar_t c_scalar_1 = { 1 }; -static const scalar_t c_scalar_L = { 0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; -static const scalar_t c_scalar_Lm1 = { 0x5812631a5cf5d3ec, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000 }; -static const scalar_t c_scalar_P = { 0xffffffffffffffed, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; -static const scalar_t c_scalar_Pm1 = { 0xffffffffffffffec, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff }; -static const scalar_t c_scalar_256m1 = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff }; - - -// H_s hash function -struct hash_helper_t -{ - static scalar_t hs(const scalar_t& s) - { - crypto::hash hash; - crypto::cn_fast_hash(s.data(), sizeof s, hash); - return scalar_t(hash); // will reduce mod L - } - - struct hs_t - { - hs_t() - { - static_assert(sizeof(scalar_t) == sizeof(crypto::public_key), "unexpected size of data"); - } - - void reserve(size_t elements_count) - { - m_elements.reserve(elements_count); - } - - void clear() - { - m_elements.clear(); - } - - void add_scalar(const scalar_t& scalar) - { - m_elements.emplace_back(scalar); - } - - void add_point(const point_t& point) - { - m_elements.emplace_back(point.to_public_key()); - } - - void add_points_array(const std::vector& points_array) - { - for (size_t i = 0, size = points_array.size(); i < size; ++i) - m_elements.emplace_back(points_array[i].to_public_key()); - } - - scalar_t calc_hash(bool clear = true) - { - size_t data_size_bytes = m_elements.size() * sizeof(item_t); - crypto::hash hash; - crypto::cn_fast_hash(m_elements.data(), data_size_bytes, hash); - if (clear) - this->clear(); - return scalar_t(hash); // this will reduce to L - } - - union item_t - { - item_t(const crypto::public_key& pk) : pk(pk) {} - item_t(const scalar_t& scalar) : scalar(scalar) {} - scalar_t scalar; - crypto::public_key pk; - }; - - std::vector m_elements; - }; - - /*static scalar_t hs(const scalar_t& s, const std::vector& ss, const std::vector& ps) - { - scalar_t result = 0; - return result; - }*/ - - static scalar_t hs(const scalar_t& s, const std::vector& ps0, const std::vector& ps1) - { - hs_t hs_calculator; - hs_calculator.add_scalar(s); - hs_calculator.add_points_array(ps0); - hs_calculator.add_points_array(ps1); - return hs_calculator.calc_hash(); - } - - static scalar_t hs(const std::vector& ps0, const std::vector& ps1) - { - hs_t hs_calculator; - hs_calculator.add_points_array(ps0); - hs_calculator.add_points_array(ps1); - return hs_calculator.calc_hash(); - } - - static point_t hp(const point_t& p) - { - point_t result; - crypto::public_key pk = p.to_public_key(); - - ge_bytes_hash_to_ec(&result.m_p3, (const unsigned char*)&pk); - - return result; - } -}; // // test helpers @@ -683,9 +192,115 @@ scalar_t scalar_from_str(const std::string& str) return result; } -//////////////////////////////////////////////////////////////////////////////// -#include "L2S.h" -//////////////////////////////////////////////////////////////////////////////// +crypto::hash hash_from_str(const std::string& str) +{ + crypto::hash hash; + if (!epee::string_tools::parse_tpod_from_hex_string(str, hash)) + throw std::runtime_error("couldn't parse hash"); + + return hash; +} + +std::string point_to_str(const point_t& point) +{ + crypto::public_key pk = point.to_public_key(); + return epee::string_tools::pod_to_hex(pk); +} + +std::string scalar_to_str(const scalar_t& scalar) +{ + return epee::string_tools::pod_to_hex(scalar); +} + +bool generate_test_ring_and_sec_keys(size_t N, size_t L, std::vector& ring, std::vector& secret_keys, + std::vector& ring_mapping, std::vector& key_images) +{ + secret_keys.resize(L); + for (size_t i = 0; i < L; ++i) + secret_keys[i].make_random(); + + std::vector fake_pub_keys(N / 2 - L); + for (size_t i = 0; i < fake_pub_keys.size(); ++i) + fake_pub_keys[i] = hash_helper_t::hp(i * c_point_G); + + ring_mapping.resize(N / 2); + for (size_t i = 0; i < N / 2; ++i) + ring_mapping[i] = i; + + std::shuffle(ring_mapping.begin(), ring_mapping.end(), crypto::uniform_random_bit_generator()); + + ring.resize(N / 2); + for (size_t i = 0; i < ring.size(); ++i) + { + if (i < L) + { + // own keys + ring[ring_mapping[i]] = secret_keys[i] * c_point_G; + } + else + { + // fake keys + ring[ring_mapping[i]] = fake_pub_keys[i - L]; + } + } + + ring_mapping.resize(L); + + key_images.resize(L); + for (size_t i = 0; i < L; ++i) + key_images[i] = hash_helper_t::hp(ring[ring_mapping[i]]) / secret_keys[i]; + + return true; +} + +bool generate_test_ring_and_sec_keys(size_t N, size_t L, std::vector& ring, std::vector& secret_keys, + std::vector& ring_mapping, std::vector & key_images) +{ + secret_keys.resize(L); + for (size_t i = 0; i < L; ++i) + secret_keys[i] = scalar_t::random().as_secret_key(); + + std::vector fake_pub_keys(N / 2 - L); + for (size_t i = 0; i < fake_pub_keys.size(); ++i) + fake_pub_keys[i] = hash_helper_t::hp(i * c_point_G); + + ring_mapping.resize(N / 2); + for (size_t i = 0; i < N / 2; ++i) + ring_mapping[i] = i; + + std::shuffle(ring_mapping.begin(), ring_mapping.end(), crypto::uniform_random_bit_generator()); + + ring.resize(N / 2); + for (size_t i = 0; i < ring.size(); ++i) + { + if (i < L) + { + // own keys + ring[ring_mapping[i]] = (secret_keys[i] * c_point_G).to_public_key(); + } + else + { + // fake keys + ring[ring_mapping[i]] = fake_pub_keys[i - L].to_public_key(); + } + } + + ring_mapping.resize(L); + + key_images.resize(L); + for (size_t i = 0; i < L; ++i) + key_images[i] = (hash_helper_t::hp(ring[ring_mapping[i]]) / secret_keys[i]).to_key_image(); + + return true; +} + +uint64_t hash_64(const void* data, size_t size) +{ + crypto::hash h = crypto::cn_fast_hash(data, size); + uint64_t* phash_as_array = (uint64_t*)&h; + return phash_as_array[0] ^ phash_as_array[1] ^ phash_as_array[2] ^ phash_as_array[3]; +} + @@ -704,7 +319,7 @@ scalar_t scalar_from_str(const std::string& str) static bool test_name_a ## _ ## test_name_b() #define ASSERT_TRUE(expr) CHECK_AND_ASSERT_MES(expr, false, "This is not true: " #expr) #define ASSERT_FALSE(expr) CHECK_AND_ASSERT_MES((expr) == false, false, "This is not false: " #expr) -#define ASSERT_EQ(a, b) CHECK_AND_ASSERT_MES(a == b, false, #a " != " #b) +#define ASSERT_EQ(a, b) CHECK_AND_ASSERT_MES(a == b, false, #a " != " #b "\n " << a << " != " << b) typedef bool(*bool_func_ptr_t)(); static std::vector> g_tests; @@ -717,12 +332,179 @@ struct test_keeper_t }; +//////////////////////////////////////////////////////////////////////////////// +#include "L2S.h" +//////////////////////////////////////////////////////////////////////////////// + // // Tests // +#include "crypto_tests_performance.h" + + +TEST(crypto, ge_scalarmult_vartime_p3) +{ + // make sure that my ge_scalarmult_vartime_p3 gives the same result as ge_scalarmul_p3 + + size_t N = 5000; + std::vector points; + points.push_back(0 * c_point_G); + points.push_back(1 * c_point_G); + points.push_back(c_scalar_Lm1 * c_point_G); + points.push_back(c_scalar_L * c_point_G); + points.push_back((c_scalar_L + 1) * c_point_G); + size_t i = points.size(); + points.resize(N); // should have kept previously added points + ASSERT_EQ(points[0], points[3]); + ASSERT_EQ(points[1], points[4]); + + for (; i < points.size(); ++i) + { + if (i & 1) + points[i] = scalar_t::random() * c_point_G; + else + points[i] = hash_helper_t::hp(points[i - 1]); + } + + for (size_t j = 0; j < points.size(); ++j) + { + scalar_t r; + r.make_random(); + + point_t A; + ge_scalarmult_p3(&A.m_p3, r.data(), &points[j].m_p3); + + point_t B; + ge_scalarmult_vartime_p3(&B.m_p3, r.data(), &points[j].m_p3); + + ASSERT_EQ(A, B); + } + + return true; +} + +size_t find_pos_hash(const boost::multiprecision::uint256_t& L_div_D, const currency::wide_difficulty_type& D, const uint64_t amount, + uint64_t& kernel, scalar_t& d0, uint64_t& d1) +{ + static const boost::multiprecision::uint256_t c_L_w = c_scalar_L.as_boost_mp_type(); + + crypto::generate_random_bytes(sizeof kernel, &kernel); + + const boost::multiprecision::uint512_t L_div_D_mul_v = boost::multiprecision::uint512_t(L_div_D) * amount; + scalar_t L_div_D_mul_v_sc; + if (L_div_D_mul_v < c_L_w) + L_div_D_mul_v_sc = scalar_t(L_div_D_mul_v); // here we assured that L_div_D_mul_v < 2**256 + else + L_div_D_mul_v_sc = scalar_t(c_L_w); // too small D or too big amount, so any h would go + + size_t i = 0; + for (; i < 1000000; ++i) + { + scalar_t h = hash_helper_t::hs(&kernel, sizeof kernel); + if (h < L_div_D_mul_v_sc) + { + // found! + boost::multiprecision::uint512_t h_w = h.as_boost_mp_type(); + boost::multiprecision::uint512_t d0_w = h_w / amount * D; + ASSERT_TRUE(d0_w < c_L_w); + d0 = scalar_t(d0_w); + + boost::multiprecision::uint512_t ddv = (d0_w / D) * amount; + ASSERT_TRUE(h_w < ddv); + if (h_w == ddv) + { + ASSERT_TRUE(false); + } + + boost::multiprecision::uint512_t d1_w = ddv - h_w; + ASSERT_TRUE(d1_w <= UINT64_MAX); + + d1 = d1_w.convert_to(); + break; + } + ++kernel; + } + + return i; +} + +TEST(crypto, pos) +{ + //scalar_t D = 10000000000000001u; + scalar_t D = 13042196742415129u; // prime number + currency::wide_difficulty_type D_w = D.as_boost_mp_type().convert_to(); + uint64_t amount = 1000000000000; + size_t count_old = 0; + size_t count_new = 0; + size_t count_3 = 0; + scalar_t x; + x.make_random(); + + const boost::multiprecision::uint512_t c_2_pow_256_m1(std::numeric_limits::max()); + + + const boost::multiprecision::uint256_t c_L_w = c_scalar_L.as_boost_mp_type(); + const boost::multiprecision::uint256_t c_L_div_D_w = c_L_w / D_w; + boost::multiprecision::uint512_t h_tres = c_L_div_D_w * amount; + + currency::wide_difficulty_type final_diff = D_w / amount; + + boost::multiprecision::uint512_t Lv = boost::multiprecision::uint512_t(c_L_w) * amount; + + constexpr uint64_t COIN = 1000000000000; + const uint64_t amounts[] = { + COIN / 100, + COIN / 50, + COIN / 20, + COIN / 10, + COIN / 5, + COIN / 2, + COIN * 1, + COIN * 2, + COIN * 5, + COIN * 10, + COIN * 20, + COIN * 50, + COIN * 100, + COIN * 200, + COIN * 500, + COIN * 1000, + COIN * 2000, + COIN * 5000, + COIN * 10000, + COIN * 20000, + COIN * 50000, + COIN * 100000, + COIN * 200000, + COIN * 500000 + }; + + uint64_t kernel = 0; + scalar_t d0 = 0; + uint64_t d1 = 0; + + + /* + for (size_t i = 0, size = sizeof pos_diffs / sizeof pos_diffs[0]; i < size; ++i) + { + auto& D = pos_diffs[i].difficulty; + boost::multiprecision::uint256_t L_dvi_D = c_L_w / D; + + for (size_t j = 0, size_j = sizeof amounts / sizeof amounts[0]; j < size_j; ++j) + { + uint64_t amount = rand_in_range(amounts[j], amounts[j] + amounts[j] / 10); + size_t iter = find_pos_hash(L_dvi_D, D, amount, kernel, d0, d1); + LOG_PRINT_L0(i << ", " << amount << ", " << iter); + } + } + */ + + return true; +} + struct sig_check_t { crypto::hash prefix_hash; @@ -743,7 +525,7 @@ struct sig_check_t crypto::generate_random_bytes(sizeof prefix_hash, &prefix_hash); - for (size_t i = 0; i < decoy_set_size; ++i) + for (size_t i = 0; i + 1 < decoy_set_size; ++i) { crypto::public_key p; crypto::secret_key s; @@ -776,14 +558,17 @@ struct sig_check_t TEST(crypto, ring_sigs) { + return true; size_t n = 1000; - size_t decoy_set = 2; + size_t decoy_set_size = 8; + + std::cout << "using decoy set with size = " << decoy_set_size << std::endl; std::vector sigs; sigs.resize(n); for (size_t i = 0; i < sigs.size(); ++i) - sigs[i].prepare_random_data(decoy_set); + sigs[i].prepare_random_data(decoy_set_size); std::cout << n << " random sigs prepared" << std::endl; @@ -867,13 +652,19 @@ TEST(crypto, scalar_basics) scalar_t one = 1; ASSERT_FALSE(one.is_zero()); ASSERT_TRUE(one > zero); + ASSERT_TRUE(one.muladd(zero, zero) == zero); + scalar_t z = 0; for (size_t j = 0; j < 1000; ++j) { z.make_random(); ASSERT_FALSE(z.is_zero()); + ASSERT_TRUE(z.is_reduced()); ASSERT_TRUE(z > z - 1); ASSERT_TRUE(z < z + 1); + ASSERT_TRUE(z.muladd(one, zero) == z); + ASSERT_TRUE(z.muladd(zero, one) == one); + ASSERT_TRUE(z.muladd(z, z) == z * z + z); } ASSERT_TRUE(c_scalar_L > 0 && !(c_scalar_L < 0)); @@ -902,6 +693,19 @@ TEST(crypto, scalar_basics) ASSERT_EQ(scalar_t(3) / c_scalar_Lm1, scalar_t(3) * c_scalar_Lm1); // because (L - 1) ^ 2 = 1 + // check is_reduced + ASSERT_TRUE(c_scalar_Lm1.is_reduced()); + ASSERT_FALSE(c_scalar_L.is_reduced()); + scalar_t p = c_scalar_L; + ASSERT_FALSE(p.is_reduced()); + p = p + 1; + ASSERT_TRUE(p.is_reduced()); + p = 0; + p = p + c_scalar_P; + ASSERT_TRUE(p.is_reduced()); + mp::uint256_t mp_p_mod_l = c_scalar_P.as_boost_mp_type() % c_scalar_L.as_boost_mp_type(); + ASSERT_EQ(p, scalar_t(mp_p_mod_l)); + return true; } @@ -925,6 +729,116 @@ TEST(crypto, sc_mul_performance) return true; } +TEST(crypto, hp) +{ + bool r = false; + scalar_t sk; + r = epee::string_tools::parse_tpod_from_hex_string("407b3b73df8f11737494bdde6ca47a42e1b537390aec2fa781a2d170335c440f", sk); + ASSERT_TRUE(r); + crypto::public_key pk; + r = epee::string_tools::parse_tpod_from_hex_string("1b546af91d31fdb1c476fd62fbb65b6fd5ed47804185fc77d48bc4cc00f47ef0", pk); + ASSERT_TRUE(r); + + point_t P; + ASSERT_TRUE(P.from_public_key(pk)); + // make sure pk and sk are pair + ASSERT_EQ(P, sk * c_point_G); + + // make sure generate_key_image does the same as sk * hash_helper::hp(pk) + crypto::key_image ki; + crypto::generate_key_image(pk, sk.as_secret_key(), ki); + point_t KI; + ASSERT_TRUE(KI.from_public_key((crypto::public_key&)ki)); // key image is a point + + ASSERT_EQ(KI, sk * hash_helper_t::hp(P)); + + LOG_PRINT_L0(sk.as_secret_key() << " * G = " << P); + + point_t P1 = hash_helper_t::hp(P); + LOG_PRINT_L0("Hp(" << P << ") = " << P1); + ASSERT_EQ(P1, point_from_str("f9506848342ddb23b014e5975462757f5d296a6acfa0e9837ff940f7655becdb")); + + point_t P100 = P; + for (size_t i = 0; i < 100; ++i) + P100 = hash_helper_t::hp(P100); + + ASSERT_EQ(P100, point_from_str("925f195fc629fa15f768c775f7eed3a43dcd45c702974d161eb610a9ab9df4f0")); + + + crypto::hash hash; + crypto::cn_fast_hash(sk.data(), sizeof sk, hash); + + LOG_PRINT_L0("cn_fast_hash(" << sk.as_secret_key() << ") = " << hash); + ASSERT_EQ(hash, hash_from_str("ee05b0f64eebf20da306eec142da99283154316391caf474be41ff010afb4298")); + + crypto::cn_fast_hash(nullptr, 0, hash); + LOG_PRINT_L0("cn_fast_hash('') = " << hash); + ASSERT_EQ(hash, hash_from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + + scalar_t z(hash); + point_t zG = z * c_point_G; + LOG_PRINT_L0("cn_fast_hash('') * G = " << zG.to_public_key() << " (pub_key)"); + ASSERT_EQ(zG, point_from_str("7849297236cd7c0d6c69a3c8c179c038d3c1c434735741bb3c8995c3c9d6f2ac")); + + crypto::cn_fast_hash("zano", 4, hash); + LOG_PRINT_L0("cn_fast_hash('zano') = " << hash); + ASSERT_EQ(hash, hash_from_str("23cea10abfdf3ace0b7132291d51e4eb5a392afb2147e67f907ff4f8f5dd4f9f")); + + z = hash; + zG = z * c_point_G; + LOG_PRINT_L0("cn_fast_hash('zano') * G = " << zG.to_public_key() << " (pub_key)"); + ASSERT_EQ(zG, point_from_str("71407d59e9d671fa02f26a6a7f4726c3087d8f1732453396638a1dc2929fb57a")); + + char buf[2000]; + for (size_t i = 0; i < sizeof buf; i += 4) + *(uint32_t*)&buf[i] = *(uint32_t*)"zano"; + crypto::cn_fast_hash(buf, sizeof buf, (char*)&hash); + LOG_PRINT_L0("cn_fast_hash('zano' x 500) = " << hash); + ASSERT_EQ(hash, hash_from_str("16d87120c601a6ef3e4ffa5e58176a36b814288199f23ec09ef178c554e8879b")); + + z = hash; + zG = z * c_point_G; + LOG_PRINT_L0("cn_fast_hash('zano' x 500) * G = " << zG.to_public_key() << " (pub_key)"); + ASSERT_EQ(zG, point_from_str("dd93067a02fb8661aa64504ac1503402a34426f43650d970c35147cec4b61d55")); + + return true; +} + +TEST(crypto, cn_fast_hash_perf) +{ + return true; + crypto::hash h = { 3, 14, 15, 9, 26 }; + + size_t n = 100000; + double diff_sum = 0; + + for (size_t j = 0; j < 20; ++j) + { + TIME_MEASURE_START(t_old); + for (size_t i = 0; i < n; ++i) + cn_fast_hash_old(&h, sizeof h, (char*)&h); + TIME_MEASURE_FINISH(t_old); + + TIME_MEASURE_START(t); + for (size_t i = 0; i < n; ++i) + cn_fast_hash(&h, sizeof h, (char*)&h); + TIME_MEASURE_FINISH(t); + + double diff = ((int64_t)t_old - (int64_t)t) / (double)n; + + LOG_PRINT_L0("cn_fast_hash (old/new): " << std::fixed << std::setprecision(3) << t_old / (double)n << " " << + std::fixed << std::setprecision(3) << t * 1.0 / n << " mcs diff => " << std::fixed << std::setprecision(4) << diff); + + diff_sum += diff; + } + + std::cout << h << " diff sum: " << diff_sum << std::endl; + + return true; +} + + + TEST(crypto, sc_invert_performance) { std::vector scalars(10000); @@ -1014,6 +928,19 @@ TEST(crypto, point_basics) ASSERT_EQ(E, c_point_G + c_point_G + c_point_G + c_point_G); ASSERT_EQ(E - c_point_G, 3 * c_point_G); + for (size_t i = 0; i < 1000; ++i) + { + E = hash_helper_t::hp(E); + point_t Z = 0 * E; + ASSERT_TRUE(Z.is_zero()); + scalar_t rnd; + rnd.make_random(); + Z = rnd * E; + Z = Z - E; + Z = Z - (rnd - 1) * E; + ASSERT_TRUE(Z.is_zero()); + } + return true; } @@ -1072,6 +999,42 @@ TEST(ml2s, rsum) TEST(ml2s, hs) { + mp::uint512_t L = c_scalar_L.as_boost_mp_type(); + L *= 15; + mp::uint256_t c_256_bit_max = ((mp::uint512_t(1) << 256) - 1).convert_to(); + std::cout << std::hex << c_256_bit_max << ENDL; + + ASSERT_TRUE(L < c_256_bit_max); + + scalar_t L15(L); + + scalar_t k = epee::string_tools::hex_to_pod("deefd263cbfed62a3711dd133df3ccbd1c4dc4aac21d7405fd667498bf8ebaa1"); + std::cout << k << ENDL; + std::cout << k.to_string_as_secret_key() << ENDL; + mp::uint256_t kw = k.as_boost_mp_type(); + sc_reduce32(k.m_s); + std::cout << k << ENDL; + std::cout << k.to_string_as_secret_key() << ENDL; + + std::cout << kw << ENDL; + kw = kw % c_scalar_L.as_boost_mp_type(); + std::cout << kw << ENDL; + + + for (size_t i = 0; i < 100000; ++i) + { + scalar_t x, y; + crypto::generate_random_bytes(32, y.m_s); + x = y; + memset(x.m_s + 18, 0xff, 13); + sc_reduce32(x.m_s); + uint64_t lo = *(uint64_t*)(x.m_s + 18); + uint64_t hi = *(uint64_t*)(x.m_s + 26) & 0xffffffffff; + ASSERT_EQ(lo, 0xffffffffffffffff); + ASSERT_EQ(hi, 0xffffffffff); + } + + scalar_t x = 2, p = 250; //sc_exp(r.data(), x.data(), p.data()); @@ -1081,34 +1044,383 @@ TEST(ml2s, hs) scalar_t r; sha3(0, 0, &h, sizeof h); - LOG_PRINT("SHA3 0 -> " << h, LOG_LEVEL_0); - LOG_PRINT("SHA3 0 -> " << (scalar_t&)h, LOG_LEVEL_0); + LOG_PRINT("SHA3() -> " << h, LOG_LEVEL_0); + LOG_PRINT("SHA3() -> " << (scalar_t&)h, LOG_LEVEL_0); h = crypto::cn_fast_hash(0, 0); - LOG_PRINT("CN 0 -> " << h, LOG_LEVEL_0); - LOG_PRINT("CN 0 -> " << (scalar_t&)h, LOG_LEVEL_0); + LOG_PRINT("CN() -> " << h, LOG_LEVEL_0); + LOG_PRINT("CN() -> " << (scalar_t&)h, LOG_LEVEL_0); std::string abc("abc"); sha3(abc.c_str(), abc.size(), &h, sizeof h); - LOG_PRINT(abc << " -> " << h, LOG_LEVEL_0); - LOG_PRINT(abc << " -> " << (scalar_t&)h, LOG_LEVEL_0); + LOG_PRINT("SHA3(" << abc << ") -> " << h, LOG_LEVEL_0); + LOG_PRINT("SHA3(" << abc << ") -> " << (scalar_t&)h, LOG_LEVEL_0); h = crypto::cn_fast_hash(abc.c_str(), abc.size()); - LOG_PRINT(abc << " -> " << h, LOG_LEVEL_0); - LOG_PRINT(abc << " -> " << (scalar_t&)h, LOG_LEVEL_0); + LOG_PRINT("CN(" << abc << ") -> " << h, LOG_LEVEL_0); + LOG_PRINT("CN(" << abc << ") -> " << (scalar_t&)h, LOG_LEVEL_0); return true; } +TEST(ml2s, hsc) +{ + hash_helper_t::hs_t hsc; + hsc.add_point(c_point_G); + LOG_PRINT_L0("hsc(G) = " << hsc.calc_hash().as_secret_key()); + + hsc.add_point(c_point_G); + hsc.add_scalar(scalar_from_str("0b2900d8eaf9996d2c5345833b280ef93be5c6881dc26f89ecad7a86441cc10e")); + hsc.add_point(c_point_G); + LOG_PRINT_L0("hsc(GsG) = " << hsc.calc_hash().as_secret_key()); + + return true; +} + + +TEST(ml2s, py2cpp) +{ + // verify a signature generated by python's reference code + + std::vector B_array = { point_from_str("2f1132ca61ab38dff00f2fea3228f24c6c71d58085b80e47e19515cb27e8d047"), point_from_str("5866666666666666666666666666666666666666666666666666666666666666") }; + ml2s_signature sig; + sig.z = scalar_from_str("0b2900d8eaf9996d2c5345833b280ef93be5c6881dc26f89ecad7a86441cc10e"); + { + ml2s_signature_element e; + e.H_array = { point_from_str("de6169b6f9e4e3dcf450c87f5f9cc5954db82fea3a0afccf285ffff0590cc343"), point_from_str("0d09ad5376090d844afde60f7b0ba78e8010987724e76eeb1f73d490e26ef114") }; + e.T = point_from_str("8df512bbe80238bbb5579e41caab69f209c35560f03a7fdde5d1369b059e38f8"); + e.T0 = point_from_str("fb973d514bb0df3f2bb30038dfe50164a9cd48ef6e7b9ac7a253cbc328f214ab"); + e.Z = point_from_str("27b77c99771a4d8198fd5ebcfae71dc063bd2286006fb9f4a72b72dbcf552c30"); + e.Z0 = point_from_str("a6dd57fcfb5d00c9ad866df931732df491fa0dde14ac76a817399fc2e212666d"); + e.r_array = { scalar_from_str("12921a30105714028187e9ecccf2106d04f6e08dbe7d07001fe3e93a1ffa6e04"), scalar_from_str("fbe2fb0ff0481309db53791b77557eab7359d6cf8274d8dac6ad5092075ec009") }; + e.t = scalar_from_str("002fca7de8af4c57b94b4b49ba2792644dddf09baf729fe31be1c265d2df2e0e"); + e.t0 = scalar_from_str("e0c20ec9adbb047d3b26f8073b49e21e700b9bfe6601b6565ec5ec0e83ad230a"); + sig.elements.emplace_back(e); + } + { + ml2s_signature_element e; + e.H_array = { point_from_str("661f05591cc6e0a42bc94b5f9d45318248051368306bc199d555306598c6330a"), point_from_str("5281aa630ee6e7ef9af5d758fd98fbf72680e8a97e3329827c9ba24577b06aea") }; + e.T = point_from_str("97463bbf9bf49474853e4e68bb927b8aeb53472b33315930583887ee63d0e341"); + e.T0 = point_from_str("e4877f4e8a9dd8a57955a13a66a2ced96f3e7f2a64ff40beef9641ad4eecbf91"); + e.Z = point_from_str("24a64d04e530772d23a3455047b89569fe796d002dc3d40b57f2ab350eddff47"); + e.Z0 = point_from_str("8db841a90faac3c019f42824fac7177505ca9fb51a21ba0fd8d4112ec9dd9e89"); + e.r_array = { scalar_from_str("52038eb19d0eaffe10cc379ce5a739557adccf4a95ccf77c3159c71d2f166b0f"), scalar_from_str("aebb28319c552708ad2495802a3f585780ae1286146d6e5a845e3074607c0a03") }; + e.t = scalar_from_str("285a411ddcf56747fe11f5053ec3bff7617ad3ed9c6f1929874d90d91fb7680a"); + e.t0 = scalar_from_str("44552f3136dbee1b5b63d4a1a9d8799aed54ddd75ed9ebdf6de0a6e032ac7505"); + sig.elements.emplace_back(e); + } + + scalar_t m = 31337; + + uint8_t err = 0; + bool r = ml2s_lnk_sig_verif(m, B_array, sig, &err); + + ASSERT_TRUE(r); + + return true; +} + +std::string ml2s_sig_to_python(const std::vector& B_array, const ml2s_signature& sig, const std::vector& I_array) +{ + std::string str; + if (B_array.empty() || sig.elements.empty()) + return str; + + auto points_array_to_dict = [](const std::vector& v) -> std::string + { + std::string s; + size_t idx = 1; + for (auto& p : v) + s += epee::string_tools::num_to_string_fast(idx++) + ": ed25519.Point('" + p.to_string() + "'), "; + if (s.size() > 2) + s.erase(s.end() - 2, s.end()); + return s; + }; + + auto points_array_to_array = [](const std::vector& v) -> std::string + { + std::string s; + for (auto& p : v) + s += "ed25519.Point('" + p.to_string() + "'), "; + if (s.size() > 2) + s.erase(s.end() - 2, s.end()); + return s; + }; + + auto scalars_array_to_dict = [](const std::vector& v) -> std::string + { + std::string s; + size_t idx = 1; + for (auto& p : v) + s += epee::string_tools::num_to_string_fast(idx++) + ": 0x" + p.to_string_as_hex_number() + ", "; + if (s.size() > 2) + s.erase(s.end() - 2, s.end()); + return s; + }; + + std::string indent = " "; + + str += indent + "B_array = [ "; + for (auto& B : B_array) + str += std::string("ed25519.Point('") + B.to_string() + "'), "; + str.erase(str.end() - 2, str.end()); + str += " ]\n"; + + str += indent + "signature = (0x" + sig.z.to_string_as_hex_number() + ", [\n"; + for (size_t i = 0; i < sig.elements.size(); ++i) + { + const auto& sel = sig.elements[i]; + str += indent + indent + "{\n"; + str += indent + indent + indent + "'H' : { " + points_array_to_dict(sel.H_array) + " },\n"; + str += indent + indent + indent + "'T' : ed25519.Point('" + sel.T.to_string() + "'),\n"; + str += indent + indent + indent + "'T0' : ed25519.Point('" + sel.T0.to_string() + "'),\n"; + str += indent + indent + indent + "'Z' : ed25519.Point('" + sel.Z.to_string() + "'),\n"; + str += indent + indent + indent + "'Z0' : ed25519.Point('" + sel.Z0.to_string() + "'),\n"; + str += indent + indent + indent + "'r' : { " + scalars_array_to_dict(sel.r_array) + " },\n"; + str += indent + indent + indent + "'t' : 0x" + sel.t.to_string_as_hex_number() + ",\n"; + str += indent + indent + indent + "'t0' : 0x" + sel.t0.to_string_as_hex_number() + "\n"; + str += indent + indent + "},\n"; + } + str.erase(str.end() - 2, str.end()); + str += "\n"; + str += indent + "])\n"; + str += "\n"; + + str += indent + "I_reference = [ " + points_array_to_array(I_array) + " ]\n"; + + return str; +} + +TEST(ml2s, cpp2py) +{ + // Generate a random sig and python code to check it + scalar_t m; + m.make_random(); + size_t n = 8; + size_t N = 1ull << n; + size_t L = 8; + + // generate a signature + + std::vector ring; + std::vector secret_keys; + std::vector ring_mapping; + std::vector key_images; + generate_test_ring_and_sec_keys(N, L, ring, secret_keys, ring_mapping, key_images); + + ml2s_signature sig; + ASSERT_TRUE(ml2s_lnk_sig_gen(m, ring, secret_keys, ring_mapping, sig)); + + ASSERT_TRUE(ml2s_lnk_sig_verif(m, ring, sig, nullptr, &key_images)); + + // generate Python code + + std::string str, indent = " "; + str += "import ed25519\n"; + str += "import L2S\n"; + str += "\n"; + str += "def check_sig():\n"; + str += ml2s_sig_to_python(ring, sig, key_images); + str += indent + "m = 0x" + m.to_string_as_hex_number() + "\n"; + str += indent + "result = L2S.mL2SLnkSig_Verif(len(B_array) * 2, B_array, m, signature)\n"; + str += indent + "print(f\"Verif returned : {result}\")\n"; + str += indent + "\n"; + str += indent + "if result != I_reference:\n"; + str += indent + indent + "print(\"ERROR: Key images don't match\")\n"; + str += indent + indent + "return False\n"; + str += indent + "\n"; + str += indent + "return True\n"; + str += "\n"; + str += "if check_sig():\n"; + str += indent + "print(\"Signature verified, key images matched\")\n"; + + epee::file_io_utils::save_string_to_file("ml2s_sig_check_test.py", str); + + return true; +} + + + +TEST(ml2s, sig_verif_performance) +{ + // sig inputs + scalar_t m = 31337; + size_t n = 8; + size_t N = 1ull << n; + size_t L = 8; + + // perf counters + uint64_t t_sig = 0; + uint64_t t_verif = 0; + size_t tests_cnt = 20; + + LOG_PRINT_L0("N / 2 = " << N / 2 << ", L = " << L); + for (size_t p = 0; p < tests_cnt; ++p) + { + std::vector ring; + std::vector secret_keys; + std::vector ring_mapping; + std::vector key_images; + generate_test_ring_and_sec_keys(N, L, ring, secret_keys, ring_mapping, key_images); + + ml2s_signature sig; + uint8_t err = 0; + TIME_MEASURE_START(time_sig); + bool r = ml2s_lnk_sig_gen(m, ring, secret_keys, ring_mapping, sig, &err); + TIME_MEASURE_FINISH(time_sig); + t_sig += time_sig; + ASSERT_TRUE(r); + + err = 0; + TIME_MEASURE_START(time_verif); + r = ml2s_lnk_sig_verif(m, ring, sig, &err); + TIME_MEASURE_FINISH(time_verif); + t_verif += time_verif; + ASSERT_TRUE(r); + + LOG_PRINT_L0(" stats over " << p + 1 << " runs:"); + LOG_PRINT_L0("ml2s_lnk_sig_gen avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_sig / double(p + 1) / 1000.0 << " ms"); + LOG_PRINT_L0("ml2s_lnk_sig_verif avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_verif / double(p + 1) / 1000.0 << " ms"); + } + + return true; +} + +TEST(ml2s, sig_verif_performance_2) +{ + // sig inputs + scalar_t m = 31337; + crypto::hash mh = crypto::cn_fast_hash(&m, sizeof m); + size_t n = 8; + size_t N = 1ull << n; + size_t L = 1; + + // perf counters + uint64_t t_sig = 0; + uint64_t t_verif = 0; + uint64_t t_sig_v2 = 0; + uint64_t t_verif_v2 = 0; + uint64_t t_sig_v3 = 0; + uint64_t t_verif_v3 = 0; + size_t tests_cnt = 200; + + LOG_PRINT_L0("N / 2 = " << N / 2 << ", L = " << L); + for (size_t p = 0; p < tests_cnt; ++p) + { + std::vector ring; + std::vector secret_keys; + std::vector ring_mapping; + std::vector key_images; + generate_test_ring_and_sec_keys(N, L, ring, secret_keys, ring_mapping, key_images); + + ml2s_signature sig; + uint8_t err = 0; + TIME_MEASURE_START(time_sig); + bool r = ml2s_lnk_sig_gen(m, ring, secret_keys, ring_mapping, sig, &err); + TIME_MEASURE_FINISH(time_sig); + t_sig += time_sig; + ASSERT_TRUE(r); + + err = 0; + TIME_MEASURE_START(time_verif); + r = ml2s_lnk_sig_verif(m, ring, sig, &err); + TIME_MEASURE_FINISH(time_verif); + t_verif += time_verif; + ASSERT_TRUE(r); + + /////////////////////////// + // v2 + ml2s_signature_v2 sig_v2; + err = 0; + TIME_MEASURE_START(time_sig_v2); + r = ml2s_lnk_sig_gen_v2(m, ring, secret_keys, ring_mapping, key_images, sig_v2, &err); + TIME_MEASURE_FINISH(time_sig_v2); + t_sig_v2 += time_sig_v2; + ASSERT_TRUE(r); + + err = 0; + TIME_MEASURE_START(time_verif_v2); + r = ml2s_lnk_sig_verif_v2(m, ring, key_images, sig_v2, &err); + TIME_MEASURE_FINISH(time_verif_v2); + t_verif_v2 += time_verif_v2; + ASSERT_TRUE(r); + + /////////////////////////// + // v3 + { + std::vector ring; + std::vector secret_keys; + std::vector ring_mapping; + std::vector key_images; + generate_test_ring_and_sec_keys(N, L, ring, secret_keys, ring_mapping, key_images); + + ml2s_signature_v3 sig_v3; + err = 0; + TIME_MEASURE_START(time_sig_v3); + r = ml2s_lnk_sig_gen_v3(mh, ring, secret_keys, ring_mapping, key_images, sig_v3, &err); + TIME_MEASURE_FINISH(time_sig_v3); + t_sig_v3 += time_sig_v3; + ASSERT_TRUE(r); + + err = 0; + TIME_MEASURE_START(time_verif_v3); + r = ml2s_lnk_sig_verif_v3(mh, ring, key_images, sig_v3, &err); + TIME_MEASURE_FINISH(time_verif_v3); + t_verif_v3 += time_verif_v3; + ASSERT_TRUE(r); + } + + + LOG_PRINT_L0(" stats over " << p + 1 << " runs:"); + LOG_PRINT_L0("ml2s_lnk_sig_gen avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_sig / double(p + 1) / 1000.0 << " ms"); + LOG_PRINT_L0("ml2s_lnk_sig_gen_v2 avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_sig_v2 / double(p + 1) / 1000.0 << " ms"); + LOG_PRINT_L0("ml2s_lnk_sig_gen_v3 avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_sig_v3 / double(p + 1) / 1000.0 << " ms"); + LOG_PRINT_L0("ml2s_lnk_sig_verif avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_verif / double(p + 1) / 1000.0 << " ms"); + LOG_PRINT_L0("ml2s_lnk_sig_verif_v2 avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_verif_v2 / double(p + 1) / 1000.0 << " ms"); + LOG_PRINT_L0("ml2s_lnk_sig_verif_v3 avg: " << std::right << std::setw(8) << std::fixed << std::setprecision(1) << t_verif_v3 / double(p + 1) / 1000.0 << " ms"); + } + + return true; +} + + // // test's runner // -int crypto_tests() +bool wildcard_match(const std::string& needle, const std::string& haystack) { - epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_1); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); + size_t h = 0; + for (size_t n = 0; n < needle.size(); ++n) + { + switch (needle[n]) + { + case '?': + if (h == haystack.size()) + return false; + ++h; + break; + case '*': + if (n + 1 == needle.size()) + return true; + for (size_t i = 0; i + h < haystack.size(); i++) + if (wildcard_match(needle.substr(n + 1), haystack.substr(h + i))) + return true; + return false; + default: + if (haystack[h] != needle[n]) + return false; + ++h; + } + } + return h == haystack.size(); +} + +int crypto_tests(const std::string& cmd_line_param) +{ + epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3); + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_3); epee::log_space::log_singletone::add_logger(LOGGER_FILE, epee::log_space::log_singletone::get_default_log_file().c_str(), epee::log_space::log_singletone::get_default_log_folder().c_str()); @@ -1119,6 +1431,11 @@ int crypto_tests() for (size_t i = 0; i < g_tests.size(); ++i) { auto& test = g_tests[i]; + + if (!wildcard_match(cmd_line_param.c_str(), test.first.c_str())) + continue; + + LOG_PRINT(" " << std::setw(40) << std::left << test.first << " >", LOG_LEVEL_0); TIME_MEASURE_START(runtime); bool r = false; try @@ -1138,11 +1455,11 @@ int crypto_tests() uint64_t runtime_mcs = runtime % 1000; if (r) { - LOG_PRINT_GREEN(" " << std::setw(40) << std::left << test.first << "OK [" << runtime_ms << "." << std::setw(3) << std::setfill('0') << runtime_mcs << " ms]", LOG_LEVEL_0); + LOG_PRINT_GREEN(" " << std::setw(40) << std::left << test.first << "OK [" << runtime_ms << "." << std::setw(3) << std::setfill('0') << runtime_mcs << " ms]", LOG_LEVEL_0); } else { - LOG_PRINT_RED(ENDL << " " << std::setw(40) << std::left << test.first << "FAILED" << ENDL, LOG_LEVEL_0); + LOG_PRINT_RED(ENDL << " " << std::setw(40) << std::left << test.first << "FAILED" << ENDL, LOG_LEVEL_0); failed_tests.push_back(i); } } diff --git a/tests/functional_tests/crypto_tests.h b/tests/functional_tests/crypto_tests.h index 492b27e0..16290dd4 100644 --- a/tests/functional_tests/crypto_tests.h +++ b/tests/functional_tests/crypto_tests.h @@ -3,4 +3,4 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once -int crypto_tests(); +int crypto_tests(const std::string& cmd_line_param); diff --git a/tests/functional_tests/crypto_tests_performance.h b/tests/functional_tests/crypto_tests_performance.h new file mode 100644 index 00000000..b686bbad --- /dev/null +++ b/tests/functional_tests/crypto_tests_performance.h @@ -0,0 +1,666 @@ +// Copyright (c) 2021 Zano Project +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + +TEST(crypto, primitives) +{ + struct helper + { + static void make_rnd_indicies(std::vector& v, size_t size) + { + v.resize(size); + for (size_t i = 0; i < size; ++i) + v[i] = i; + std::shuffle(v.begin(), v.end(), crypto::uniform_random_bit_generator()); + }; + }; + + struct timer_t + { + std::chrono::high_resolution_clock::time_point m_tp{}; + uint64_t m_t{}; + uint64_t m_div_coeff{ 1 }; + void start(uint64_t div_coeff = 1) { m_tp = std::chrono::high_resolution_clock::now(); m_div_coeff = div_coeff; } + void stop() { m_t = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_tp).count(); } + uint64_t get_time_mcs() { return m_div_coeff == 1 ? m_t : m_t / m_div_coeff; } + }; + + typedef uint64_t(*run_func_t)(timer_t& t, size_t rounds); + + + + auto run = [](const std::string& title, size_t rounds, run_func_t cb) + { + uint64_t result; + timer_t t_warmup, t, t_total; + t_total.start(); + result = cb(t_warmup, rounds); + result += cb(t, rounds); + t_total.stop(); + double run_time_mcs_x_100 = double(uint64_t(t.get_time_mcs() / (rounds / 100))); + LOG_PRINT_L0(std::left << std::setw(40) << title << std::setw(7) << rounds << " rnds -> " + << std::right << std::setw(7) << std::fixed << std::setprecision(2) << run_time_mcs_x_100 / 100.0 << " mcs avg. (gross: " + << std::fixed << std::setprecision(2) << double(t_total.get_time_mcs()) / 1000.0 << " ms), result hash: " << result); + }; + +#define HASH_64_VEC(vec_var_name) hash_64(vec_var_name.data(), vec_var_name.size() * sizeof(vec_var_name[0])) + + LOG_PRINT_L0(ENDL << "native crypto primitives:"); + + run("sc_reduce", 30000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + struct bytes64 + { + unsigned char b[64]; + }; + + std::vector scalars_64(rounds); + for (size_t i = 0; i < scalars_64.size(); ++i) + crypto::generate_random_bytes(sizeof(bytes64), scalars_64[i].b); + + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + sc_reduce(scalars_64[rnd_indecies[i]].b); + } + t.stop(); + + return HASH_64_VEC(scalars_64); + }); + + run("sc_reduce32", 30000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector scalars(rounds); + for (size_t i = 0; i < scalars.size(); ++i) + crypto::generate_random_bytes(sizeof(crypto::ec_scalar), scalars[i].data); + + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + sc_reduce32((unsigned char*)&scalars[rnd_indecies[i]].data); + } + t.stop(); + + return HASH_64_VEC(scalars); + }); + + run("sc_mul", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector a(rounds), b(rounds); + for (size_t i = 0; i < rounds; ++i) + { + a[i].make_random(); + b[i].make_random(); + } + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + sc_mul(result[rnd_indecies[i]].m_s, a[rnd_indecies[i]].m_s, b[rnd_indecies[i]].m_s); + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sc_add", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector a(rounds), b(rounds); + for (size_t i = 0; i < rounds; ++i) + { + a[i].make_random(); + b[i].make_random(); + } + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + sc_add(result[i].m_s, a[rnd_indecies[i]].m_s, b[rnd_indecies[i]].m_s); + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sc_mul + sc_add", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector a(rounds), b(rounds), c(rounds); + for (size_t i = 0; i < rounds; ++i) + { + a[i].make_random(); + b[i].make_random(); + c[i].make_random(); + } + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + scalar_t tmp; + sc_mul(tmp.m_s, a[rnd_indecies[i]].m_s, b[rnd_indecies[i]].m_s); // tmp = a * b + sc_add(result[i].m_s, tmp.m_s, c[rnd_indecies[i]].m_s); // result = tmp + c + } + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sc_muladd", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector a(rounds), b(rounds), c(rounds); + for (size_t i = 0; i < rounds; ++i) + { + a[i].make_random(); + b[i].make_random(); + c[i].make_random(); + } + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + sc_muladd(result[i].m_s, a[rnd_indecies[i]].m_s, b[rnd_indecies[i]].m_s, c[rnd_indecies[i]].m_s); + t.stop(); + + return HASH_64_VEC(result); + }); + + run("ge_p3_tobytes", 10000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector points_p3(rounds); + ge_scalarmult_base(&points_p3[0], c_scalar_1.data()); + for (size_t i = 1; i < points_p3.size(); ++i) + ge_bytes_hash_to_ec_32(&points_p3[i], (const unsigned char*)&points_p3[i - 1].X); // P_{i+1} = Hp(P_i.X) + + std::vector points(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_p3_tobytes((unsigned char*)points[i].data, &points_p3[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points); + }); + + run("ge_frombytes_vartime(p3)", 10000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + point_t P = c_point_G; + + std::vector points_p3_bytes(rounds); + for (size_t i = 0; i < points_p3_bytes.size(); ++i) + { + P = hash_helper_t::hp(P); + ge_p3_tobytes((unsigned char*)&points_p3_bytes[i], &P.m_p3); + } + + std::vector points(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_frombytes_vartime(&points[i], (unsigned char*)&points_p3_bytes[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points); + }); + + run("ge_p3_to_cached(p3)", 10000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector points_p3(rounds); + ge_scalarmult_base(&points_p3[0], c_scalar_1.data()); + for (size_t i = 1; i < points_p3.size(); ++i) + ge_bytes_hash_to_ec_32(&points_p3[i], (const unsigned char*)&points_p3[i - 1].X); // P_{i+1} = Hp(P_i.X) + + std::vector points_cached(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_p3_to_cached(&points_cached[i], &points_p3[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points_cached); + }); + + run("ge_add(p3 + p3)", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + std::vector points_cached(rounds); + point_t p = scalar_t::random() * c_point_G; + for (size_t i = 0; i < rnd_indecies.size(); ++i) + { + ge_p3_to_cached(&points_cached[i], &p.m_p3); + p = p + p; + } + ge_p3 Q; + ge_scalarmult_base(&Q, &scalar_t::random().m_s[0]); + std::vector results(rnd_indecies.size()); + + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_add(&results[i], &Q, &points_cached[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(results); + }); + + run("ge_p1p1_to_p3(p1p1)", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + ge_cached G; + ge_p3_to_cached(&G, &c_point_G.m_p3); + + std::vector points_p1p1(rounds); + ge_add(&points_p1p1[0], &c_point_G.m_p3, &G); + for (size_t i = 1; i < points_p1p1.size(); ++i) + { + ge_p3 p3; + ge_p1p1_to_p3(&p3, &points_p1p1[i - 1]); + ge_add(&points_p1p1[i], &p3, &G); + } + + std::vector points_p3(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_p1p1_to_p3(&points_p3[i], &points_p1p1[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_scalarmult()", 5000, [](timer_t& t, size_t rounds) { + //rounds -= rounds % 8; + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + scalar_t x; + x.make_random(); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + { + //scalar_t x = x + x + x; + scalar_t x; + x.make_random(); + memcpy(&scalars[i].data, x.data(), 32); + } + + point_t p = scalar_t::random() * c_point_G; + + //std::vector points_p2(rounds); + std::vector points_p3(rounds); + + // warmup round + //for (size_t i = 0; i < rounds; ++i) + // ge_scalarmult((ge_p2*)&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]], &p.m_p3); + + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_scalarmult((ge_p2*)&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 3], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 3]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 0], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 0]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 1], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 1]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 2], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 2]], &p.m_p3); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_scalarmult() (2)", 5000, [](timer_t& t, size_t rounds) { + //rounds -= rounds % 8; + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + scalar_t x; + x.make_random(); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + { + //scalar_t x = x + x + x; + scalar_t x; + x.make_random(); + memcpy(&scalars[i].data, x.data(), 32); + } + + point_t p = scalar_t::random() * c_point_G; + + //std::vector points_p2(rounds); + std::vector points_p3(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_scalarmult((ge_p2*)&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 3], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 3]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 0], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 0]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 1], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 1]], &p.m_p3); + //ge_scalarmult(&points_p2[i * 4 + 2], (const unsigned char*)&scalars[rnd_indecies[i * 4 + 2]], &p.m_p3); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_scalarmult_p3()", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + scalar_t x; + x.make_random(); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + { + //scalar_t x = x + x + x; + scalar_t x; + x.make_random(); + memcpy(&scalars[i].data, x.data(), 32); + } + + point_t p = scalar_t::random() * c_point_G; + + std::vector points_p3(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_scalarmult_p3(&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]], &p.m_p3); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_scalarmult_vartime_p3()", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + scalar_t x; + x.make_random(); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + { + //scalar_t x = x + x + x; + scalar_t x; + x.make_random(); + memcpy(&scalars[i].data, x.data(), 32); + } + + point_t p = scalar_t::random() * c_point_G; + + //memcpy(&scalars[rnd_indecies[0]], scalar_t(1).data(), 32); + + std::vector points_p3(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_scalarmult_vartime_p3(&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]], &p.m_p3); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_scalarmult_vartime_p3_v2()", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + scalar_t x; + x.make_random(); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + { + //scalar_t x = x + x + x; + scalar_t x; + x.make_random(); + memcpy(&scalars[i].data, x.data(), 32); + } + + point_t p = scalar_t::random() * c_point_G; + + std::vector points_p3(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_scalarmult_vartime_p3_v2(&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]], &p.m_p3); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_scalarmult_base()", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + scalar_t x; + x.make_random(); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + { + scalar_t x = x + x + x; + memcpy(&scalars[i].data, x.data(), 32); + } + + std::vector points_p3(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_scalarmult_base(&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points_p3); + }); + + run("ge_mul8_p3()", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector points_p3(rounds); + ge_scalarmult_base(&points_p3[0], c_scalar_1.data()); + for (size_t i = 1; i < points_p3.size(); ++i) + ge_bytes_hash_to_ec_32(&points_p3[i], (const unsigned char*)&points_p3[i - 1].X); // P_{i+1} = Hp(P_i.X) + + std::vector points_result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_mul8_p3(&points_result[i], &points_p3[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points_result); + }); + + run("ge_mul8()", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + point_t p = scalar_t::random() * c_point_G; + + std::vector points_p2(rounds); + ge_p3_to_p2(&points_p2[0], &p.m_p3); + ge_p1p1 p1; + for (size_t i = 0; i < points_p2.size() - 1; ++i) + { + ge_p2_dbl(&p1, &points_p2[i]); + ge_p1p1_to_p2(&points_p2[i + 1], &p1); + } + + std::vector points_result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + ge_mul8(&points_result[i], &points_p2[rnd_indecies[i]]); + } + t.stop(); + + return HASH_64_VEC(points_result); + }); + + + + + LOG_PRINT_L0(ENDL << "new primitives:"); + + run("point_t + point_t", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector points(rounds); + point_t p = c_point_G; + for (size_t i = 0; i < rounds; ++i) + { + points[i] = p; + p = p + p; + } + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + result[i] = points[rnd_indecies[i]] + p; + } + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sclar_t * point_t", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + scalars[i].make_random(); + + point_t p = scalar_t::random() * c_point_G; + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + result[i] = scalars[rnd_indecies[i]] * p; + } + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sclar_t * point_g_t", 5000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + scalars[i].make_random(); + + std::vector result(rounds); + t.start(); + for (size_t i = 0; i < rounds; ++i) + { + result[i] = scalars[rnd_indecies[i]] * c_point_G; + } + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sclar_t * scalar_t", 50000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + scalars[i].make_random(); + + scalar_t s = scalar_t::random(); + + std::vector result(rounds); + t.start(4); + for (size_t i = 0; i < rounds; ++i) + { + result[i] = scalars[rnd_indecies[i]] * s * s * s * s; + } + t.stop(); + + return HASH_64_VEC(result); + }); + + run("sclar_t / scalar_t", 10000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector scalars(rounds); + for (size_t i = 0; i < rounds; ++i) + scalars[i].make_random(); + + scalar_t s = scalar_t::random(); + + std::vector result(rounds); + t.start(2); + for (size_t i = 0; i < rounds; ++i) + { + result[i] = scalars[rnd_indecies[i]] / s / s; + } + t.stop(); + + return HASH_64_VEC(result); + }); + + run("mul_plus_G", 2000, [](timer_t& t, size_t rounds) { + std::vector rnd_indecies; + helper::make_rnd_indicies(rnd_indecies, rounds); + + std::vector points(rounds); + point_t p = c_point_G; + for (size_t i = 0; i < rounds; ++i) + { + points[i] = p; + p = p + p; + } + + scalar_t a, b; + a.make_random(); + b.make_random(); + + std::vector result(rounds); + t.start(2); + for (size_t i = 0; i < rounds; ++i) + { + result[i] = points[rnd_indecies[i]].mul_plus_G(a, b).mul_plus_G(a, b); + } + t.stop(); + + return HASH_64_VEC(result); + }); + + return true; +} diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index 65acf07d..ed53cbe3 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -56,7 +56,7 @@ namespace const command_line::arg_descriptor arg_deadlock_guard = { "test-deadlock-guard", "Do deadlock guard test", false, true }; const command_line::arg_descriptor arg_difficulty_analysis = { "difficulty-analysis", "Do difficulty analysis", "", true }; const command_line::arg_descriptor arg_test_plain_wallet = { "test-plainwallet", "Do testing of plain wallet interface", false, true }; - const command_line::arg_descriptor arg_crypto_tests = { "crypto-tests", "Run experimental crypto tests", false, true }; + const command_line::arg_descriptor arg_crypto_tests = { "crypto-tests", "Run experimental crypto tests", "", true }; } @@ -212,9 +212,9 @@ int main(int argc, char* argv[]) } return 0; } - else if (command_line::get_arg(vm, arg_crypto_tests)) + else if (command_line::has_arg(vm, arg_crypto_tests)) { - return crypto_tests(); + return crypto_tests(command_line::get_arg(vm, arg_crypto_tests)); } else {