diff --git a/Makefile b/Makefile index 837e0ecd01..3bad57c8a0 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,11 @@ SRCS += aes/aescrypt.c aes/aeskey.c aes/aestab.c aes/aes_modes.c SRCS += ed25519-donna/curve25519-donna-32bit.c ed25519-donna/curve25519-donna-helpers.c ed25519-donna/modm-donna-32bit.c SRCS += ed25519-donna/ed25519-donna-basepoint-table.c ed25519-donna/ed25519-donna-32bit-tables.c ed25519-donna/ed25519-donna-impl-base.c SRCS += ed25519-donna/ed25519.c ed25519-donna/curve25519-donna-scalarmult-base.c ed25519-donna/ed25519-sha3.c ed25519-donna/ed25519-keccak.c +SRCS += ed25519-donna/ge25519.c +SRCS += monero/base58.c +SRCS += monero/serialize.c +SRCS += monero/xmr.c +SRCS += monero/range_proof.c SRCS += blake256.c SRCS += blake2b.c blake2s.c SRCS += groestl.c @@ -75,7 +80,7 @@ tests: tests/test_check tests/test_openssl tests/test_speed tests/libtrezor-cryp tests/aestst: aes/aestst.o aes/aescrypt.o aes/aeskey.o aes/aestab.o $(CC) $^ -o $@ -tests/test_check.o: tests/test_check_cardano.h tests/test_check_cashaddr.h tests/test_check_segwit.h +tests/test_check.o: tests/test_check_cardano.h tests/test_check_monero.h tests/test_check_cashaddr.h tests/test_check_segwit.h tests/test_check: tests/test_check.o $(OBJS) $(CC) tests/test_check.o $(OBJS) $(TESTLIBS) -o tests/test_check diff --git a/base58.c b/base58.c index 5afbbd0d44..6825ae56b8 100644 --- a/base58.c +++ b/base58.c @@ -29,7 +29,8 @@ #include "ripemd160.h" #include "memzero.h" -static const int8_t b58digits_map[] = { +const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +const int8_t b58digits_map[] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, @@ -148,8 +149,6 @@ int b58check(const void *bin, size_t binsz, HasherType hasher_type, const char * return binc[0]; } -static const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { const uint8_t *bin = data; diff --git a/base58.h b/base58.h index 41b0b7d4c6..0fa9167bfc 100644 --- a/base58.h +++ b/base58.h @@ -29,6 +29,9 @@ #include "hasher.h" #include "options.h" +extern const char b58digits_ordered[]; +extern const int8_t b58digits_map[]; + int base58_encode_check(const uint8_t *data, int len, HasherType hasher_type, char *str, int strsize); int base58_decode_check(const char *str, HasherType hasher_type, uint8_t *data, int datalen); diff --git a/ed25519-donna/ge25519.c b/ed25519-donna/ge25519.c new file mode 100644 index 0000000000..da1fac7b74 --- /dev/null +++ b/ed25519-donna/ge25519.c @@ -0,0 +1,378 @@ +// +// Created by Dusan Klinec on 29/04/2018. +// + +#include +#include "ge25519.h" + +static const uint32_t reduce_mask_25 = (1 << 25) - 1; +static const uint32_t reduce_mask_26 = (1 << 26) - 1; + +/* sqrt(x) is such an integer y that 0 <= y <= p - 1, y % 2 = 0, and y^2 = x (mod p). */ +/* d = -121665 / 121666 */ +static const bignum25519 ALIGN(16) fe_d = { + 0x35978a3, 0x0d37284, 0x3156ebd, 0x06a0a0e, 0x001c029, 0x179e898, 0x3a03cbb, 0x1ce7198, 0x2e2b6ff, 0x1480db3}; /* d */ +static const bignum25519 ALIGN(16) fe_sqrtm1 = { + 0x20ea0b0, 0x186c9d2, 0x08f189d, 0x035697f, 0x0bd0c60, 0x1fbd7a7, 0x2804c9e, 0x1e16569, 0x004fc1d, 0x0ae0c92}; /* sqrt(-1) */ +//static const bignum25519 ALIGN(16) fe_d2 = { +// 0x2b2f159, 0x1a6e509, 0x22add7a, 0x0d4141d, 0x0038052, 0x0f3d130, 0x3407977, 0x19ce331, 0x1c56dff, 0x0901b67}; /* 2 * d */ + +/* A = 2 * (1 - d) / (1 + d) = 486662 */ +static const bignum25519 ALIGN(16) fe_ma2 = { + 0x33de3c9, 0x1fff236, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff}; /* -A^2 */ +static const bignum25519 ALIGN(16) fe_ma = { + 0x3f892e7, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff}; /* -A */ +static const bignum25519 ALIGN(16) fe_fffb1 = { + 0x1e3bdff, 0x025a2b3, 0x18e5bab, 0x0ba36ac, 0x0b9afed, 0x004e61c, 0x31d645f, 0x09d1bea, 0x102529e, 0x0063810}; /* sqrt(-2 * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb2 = { + 0x383650d, 0x066df27, 0x10405a4, 0x1cfdd48, 0x2b887f2, 0x1e9a041, 0x1d7241f, 0x0612dc5, 0x35fba5d, 0x0cbe787}; /* sqrt(2 * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb3 = { + 0x0cfd387, 0x1209e3a, 0x3bad4fc, 0x18ad34d, 0x2ff6c02, 0x0f25d12, 0x15cdfe0, 0x0e208ed, 0x32eb3df, 0x062d7bb}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb4 = { + 0x2b39186, 0x14640ed, 0x14930a7, 0x04509fa, 0x3b91bf0, 0x0f7432e, 0x07a443f, 0x17f24d8, 0x031067d, 0x0690fcc}; /* sqrt(sqrt(-1) * A * (A + 2)) */ + +void curve25519_set(bignum25519 r, uint32_t x){ + r[0] = x & reduce_mask_26; x >>= 26; + r[1] = x & reduce_mask_25; + r[2] = 0; + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 0; + r[9] = 0; +} + +void curve25519_set_d(bignum25519 r){ + curve25519_copy(r, ge25519_ecd); +} + +void curve25519_set_2d(bignum25519 r){ + curve25519_copy(r, ge25519_ec2d); +} + +void curve25519_set_sqrtneg1(bignum25519 r){ + curve25519_copy(r, ge25519_sqrtneg1); +} + +int curve25519_isnegative(const bignum25519 f) { + unsigned char s[32]; + curve25519_contract(s, f); + return s[0] & 1; +} + +int curve25519_isnonzero(const bignum25519 f) { + unsigned char s[32]; + curve25519_contract(s, f); + return ((((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | + s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | + s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | + s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1) & 0x1; +} + +void curve25519_reduce(bignum25519 out, const bignum25519 in) { + uint32_t c; + out[0] = in[0] ; c = (out[0] >> 26); out[0] &= reduce_mask_26; + out[1] = in[1] + c; c = (out[1] >> 25); out[1] &= reduce_mask_25; + out[2] = in[2] + c; c = (out[2] >> 26); out[2] &= reduce_mask_26; + out[3] = in[3] + c; c = (out[3] >> 25); out[3] &= reduce_mask_25; + out[4] = in[4] + c; c = (out[4] >> 26); out[4] &= reduce_mask_26; + out[5] = in[5] + c; c = (out[5] >> 25); out[5] &= reduce_mask_25; + out[6] = in[6] + c; c = (out[6] >> 26); out[6] &= reduce_mask_26; + out[7] = in[7] + c; c = (out[7] >> 25); out[7] &= reduce_mask_25; + out[8] = in[8] + c; c = (out[8] >> 26); out[8] &= reduce_mask_26; + out[9] = in[9] + c; c = (out[9] >> 25); out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +static void curve25519_divpowm1(bignum25519 r, const bignum25519 u, const bignum25519 v) { + bignum25519 v3={0}, uv7={0}, t0={0}, t1={0}, t2={0}; + int i; + + curve25519_square(v3, v); + curve25519_mul(v3, v3, v); /* v3 = v^3 */ + curve25519_square(uv7, v3); + curve25519_mul(uv7, uv7, v); + curve25519_mul(uv7, uv7, u); /* uv7 = uv^7 */ + + /*fe_pow22523(uv7, uv7);*/ + /* From fe_pow22523.c */ + + curve25519_square(t0, uv7); + curve25519_square(t1, t0); + curve25519_square(t1, t1); + curve25519_mul(t1, uv7, t1); + curve25519_mul(t0, t0, t1); + curve25519_square(t0, t0); + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for (i = 0; i < 4; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for (i = 0; i < 9; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t1, t1, t0); + curve25519_square(t2, t1); + for (i = 0; i < 19; ++i) { + curve25519_square(t2, t2); + } + curve25519_mul(t1, t2, t1); + for (i = 0; i < 10; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for (i = 0; i < 49; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t1, t1, t0); + curve25519_square(t2, t1); + for (i = 0; i < 99; ++i) { + curve25519_square(t2, t2); + } + curve25519_mul(t1, t2, t1); + for (i = 0; i < 50; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t0, t0); + curve25519_square(t0, t0); + curve25519_mul(t0, t0, uv7); + + /* End fe_pow22523.c */ + /* t0 = (uv^7)^((q-5)/8) */ + curve25519_mul(t0, t0, v3); + curve25519_mul(r, t0, u); /* u^(m+1)v^(-(m+1)) */ +} + +void curve25519_expand_reduce(bignum25519 out, const unsigned char in[32]) { + uint32_t x0,x1,x2,x3,x4,x5,x6,x7; +#define F(s) \ + ((((uint32_t)in[s + 0]) ) | \ + (((uint32_t)in[s + 1]) << 8) | \ + (((uint32_t)in[s + 2]) << 16) | \ + (((uint32_t)in[s + 3]) << 24)) + x0 = F(0); + x1 = F(4); + x2 = F(8); + x3 = F(12); + x4 = F(16); + x5 = F(20); + x6 = F(24); + x7 = F(28); +#undef F + + out[0] = ( x0 ) & reduce_mask_26; + out[1] = ((((uint64_t)x1 << 32) | x0) >> 26) & reduce_mask_25; + out[2] = ((((uint64_t)x2 << 32) | x1) >> 19) & reduce_mask_26; + out[3] = ((((uint64_t)x3 << 32) | x2) >> 13) & reduce_mask_25; + out[4] = (( x3) >> 6) & reduce_mask_26; + out[5] = ( x4 ) & reduce_mask_25; + out[6] = ((((uint64_t)x5 << 32) | x4) >> 25) & reduce_mask_26; + out[7] = ((((uint64_t)x6 << 32) | x5) >> 19) & reduce_mask_25; + out[8] = ((((uint64_t)x7 << 32) | x6) >> 12) & reduce_mask_26; + out[9] = (( x7) >> 6); // & reduce_mask_25; /* ignore the top bit */ + out[0] += 19 * (out[9] >> 25); + out[9] &= reduce_mask_25; +} + +int ge25519_check(const ge25519 *r){ + /* return (z % q != 0 and + x * y % q == z * t % q and + (y * y - x * x - z * z - ed25519.d * t * t) % q == 0) + */ + + bignum25519 z={0}, lhs={0}, rhs={0}, tmp={0}, res={0}; + curve25519_reduce(z, r->z); + + curve25519_mul(lhs, r->x, r->y); + curve25519_mul(rhs, r->z, r->t); + curve25519_sub_reduce(lhs, lhs, rhs); + + curve25519_square(res, r->y); + curve25519_square(tmp, r->x); + curve25519_sub_reduce(res, res, tmp); + curve25519_square(tmp, r->z); + curve25519_sub_reduce(res, res, tmp); + curve25519_square(tmp, r->t); + curve25519_mul(tmp, tmp, ge25519_ecd); + curve25519_sub_reduce(res, res, tmp); + + const int c1 = curve25519_isnonzero(z); + const int c2 = curve25519_isnonzero(lhs); + const int c3 = curve25519_isnonzero(res); + return c1 & (c2^0x1) & (c3^0x1); +} + +int ge25519_eq(const ge25519 *a, const ge25519 *b){ + int eq = 1; + bignum25519 t1={0}, t2={0}; + + eq &= ge25519_check(a); + eq &= ge25519_check(b); + + curve25519_mul(t1, a->x, b->z); + curve25519_mul(t2, b->x, a->z); + curve25519_sub_reduce(t1, t1, t2); + eq &= curve25519_isnonzero(t1) ^ 1; + + curve25519_mul(t1, a->y, b->z); + curve25519_mul(t2, b->y, a->z); + curve25519_sub_reduce(t1, t1, t2); + eq &= curve25519_isnonzero(t1) ^ 1; + + return eq; +} + +void ge25519_copy(ge25519 *dst, const ge25519 *src){ + curve25519_copy(dst->x, src->x); + curve25519_copy(dst->y, src->y); + curve25519_copy(dst->z, src->z); + curve25519_copy(dst->t, src->t); +} + +void ge25519_set_base(ge25519 *r){ + ge25519_copy(r, &ge25519_basepoint); +} + +void ge25519_mul8(ge25519 *r, const ge25519 *t) { + ge25519_double_partial(r, t); + ge25519_double_partial(r, r); + ge25519_double(r, r); +} + +void ge25519_neg_partial(ge25519 *r){ + curve25519_neg(r->x, r->x); +} + +void ge25519_neg_full(ge25519 *r){ + curve25519_neg(r->x, r->x); + curve25519_neg(r->t, r->t); +} + +void ge25519_reduce(ge25519 *r, const ge25519 *t){ + curve25519_reduce(r->x, t->x); + curve25519_reduce(r->y, t->y); + curve25519_reduce(r->z, t->z); + curve25519_reduce(r->t, t->t); +} + +void ge25519_norm(ge25519 *r, const ge25519 * t){ + bignum25519 zinv; + curve25519_recip(zinv, t->z); + curve25519_mul(r->x, t->x, zinv); + curve25519_mul(r->y, t->y, zinv); + curve25519_mul(r->t, r->x, r->y); + curve25519_set(r->z, 1); +} + +void ge25519_add(ge25519 *r, const ge25519 *p, const ge25519 *q, unsigned char signbit) { + ge25519_pniels P_ni; + ge25519_p1p1 P_11; + + ge25519_full_to_pniels(&P_ni, q); + ge25519_pnielsadd_p1p1(&P_11, p, &P_ni, signbit); + ge25519_p1p1_to_full(r, &P_11); +} + +void ge25519_fromfe_frombytes_vartime(ge25519 *r, const unsigned char *s){ + bignum25519 u={0}, v={0}, w={0}, x={0}, y={0}, z={0}; + unsigned char sign; + + curve25519_expand_reduce(u, s); + + curve25519_square(v, u); + curve25519_add_reduce(v, v, v); /* 2 * u^2 */ + curve25519_set(w, 1); + curve25519_add_reduce(w, v, w); /* w = 2 * u^2 + 1 */ + + curve25519_square(x, w); /* w^2 */ + curve25519_mul(y, fe_ma2, v); /* -2 * A^2 * u^2 */ + curve25519_add_reduce(x, x, y); /* x = w^2 - 2 * A^2 * u^2 */ + + curve25519_divpowm1(r->x, w, x); /* (w / x)^(m + 1) */ + curve25519_square(y, r->x); + curve25519_mul(x, y, x); + curve25519_sub_reduce(y, w, x); + curve25519_copy(z, fe_ma); + + if (curve25519_isnonzero(y)) { + curve25519_add_reduce(y, w, x); + if (curve25519_isnonzero(y)) { + goto negative; + } else { + curve25519_mul(r->x, r->x, fe_fffb1); + } + } else { + curve25519_mul(r->x, r->x, fe_fffb2); + } + curve25519_mul(r->x, r->x, u); /* u * sqrt(2 * A * (A + 2) * w / x) */ + curve25519_mul(z, z, v); /* -2 * A * u^2 */ + sign = 0; + goto setsign; +negative: + curve25519_mul(x, x, fe_sqrtm1); + curve25519_sub_reduce(y, w, x); + if (curve25519_isnonzero(y)) { + assert((curve25519_add_reduce(y, w, x), !curve25519_isnonzero(y))); + curve25519_mul(r->x, r->x, fe_fffb3); + } else { + curve25519_mul(r->x, r->x, fe_fffb4); + } + /* r->x = sqrt(A * (A + 2) * w / x) */ + /* z = -A */ + sign = 1; +setsign: + if (curve25519_isnegative(r->x) != sign) { + assert(curve25519_isnonzero(r->x)); + curve25519_neg(r->x, r->x); + } + curve25519_add_reduce(r->z, z, w); + curve25519_sub_reduce(r->y, z, w); + curve25519_mul(r->x, r->x, r->z); + + // Partial form, saving from T coord computation . + // Later is mul8 discarding T anyway. + // rt = ((rx * ry % q) * inv(rz)) % q + // curve25519_mul(x, r->x, r->y); + // curve25519_recip(z, r->z); + // curve25519_mul(r->t, x, z); + +#if !defined(NDEBUG) + { + bignum25519 check_x={0}, check_y={0}, check_iz={0}, check_v={0}; + curve25519_recip(check_iz, r->z); + curve25519_mul(check_x, r->x, check_iz); + curve25519_mul(check_y, r->y, check_iz); + curve25519_square(check_x, check_x); + curve25519_square(check_y, check_y); + curve25519_mul(check_v, check_x, check_y); + curve25519_mul(check_v, fe_d, check_v); + curve25519_add_reduce(check_v, check_v, check_x); + curve25519_sub_reduce(check_v, check_v, check_y); + curve25519_set(check_x, 1); + curve25519_add_reduce(check_v, check_v, check_x); + assert(!curve25519_isnonzero(check_v)); + } +#endif +} + +int ge25519_unpack_vartime(ge25519 *r, const unsigned char *s){ + int res = ge25519_unpack_negative_vartime(r, s); + ge25519_neg_full(r); + return res; +} + +void ge25519_scalarmult_base_wrapper(ge25519 *r, const bignum256modm s){ + ge25519_scalarmult_base_niels(r, ge25519_niels_base_multiples, s); + ge25519_norm(r, r); +} + +void ge25519_scalarmult_wrapper(ge25519 *r, const ge25519 *P, const bignum256modm a){ + ge25519_scalarmult(r, P, a); + ge25519_norm(r, r); +} diff --git a/ed25519-donna/ge25519.h b/ed25519-donna/ge25519.h new file mode 100644 index 0000000000..964c6aef17 --- /dev/null +++ b/ed25519-donna/ge25519.h @@ -0,0 +1,77 @@ +// +// Created by Dusan Klinec on 29/04/2018. +// + +#ifndef GE25519_H +#define GE25519_H + +#include +#include "ed25519-donna.h" + +/* uint32_t to Zmod(2^255-19) */ +void curve25519_set(bignum25519 r, uint32_t x); + +/* set d */ +void curve25519_set_d(bignum25519 r); + +/* set 2d */ +void curve25519_set_2d(bignum25519 r); + +/* set sqrt(-1) */ +void curve25519_set_sqrtneg1(bignum25519 r); + +/* constant time Zmod(2^255-19) negative test */ +int curve25519_isnegative(const bignum25519 f); + +/* constant time Zmod(2^255-19) non-zero test */ +int curve25519_isnonzero(const bignum25519 f); + +/* reduce Zmod(2^255-19) */ +void curve25519_reduce(bignum25519 r, const bignum25519 in); + +/* Zmod(2^255-19) from byte array to bignum25519 expansion with modular reduction */ +void curve25519_expand_reduce(bignum25519 out, const unsigned char in[32]); + +/* check if r is on curve */ +int ge25519_check(const ge25519 *r); + +/* a == b */ +int ge25519_eq(const ge25519 *a, const ge25519 *b); + +/* copies one point to another */ +void ge25519_copy(ge25519 *dst, const ge25519 *src); + +/* sets B point to r */ +void ge25519_set_base(ge25519 *r); + +/* 8*P */ +void ge25519_mul8(ge25519 *r, const ge25519 *t); + +/* -P */ +void ge25519_neg_partial(ge25519 *r); + +/* -P */ +void ge25519_neg_full(ge25519 *r); + +/* reduce all coords */ +void ge25519_reduce(ge25519 *r, const ge25519 *t); + +/* normalizes coords. (x, y, 1, x*y) */ +void ge25519_norm(ge25519 *r, const ge25519 * t); + +/* Simple addition */ +void ge25519_add(ge25519 *r, const ge25519 *a, const ge25519 *b, unsigned char signbit); + +/* point from bytes, used in H_p() */ +void ge25519_fromfe_frombytes_vartime(ge25519 *r, const unsigned char *s); + +/* point from bytes */ +int ge25519_unpack_vartime(ge25519 *r, const unsigned char *s); + +/* aG, wrapper for niels base mult. */ +void ge25519_scalarmult_base_wrapper(ge25519 *r, const bignum256modm s); + +/* aP, wrapper. General purpose, normalizes after multiplication */ +void ge25519_scalarmult_wrapper(ge25519 *r, const ge25519 *P, const bignum256modm a); + +#endif diff --git a/ed25519-donna/modm-donna-32bit.c b/ed25519-donna/modm-donna-32bit.c index 0086cd2cce..de2f5220e7 100644 --- a/ed25519-donna/modm-donna-32bit.c +++ b/ed25519-donna/modm-donna-32bit.c @@ -216,13 +216,13 @@ void mul256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) c += mul32x32_64(x[0], y[8]) + mul32x32_64(x[1], y[7]) + mul32x32_64(x[2], y[6]) + mul32x32_64(x[3], y[5]) + mul32x32_64(x[4], y[4]) + mul32x32_64(x[5], y[3]) + mul32x32_64(x[6], y[2]) + mul32x32_64(x[7], y[1]) + mul32x32_64(x[8], y[0]); f = (bignum256modm_element_t)c; r1[8] = (f & 0x00ffffff); q1[0] = (f >> 8) & 0x3fffff; c >>= 30; c += mul32x32_64(x[1], y[8]) + mul32x32_64(x[2], y[7]) + mul32x32_64(x[3], y[6]) + mul32x32_64(x[4], y[5]) + mul32x32_64(x[5], y[4]) + mul32x32_64(x[6], y[3]) + mul32x32_64(x[7], y[2]) + mul32x32_64(x[8], y[1]); - f = (bignum256modm_element_t)c; q1[0] = (q1[0] | (f << 22)) & 0x3fffffff; q1[1] = (f >> 8) & 0x3fffff; c >>= 30; + f = (bignum256modm_element_t)c; q1[0] = (q1[0] | (f << 22)) & 0x3fffffff; q1[1] = (f >> 8) & 0x3fffff; c >>= 30; c += mul32x32_64(x[2], y[8]) + mul32x32_64(x[3], y[7]) + mul32x32_64(x[4], y[6]) + mul32x32_64(x[5], y[5]) + mul32x32_64(x[6], y[4]) + mul32x32_64(x[7], y[3]) + mul32x32_64(x[8], y[2]); - f = (bignum256modm_element_t)c; q1[1] = (q1[1] | (f << 22)) & 0x3fffffff; q1[2] = (f >> 8) & 0x3fffff; c >>= 30; + f = (bignum256modm_element_t)c; q1[1] = (q1[1] | (f << 22)) & 0x3fffffff; q1[2] = (f >> 8) & 0x3fffff; c >>= 30; c += mul32x32_64(x[3], y[8]) + mul32x32_64(x[4], y[7]) + mul32x32_64(x[5], y[6]) + mul32x32_64(x[6], y[5]) + mul32x32_64(x[7], y[4]) + mul32x32_64(x[8], y[3]); - f = (bignum256modm_element_t)c; q1[2] = (q1[2] | (f << 22)) & 0x3fffffff; q1[3] = (f >> 8) & 0x3fffff; c >>= 30; + f = (bignum256modm_element_t)c; q1[2] = (q1[2] | (f << 22)) & 0x3fffffff; q1[3] = (f >> 8) & 0x3fffff; c >>= 30; c += mul32x32_64(x[4], y[8]) + mul32x32_64(x[5], y[7]) + mul32x32_64(x[6], y[6]) + mul32x32_64(x[7], y[5]) + mul32x32_64(x[8], y[4]); - f = (bignum256modm_element_t)c; q1[3] = (q1[3] | (f << 22)) & 0x3fffffff; q1[4] = (f >> 8) & 0x3fffff; c >>= 30; + f = (bignum256modm_element_t)c; q1[3] = (q1[3] | (f << 22)) & 0x3fffffff; q1[4] = (f >> 8) & 0x3fffff; c >>= 30; c += mul32x32_64(x[5], y[8]) + mul32x32_64(x[6], y[7]) + mul32x32_64(x[7], y[6]) + mul32x32_64(x[8], y[5]); f = (bignum256modm_element_t)c; q1[4] = (q1[4] | (f << 22)) & 0x3fffffff; q1[5] = (f >> 8) & 0x3fffff; c >>= 30; c += mul32x32_64(x[6], y[8]) + mul32x32_64(x[7], y[7]) + mul32x32_64(x[8], y[6]); @@ -282,7 +282,7 @@ void expand256_modm(bignum256modm out, const unsigned char *in, size_t len) { q1[5] = ((x[12] >> 14) | (x[13] << 18)) & 0x3fffffff; q1[6] = ((x[13] >> 12) | (x[14] << 20)) & 0x3fffffff; q1[7] = ((x[14] >> 10) | (x[15] << 22)) & 0x3fffffff; - q1[8] = ((x[15] >> 8) ); + q1[8] = ((x[15] >> 8) ); barrett_reduce256_modm(out, q1, out); } @@ -408,3 +408,110 @@ void contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, i } } } + +void set256_modm(bignum256modm r, uint64_t v) { + r[0] = (bignum256modm_element_t) (v & 0x3fffffff); v >>= 30; + r[1] = (bignum256modm_element_t) (v & 0x3fffffff); v >>= 30; + r[2] = (bignum256modm_element_t) (v & 0x3fffffff); + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 0; +} + +int get256_modm(uint64_t * v, const bignum256modm r){ + *v = 0; + int con1 = 0; + +#define NONZ(x) ((((((int64_t)(x)) - 1) >> 32) + 1) & 1) + bignum256modm_element_t c = 0; + c = r[0]; *v += (uint64_t)c & 0x3fffffff; c >>= 30; // 30 + c += r[1]; *v += ((uint64_t)c & 0x3fffffff) << 30; c >>= 30; // 60 + c += r[2]; *v += ((uint64_t)c & 0xf) << 60; con1 |= NONZ(c>>4); c >>= 30; // 64 bits + c += r[3]; con1 |= NONZ(c); c >>= 30; + c += r[4]; con1 |= NONZ(c); c >>= 30; + c += r[5]; con1 |= NONZ(c); c >>= 30; + c += r[6]; con1 |= NONZ(c); c >>= 30; + c += r[7]; con1 |= NONZ(c); c >>= 30; + c += r[8]; con1 |= NONZ(c); c >>= 30; + con1 |= NONZ(c); +#undef NONZ + + return con1 ^ 1; +} + +int eq256_modm(const bignum256modm x, const bignum256modm y){ + size_t differentbits = 0; + int len = bignum256modm_limb_size; + while (len--) { + differentbits |= (*x++ ^ *y++); + } + return (int) (1 & ((differentbits - 1) >> bignum256modm_bits_per_limb)); +} + +int cmp256_modm(const bignum256modm x, const bignum256modm y){ + int len = 2*bignum256modm_limb_size; + uint32_t a_gt = 0; + uint32_t b_gt = 0; + + // 16B chunks + while (len--) { + const uint32_t ln = (const uint32_t) len; + const uint32_t a = (x[ln>>1] >> 16*(ln & 1)) & 0xffff; + const uint32_t b = (y[ln>>1] >> 16*(ln & 1)) & 0xffff; + + const uint32_t limb_a_gt = ((b - a) >> 16) & 1; + const uint32_t limb_b_gt = ((a - b) >> 16) & 1; + a_gt |= limb_a_gt & ~b_gt; + b_gt |= limb_b_gt & ~a_gt; + } + + return a_gt - b_gt; +} + +int iszero256_modm(const bignum256modm x){ + size_t differentbits = 0; + int len = bignum256modm_limb_size; + while (len--) { + differentbits |= (*x++); + } + return (int) (1 & ((differentbits - 1) >> bignum256modm_bits_per_limb)); +} + +void copy256_modm(bignum256modm r, const bignum256modm x){ + r[0] = x[0]; + r[1] = x[1]; + r[2] = x[2]; + r[3] = x[3]; + r[4] = x[4]; + r[5] = x[5]; + r[6] = x[6]; + r[7] = x[7]; + r[8] = x[8]; +} + +int check256_modm(const bignum256modm x){ + int ok = 1; + bignum256modm t={0}, z={0}; + + ok &= iszero256_modm(x) ^ 1; + barrett_reduce256_modm(t, z, x); + ok &= eq256_modm(t, x); + return ok; +} + +void mulsub256_modm(bignum256modm r, const bignum256modm a, const bignum256modm b, const bignum256modm c){ + //(cc - aa * bb) % l + bignum256modm t={0}; + mul256_modm(t, a, b); + sub256_modm(r, c, t); +} + +void muladd256_modm(bignum256modm r, const bignum256modm a, const bignum256modm b, const bignum256modm c){ + //(cc + aa * bb) % l + bignum256modm t={0}; + mul256_modm(t, a, b); + add256_modm(r, c, t); +} diff --git a/ed25519-donna/modm-donna-32bit.h b/ed25519-donna/modm-donna-32bit.h index f346336457..98090f2fee 100644 --- a/ed25519-donna/modm-donna-32bit.h +++ b/ed25519-donna/modm-donna-32bit.h @@ -51,3 +51,30 @@ void contract256_modm(unsigned char out[32], const bignum256modm in); void contract256_window4_modm(signed char r[64], const bignum256modm in); void contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, int windowsize); + +/* 64bit uint to scalar value */ +void set256_modm(bignum256modm r, uint64_t v); + +/* scalar value to 64bit uint */ +int get256_modm(uint64_t * v, const bignum256modm r); + +/* equality test on two reduced scalar values */ +int eq256_modm(const bignum256modm x, const bignum256modm y); + +/* comparison of two reduced scalar values */ +int cmp256_modm(const bignum256modm x, const bignum256modm y); + +/* scalar null check, has to be reduced */ +int iszero256_modm(const bignum256modm x); + +/* simple copy, no reduction */ +void copy256_modm(bignum256modm r, const bignum256modm x); + +/* check if nonzero && same after reduction */ +int check256_modm(const bignum256modm x); + +/* (cc - aa * bb) % l */ +void mulsub256_modm(bignum256modm r, const bignum256modm a, const bignum256modm b, const bignum256modm c); + +/* (cc + aa * bb) % l */ +void muladd256_modm(bignum256modm r, const bignum256modm a, const bignum256modm b, const bignum256modm c); diff --git a/monero/base58.c b/monero/base58.c new file mode 100644 index 0000000000..8769f11f90 --- /dev/null +++ b/monero/base58.c @@ -0,0 +1,243 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include +#include +#include +#include +#include "base58.h" +#include "int-util.h" +#include "sha2.h" +#include "../base58.h" + +const size_t alphabet_size = 58; // sizeof(b58digits_ordered) - 1; +const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11}; +const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1; +const size_t full_encoded_block_size = 11; // encoded_block_sizes[full_block_size]; +const size_t addr_checksum_size = 4; +const int decoded_block_sizes[] = {0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8}; +#define reverse_alphabet(letter) ((int8_t) b58digits_map[(int)letter]) + + +uint64_t uint_8be_to_64(const uint8_t* data, size_t size) +{ + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t res = 0; + switch (9 - size) + { + case 1: res |= *data++; /* FALLTHRU */ + case 2: res <<= 8; res |= *data++; /* FALLTHRU */ + case 3: res <<= 8; res |= *data++; /* FALLTHRU */ + case 4: res <<= 8; res |= *data++; /* FALLTHRU */ + case 5: res <<= 8; res |= *data++; /* FALLTHRU */ + case 6: res <<= 8; res |= *data++; /* FALLTHRU */ + case 7: res <<= 8; res |= *data++; /* FALLTHRU */ + case 8: res <<= 8; res |= *data; break; + default: assert(false); + } + + return res; +} + +void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) +{ + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t num_be = SWAP64(num); + memcpy(data, (uint8_t*)(&num_be) + sizeof(uint64_t) - size, size); +} + +void encode_block(const char* block, size_t size, char* res) +{ + assert(1 <= size && size <= full_block_size); + + uint64_t num = uint_8be_to_64((uint8_t*)(block), size); + int i = ((int)(encoded_block_sizes[size])) - 1; + while (0 <= i) + { + uint64_t remainder = num % alphabet_size; + num /= alphabet_size; + res[i] = b58digits_ordered[remainder]; + --i; + } +} + +bool decode_block(const char* block, size_t size, char* res) +{ + assert(1 <= size && size <= full_encoded_block_size); + + int res_size = decoded_block_sizes[size]; + if (res_size <= 0) + return false; // Invalid block size + + uint64_t res_num = 0; + uint64_t order = 1; + for (size_t i = size - 1; i < size; --i) + { + int digit = reverse_alphabet(block[i]); + if (digit < 0) + return false; // Invalid symbol + + uint64_t product_hi; + uint64_t tmp = res_num + mul128(order, (uint64_t) digit, &product_hi); + if (tmp < res_num || 0 != product_hi) + return false; // Overflow + + res_num = tmp; + order *= alphabet_size; // Never overflows, 58^10 < 2^64 + } + + if ((size_t)res_size < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + return false; // Overflow + + uint_64_to_8be(res_num, res_size, (uint8_t*)(res)); + + return true; +} + + +bool xmr_base58_encode(char *b58, size_t *b58sz, const void *data, size_t binsz) +{ + if (binsz==0) + return true; + + const char * data_bin = data; + size_t full_block_count = binsz / full_block_size; + size_t last_block_size = binsz % full_block_size; + size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; + + if (b58sz){ + if (res_size >= *b58sz){ + return false; + } + *b58sz = res_size; + } + + for (size_t i = 0; i < full_block_count; ++i) + { + encode_block(data_bin + i * full_block_size, full_block_size, b58 + i * full_encoded_block_size); + } + + if (0 < last_block_size) + { + encode_block(data_bin + full_block_count * full_block_size, last_block_size, b58 + full_block_count * full_encoded_block_size); + } + + return true; +} + +bool xmr_base58_decode(const char *b58, size_t b58sz, void *data, size_t *binsz) +{ + if (b58sz == 0) { + *binsz = 0; + return true; + } + + size_t full_block_count = b58sz / full_encoded_block_size; + size_t last_block_size = b58sz % full_encoded_block_size; + int last_block_decoded_size = decoded_block_sizes[last_block_size]; + if (last_block_decoded_size < 0) { + *binsz = 0; + return false; // Invalid enc length + } + + size_t data_size = full_block_count * full_block_size + last_block_decoded_size; + if (*binsz < data_size){ + *binsz = 0; + return false; + } + + char * data_bin = data; + for (size_t i = 0; i < full_block_count; ++i) + { + if (!decode_block(b58 + i * full_encoded_block_size, full_encoded_block_size, data_bin + i * full_block_size)) + return false; + } + + if (0 < last_block_size) + { + if (!decode_block(b58 + full_block_count * full_encoded_block_size, last_block_size, + data_bin + full_block_count * full_block_size)) + return false; + } + + return true; +} + +int xmr_base58_addr_encode_check(uint64_t tag, const uint8_t *data, size_t binsz, char *b58, size_t b58sz) +{ + if (binsz > 128 || tag > 127) { // tag varint + return false; + } + + size_t b58size = b58sz; + uint8_t buf[binsz + 1 + HASHER_DIGEST_LENGTH]; + uint8_t *hash = buf + binsz + 1; + buf[0] = (uint8_t) tag; + memcpy(buf + 1, data, binsz); + hasher_Raw(HASHER_SHA3K, buf, binsz + 1, hash); + + bool r = xmr_base58_encode(b58, &b58size, buf, binsz + 1 + addr_checksum_size); + return (int) (!r ? 0 : b58size); +} + +int xmr_base58_addr_decode_check(const char *addr, size_t sz, uint64_t *tag, void *data, size_t datalen) +{ + size_t buflen = 1 + 64 + addr_checksum_size; + uint8_t buf[buflen]; + uint8_t hash[HASHER_DIGEST_LENGTH]; + + if (!xmr_base58_decode(addr, sz, buf, &buflen)){ + return 0; + } + + size_t res_size = buflen - addr_checksum_size - 1; + if (datalen < res_size){ + return 0; + } + + if (buflen <= addr_checksum_size+1) { + return 0; + } + + hasher_Raw(HASHER_SHA3K, buf, buflen - addr_checksum_size, hash); + if (memcmp(hash, buf + buflen - addr_checksum_size, addr_checksum_size) != 0){ + return 0; + } + + *tag = buf[0]; + if (*tag > 127){ + return false; // varint + } + + memcpy(data, buf+1, res_size); + return (int) res_size; +} diff --git a/monero/base58.h b/monero/base58.h new file mode 100644 index 0000000000..628b38eb80 --- /dev/null +++ b/monero/base58.h @@ -0,0 +1,43 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#ifndef __XMR_BASE58_H__ +#define __XMR_BASE58_H__ + +#include +#include "hasher.h" +#include "options.h" + +int xmr_base58_addr_encode_check(uint64_t tag, const uint8_t *data, size_t binsz, char *b58, size_t b58sz); +int xmr_base58_addr_decode_check(const char *addr, size_t sz, uint64_t *tag, void *data, size_t datalen); +bool xmr_base58_encode(char *b58, size_t *b58sz, const void *data, size_t binsz); +bool xmr_base58_decode(const char *b58, size_t b58sz, void *data, size_t *binsz); + +#endif \ No newline at end of file diff --git a/monero/int-util.h b/monero/int-util.h new file mode 100644 index 0000000000..c6fb225b4e --- /dev/null +++ b/monero/int-util.h @@ -0,0 +1,77 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include + +static inline uint64_t hi_dword(uint64_t val) { + return val >> 32; +} + +static inline uint64_t lo_dword(uint64_t val) { + return val & 0xFFFFFFFF; +} + +static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = hi_dword(multiplier); + uint64_t b = lo_dword(multiplier); + uint64_t c = hi_dword(multiplicand); + uint64_t d = lo_dword(multiplicand); + + uint64_t ac = a * c; + uint64_t ad = a * d; + uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + bc; + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + assert(ac <= *product_hi); + + return product_lo; +} + +#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \ + (((uint64_t) (x) & 0x000000000000ff00) << 40) | \ + (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \ + (((uint64_t) (x) & 0x00000000ff000000) << 8) | \ + (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \ + (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \ + (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \ + (((uint64_t) (x) & 0xff00000000000000) >> 56)) diff --git a/monero/monero.h b/monero/monero.h new file mode 100644 index 0000000000..d281f333ba --- /dev/null +++ b/monero/monero.h @@ -0,0 +1,21 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#ifndef TREZOR_CRYPTO_MONERO_H +#define TREZOR_CRYPTO_MONERO_H + +#if !USE_MONERO +#error "Compile with -DUSE_MONERO=1" +#endif + +#if !USE_KECCAK +#error "Compile with -DUSE_KECCAK=1" +#endif + +#include "base58.h" +#include "serialize.h" +#include "xmr.h" +#include "range_proof.h" + +#endif //TREZOR_CRYPTO_MONERO_H diff --git a/monero/range_proof.c b/monero/range_proof.c new file mode 100644 index 0000000000..aaad23dec1 --- /dev/null +++ b/monero/range_proof.c @@ -0,0 +1,115 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#include "range_proof.h" + + +static void xmr_hash_ge25519_to_scalar(bignum256modm r, const ge25519 *p){ + unsigned char buff[32]; + ge25519_pack(buff, p); + xmr_hash_to_scalar(r, buff, sizeof(buff)); +} + +void xmr_gen_range_sig(xmr_range_sig_t * sig, ge25519 * C, bignum256modm mask, xmr_amount amount, bignum256modm * last_mask){ + bignum256modm ai[64]; + bignum256modm alpha[64]; + xmr_gen_range_sig_ex(sig, C, mask, amount, last_mask, ai, alpha); +} + +void xmr_gen_range_sig_ex(xmr_range_sig_t * sig, ge25519 * C, bignum256modm mask, xmr_amount amount, bignum256modm * last_mask, + bignum256modm ai[64], bignum256modm alpha[64]) +{ + const unsigned n = XMR_ATOMS; + bignum256modm a={0}; + bignum256modm si={0}; + bignum256modm c={0}; + bignum256modm ee={0}; + unsigned char buff[32]; + + Hasher kck; + xmr_hasher_init(&kck); + + ge25519 C_acc; + ge25519 C_h; + ge25519 C_tmp; + ge25519 L; + ge25519 Zero; + + ge25519_set_neutral(&Zero); + ge25519_set_neutral(&C_acc); + ge25519_set_xmr_h(&C_h); + set256_modm(a, 0); + +#define BB(i) ((amount>>(i)) & 1) + + // First pass, generates: ai, alpha, Ci, ee, s1 + for(unsigned ii=0; iiCi[ii], &C_tmp); + + if (BB(ii) == 0) { + xmr_random_scalar(si); + xmr_hash_ge25519_to_scalar(c, &L); + + ge25519_add(&C_tmp, &C_tmp, &C_h, 1); // Ci[ii] -= c_h + xmr_add_keys2_vartime(&L, si, c, &C_tmp); + + // Set s1[ii] to sigs + contract256_modm(sig->asig.s1[ii], si); + } + + ge25519_pack(buff, &L); + xmr_hasher_update(&kck, buff, sizeof(buff)); + + ge25519_double(&C_h, &C_h); // c_H = crypto.scalarmult(c_H, 2) + } + + // Compute ee + xmr_hasher_final(&kck, buff); + expand256_modm(ee, buff, sizeof(buff)); + + ge25519_set_xmr_h(&C_h); + + // Second pass, s0, s1 + for(unsigned ii=0; iiasig.s0[ii], si); + + } else { + xmr_random_scalar(si); + contract256_modm(sig->asig.s0[ii], si); + + ge25519_unpack_vartime(&C_tmp, sig->Ci[ii]); + xmr_add_keys2_vartime(&L, si, ee, &C_tmp); + xmr_hash_ge25519_to_scalar(c, &L); + + mulsub256_modm(si, ai[ii], c, alpha[ii]); + contract256_modm(sig->asig.s1[ii], si); + } + + ge25519_double(&C_h, &C_h); // c_H = crypto.scalarmult(c_H, 2) + } + + ge25519_copy(C, &C_acc); + copy256_modm(mask, a); + contract256_modm(sig->asig.ee, ee); +#undef BB +} + diff --git a/monero/range_proof.h b/monero/range_proof.h new file mode 100644 index 0000000000..cd846a095b --- /dev/null +++ b/monero/range_proof.h @@ -0,0 +1,30 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#ifndef TREZOR_CRYPTO_RANGE_PROOF_H +#define TREZOR_CRYPTO_RANGE_PROOF_H + +#include "xmr.h" +#define XMR_ATOMS 64 + +typedef uint64_t xmr_amount; +typedef xmr_key_t xmr_key64_t[64]; + +typedef struct xmr_boro_sig { + xmr_key64_t s0; + xmr_key64_t s1; + xmr_key_t ee; +} xmr_boro_sig_t; + +typedef struct range_sig { + xmr_boro_sig_t asig; + xmr_key64_t Ci; +} xmr_range_sig_t; + + +void xmr_gen_range_sig(xmr_range_sig_t * sig, ge25519 * C, bignum256modm mask, xmr_amount amount, bignum256modm * last_mask); +void xmr_gen_range_sig_ex(xmr_range_sig_t * sig, ge25519 * C, bignum256modm mask, xmr_amount amount, bignum256modm * last_mask, + bignum256modm ai[64], bignum256modm alpha[64]); + +#endif //TREZOR_CRYPTO_RANGE_PROOF_H diff --git a/monero/serialize.c b/monero/serialize.c new file mode 100644 index 0000000000..ee6aab4207 --- /dev/null +++ b/monero/serialize.c @@ -0,0 +1,54 @@ +// +// Created by Dusan Klinec on 02/05/2018. +// + +#include "serialize.h" + +int xmr_size_varint(uint64_t num){ + int ctr = 1; + while (num >= 0x80) { + ++ctr; + num >>= 7; + } + return ctr; +} + +int xmr_write_varint(uint8_t * buff, size_t buff_size, uint64_t num){ + unsigned ctr = 0; + while (num >= 0x80 && ctr < buff_size) { + *buff = (uint8_t) (((num) & 0x7f) | 0x80); + ++buff; + ++ctr; + num >>= 7; + } + + /* writes the last one to dest */ + if (ctr < buff_size) { + *buff = (uint8_t) num; + ++ctr; + } + return ctr <= buff_size ? (int)ctr : -1; +} + +int xmr_read_varint(uint8_t * buff, size_t buff_size, uint64_t *val) { + unsigned read = 0; + int finished_ok = 0; + *val = 0; + + for (int shift = 0; read < buff_size; shift += 7, ++read) { + uint8_t byte = buff[read]; + if (byte == 0 && shift != 0) { + return -1; + } + + *val |= (uint64_t)(byte & 0x7f) << shift; + + /* If there is no next */ + if ((byte & 0x80) == 0) { + finished_ok = 1; + break; + } + } + return finished_ok ? (int)read + 1 : -2; +} + diff --git a/monero/serialize.h b/monero/serialize.h new file mode 100644 index 0000000000..f2b0fb9033 --- /dev/null +++ b/monero/serialize.h @@ -0,0 +1,15 @@ +// +// Created by Dusan Klinec on 02/05/2018. +// + +#ifndef TREZOR_XMR_SERIALIZE_H +#define TREZOR_XMR_SERIALIZE_H + +#include +#include + +int xmr_size_varint(uint64_t num); +int xmr_write_varint(uint8_t * buff, size_t buff_size, uint64_t num); +int xmr_read_varint(uint8_t * buff, size_t buff_size, uint64_t *val); + +#endif //TREZOR_XMR_SERIALIZE_H diff --git a/monero/xmr.c b/monero/xmr.c new file mode 100644 index 0000000000..ccac66d951 --- /dev/null +++ b/monero/xmr.c @@ -0,0 +1,159 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#include "xmr.h" +#include "int-util.h" +#include "serialize.h" +#include "rand.h" + + +const ge25519 ALIGN(16) xmr_h = { + {0x1861ec7, 0x1ceac77, 0x2f11626, 0x1f261d3, 0x346107c, 0x06d8c4a, 0x254201d, 0x1675c09, 0x1301c3f, 0x0211d73}, + {0x326feb4, 0x12e30cc, 0x0cf54b4, 0x1117305, 0x318f5d5, 0x06cf754, 0x2e578a1, 0x1daf058, 0x34430a1, 0x04410e9}, + {0x0fde4d2, 0x0774049, 0x22ca951, 0x05aec2b, 0x07a36a5, 0x1394f13, 0x3c5385c, 0x1adb924, 0x2b6c581, 0x0a55fa4}, + {0x24517f7, 0x05ee936, 0x3acf5d9, 0x14b08aa, 0x3363738, 0x1051745, 0x360601e, 0x0f3f2c9, 0x1ead2cd, 0x1d3e3df} +}; + + +void ge25519_set_xmr_h(ge25519 *r){ + ge25519_copy(r, &xmr_h); +} + +void xmr_random_scalar(bignum256modm m){ + unsigned char buff[32]={0}; + random_buffer(buff, sizeof(buff)); + expand256_modm(m, buff, sizeof(buff)); +} + +void xmr_fast_hash(uint8_t * hash, const void *data, size_t length){ + hasher_Raw(HASHER_SHA3K, data, length, hash); +} + +void xmr_hasher_init(Hasher * hasher){ + hasher_Init(hasher, HASHER_SHA3K); +} + +void xmr_hasher_update(Hasher * hasher, const void *data, size_t length){ + hasher_Update(hasher, data, length); +} + +void xmr_hasher_final(Hasher * hasher, uint8_t * hash){ + hasher_Final(hasher, hash); +} + +void xmr_hasher_copy(Hasher * dst, const Hasher * src){ + memcpy(dst, src, sizeof(Hasher)); +} + +void xmr_hash_to_scalar(bignum256modm r, const void *data, size_t length){ + uint8_t hash[HASHER_DIGEST_LENGTH]; + hasher_Raw(HASHER_SHA3K, data, length, hash); + expand256_modm(r, hash, HASHER_DIGEST_LENGTH); +} + +void xmr_hash_to_ec(ge25519 *P, const void *data, size_t length){ + ge25519 point2; + uint8_t hash[HASHER_DIGEST_LENGTH]; + hasher_Raw(HASHER_SHA3K, data, length, hash); + + ge25519_fromfe_frombytes_vartime(&point2, hash); + ge25519_mul8(P, &point2); +} + +void xmr_derivation_to_scalar(bignum256modm s, const ge25519 * p, uint32_t output_index){ + uint8_t buff[32 + 8]; + ge25519_pack(buff, p); + int written = xmr_write_varint(buff + 32, 8, output_index); + xmr_hash_to_scalar(s, buff, 32u + written); +} + +void xmr_generate_key_derivation(ge25519 * r, const ge25519 * A, const bignum256modm b){ + ge25519 bA; + ge25519_scalarmult(&bA, A, b); + ge25519_norm(&bA, &bA); + ge25519_mul8(r, &bA); +} + +void xmr_derive_private_key(bignum256modm s, const ge25519 * deriv, uint32_t idx, const bignum256modm base){ + xmr_derivation_to_scalar(s, deriv, idx); + add256_modm(s, s, base); +} + +void xmr_derive_public_key(ge25519 * r, const ge25519 * deriv, uint32_t idx, const ge25519 * base){ + bignum256modm s={0}; + ge25519 p2; + ge25519_pniels Bp; + ge25519_p1p1 p1; + + xmr_derivation_to_scalar(s, deriv, idx); + ge25519_scalarmult_base_niels(&p2, ge25519_niels_base_multiples, s); + ge25519_norm(&p2, &p2); + + ge25519_full_to_pniels(&Bp, base); + ge25519_pnielsadd_p1p1(&p1, &p2, &Bp, 0); + ge25519_p1p1_to_full(r, &p1); +} + +void xmr_add_keys2(ge25519 * r, const bignum256modm a, const bignum256modm b, const ge25519 * B){ + // aG + bB, G is basepoint + ge25519 aG, bB; + ge25519_pniels bBn; + ge25519_p1p1 p1; + ge25519_scalarmult_base_niels(&aG, ge25519_niels_base_multiples, a); + ge25519_scalarmult(&bB, B, b); + ge25519_norm(&bB, &bB); + ge25519_norm(&aG, &aG); + + ge25519_full_to_pniels(&bBn, &bB); + ge25519_pnielsadd_p1p1(&p1, &aG, &bBn, 0); + ge25519_p1p1_to_full(r, &p1); +} + +void xmr_add_keys2_vartime(ge25519 * r, const bignum256modm a, const bignum256modm b, const ge25519 * B){ + // aG + bB, G is basepoint + ge25519_double_scalarmult_vartime(r, B, b, a); + ge25519_norm(r, r); +} + +void xmr_add_keys3(ge25519 * r, const bignum256modm a, const ge25519 * A, const bignum256modm b, const ge25519 * B){ + // aA + bB + ge25519 aA, bB; + ge25519_pniels bBn; + ge25519_p1p1 p1; + ge25519_scalarmult(&aA, A, a); + ge25519_scalarmult(&bB, B, b); + ge25519_norm(&bB, &bB); + ge25519_norm(&aA, &aA); + + ge25519_full_to_pniels(&bBn, &bB); + ge25519_pnielsadd_p1p1(&p1, &aA, &bBn, 0); + ge25519_p1p1_to_full(r, &p1); +} + +void xmr_add_keys3_vartime(ge25519 * r, const bignum256modm a, const ge25519 * A, const bignum256modm b, const ge25519 * B){ + // aA + bB + ge25519_double_scalarmult_vartime2(r, A, a, B, b); + ge25519_norm(r, r); +} + +void xmr_get_subaddress_secret_key(bignum256modm r, uint32_t major, uint32_t minor, const bignum256modm m){ + const char prefix[] = "SubAddr"; + unsigned char buff[32]; + contract256_modm(buff, m); + + char data[sizeof(prefix) + sizeof(buff) + 2 * sizeof(uint32_t)]; + memcpy(data, prefix, sizeof(prefix)); + memcpy(data + sizeof(prefix), buff, sizeof(buff)); + memcpy(data + sizeof(prefix) + sizeof(buff), &major, sizeof(uint32_t)); + memcpy(data + sizeof(prefix) + sizeof(buff) + sizeof(uint32_t), &minor, sizeof(uint32_t)); + + xmr_hash_to_scalar(r, data, sizeof(data)); +} + +void xmr_gen_c(ge25519 * r, const bignum256modm a, uint64_t amount){ + // C = aG + bH + bignum256modm b={0}; + set256_modm(b, amount); + xmr_add_keys2(r, a, b, &xmr_h); +} diff --git a/monero/xmr.h b/monero/xmr.h new file mode 100644 index 0000000000..4e37da9a1b --- /dev/null +++ b/monero/xmr.h @@ -0,0 +1,67 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#ifndef TREZOR_CRYPTO_XMR_H +#define TREZOR_CRYPTO_XMR_H + +#include +#include "hasher.h" + +extern const ge25519 ALIGN(16) xmr_h; + +typedef unsigned char xmr_key_t[32]; + +typedef struct xmr_ctkey { + xmr_key_t dest; + xmr_key_t mask; +} xmr_ctkey_t; + +/* sets H point to r */ +void ge25519_set_xmr_h(ge25519 *r); + +/* random scalar value */ +void xmr_random_scalar(bignum256modm m); + +/* cn_fast_hash */ +void xmr_fast_hash(uint8_t * hash, const void *data, size_t length); + +/* incremental hashing wrappers */ +void xmr_hasher_init(Hasher * hasher); +void xmr_hasher_update(Hasher * hasher, const void *data, size_t length); +void xmr_hasher_final(Hasher * hasher, uint8_t * hash); +void xmr_hasher_copy(Hasher * dst, const Hasher * src); + +/* H_s(buffer) */ +void xmr_hash_to_scalar(bignum256modm r, const void *data, size_t length); + +/* H_p(buffer) */ +void xmr_hash_to_ec(ge25519 *P, const void *data, size_t length); + +/* derivation to scalar value */ +void xmr_derivation_to_scalar(bignum256modm s, const ge25519 * p, uint32_t output_index); + +/* derivation */ +void xmr_generate_key_derivation(ge25519 * r, const ge25519 * A, const bignum256modm b); + +/* H_s(derivation || varint(output_index)) + base */ +void xmr_derive_private_key(bignum256modm s, const ge25519 * deriv, uint32_t idx, const bignum256modm base); + +/* H_s(derivation || varint(output_index))G + base */ +void xmr_derive_public_key(ge25519 * r, const ge25519 * deriv, uint32_t idx, const ge25519 * base); + +/* aG + bB, G is basepoint */ +void xmr_add_keys2(ge25519 * r, const bignum256modm a, const bignum256modm b, const ge25519 * B); +void xmr_add_keys2_vartime(ge25519 * r, const bignum256modm a, const bignum256modm b, const ge25519 * B); + +/* aA + bB */ +void xmr_add_keys3(ge25519 * r, const bignum256modm a, const ge25519 * A, const bignum256modm b, const ge25519 * B); +void xmr_add_keys3_vartime(ge25519 * r, const bignum256modm a, const ge25519 * A, const bignum256modm b, const ge25519 * B); + +/* subaddress secret */ +void xmr_get_subaddress_secret_key(bignum256modm r, uint32_t major, uint32_t minor, const bignum256modm m); + +/* Generates Pedersen commitment C = aG + bH */ +void xmr_gen_c(ge25519 * r, const bignum256modm a, uint64_t amount); + +#endif //TREZOR_CRYPTO_XMR_H diff --git a/tests/test_check.c b/tests/test_check.c index ae932e9158..86389b46a7 100644 --- a/tests/test_check.c +++ b/tests/test_check.c @@ -58,11 +58,13 @@ #include "ed25519-donna/ed25519.h" #include "ed25519-donna/ed25519-donna.h" #include "ed25519-donna/ed25519-keccak.h" +#include "ed25519-donna/ge25519.h" #include "script.h" #include "rfc6979.h" #include "address.h" #include "rc4.h" #include "nem.h" +#include "monero/monero.h" #if VALGRIND /* @@ -4808,6 +4810,10 @@ END_TEST #include "test_check_cardano.h" #endif +#if USE_MONERO +#include "test_check_monero.h" +#endif + // define test suite and cases Suite *test_suite(void) { @@ -5008,9 +5014,11 @@ Suite *test_suite(void) tcase_add_test(tc, test_ed25519_modl_sub); suite_add_tcase(s, tc); +#if USE_MONERO tc = tcase_create("ed25519_ge"); tcase_add_test(tc, test_ge25519_double_scalarmult_vartime2); suite_add_tcase(s, tc); +#endif tc = tcase_create("script"); tcase_add_test(tc, test_output_script); @@ -5067,6 +5075,47 @@ Suite *test_suite(void) suite_add_tcase(s,tc); #endif +#if USE_MONERO + tc = tcase_create("xmr_base58"); + tcase_add_test(tc, test_xmr_base58); + suite_add_tcase(s, tc); + + tc = tcase_create("xmr_crypto"); + tcase_add_test(tc, test_xmr_getset256_modm); + tcase_add_test(tc, test_xmr_cmp256_modm); + tcase_add_test(tc, test_xmr_copy_check_modm); + tcase_add_test(tc, test_xmr_mulsub256_modm); + tcase_add_test(tc, test_xmr_muladd256_modm); + tcase_add_test(tc, test_xmr_curve25519_set); + tcase_add_test(tc, test_xmr_curve25519_consts); + tcase_add_test(tc, test_xmr_curve25519_tests); + tcase_add_test(tc, test_xmr_curve25519_expand_reduce); + tcase_add_test(tc, test_xmr_ge25519_base); + tcase_add_test(tc, test_xmr_ge25519_check); + tcase_add_test(tc, test_xmr_ge25519_scalarmult_base_wrapper); + tcase_add_test(tc, test_xmr_ge25519_scalarmult_wrapper); + tcase_add_test(tc, test_xmr_ge25519_ops); + suite_add_tcase(s, tc); + + tc = tcase_create("xmr_xmr"); + tcase_add_test(tc, test_xmr_check_point); + tcase_add_test(tc, test_xmr_h); + tcase_add_test(tc, test_xmr_fast_hash); + tcase_add_test(tc, test_xmr_hasher); + tcase_add_test(tc, test_xmr_hash_to_scalar); + tcase_add_test(tc, test_xmr_hash_to_ec); + tcase_add_test(tc, test_xmr_derivation_to_scalar); + tcase_add_test(tc, test_xmr_generate_key_derivation); + tcase_add_test(tc, test_xmr_derive_private_key); + tcase_add_test(tc, test_xmr_derive_public_key); + tcase_add_test(tc, test_xmr_add_keys2); + tcase_add_test(tc, test_xmr_add_keys3); + tcase_add_test(tc, test_xmr_get_subaddress_secret_key); + tcase_add_test(tc, test_xmr_gen_c); + tcase_add_test(tc, test_xmr_varint); + tcase_add_test(tc, test_xmr_gen_range_sig); + suite_add_tcase(s, tc); +#endif return s; } diff --git a/tests/test_check_monero.h b/tests/test_check_monero.h new file mode 100644 index 0000000000..c76edefc1a --- /dev/null +++ b/tests/test_check_monero.h @@ -0,0 +1,1216 @@ +#if USE_MONERO +START_TEST(test_xmr_base58) +{ + static const struct { + uint64_t tag; + char *v1; + char *v2; + } tests[] = { + {0x12, + "3bec484c5d7f0246af520aab550452b5b6013733feabebd681c4a60d457b7fc12d5918e31d3c003da3c778592c07b398ad6f961a67082a75fd49394d51e69bbe", + "43tpGG9PKbwCpjRvNLn1jwXPpnacw2uVUcszAtgmDiVcZK4VgHwjJT9BJz1WGF9eMxSYASp8yNMkuLjeQfWqJn3CNWdWfzV" + }, + {0x12, + "639050436fa36c8288706771412c5972461578d564188cd7fc6f81d6973d064fa461afe66fb23879936d7225051bebbf7f3ae0c801a90bb99fbb346b2fd4d702", + "45PwgoUKaDHNqLL8o3okzLL7biv7GqPVmd8LTcTrYVrMEKdSYwFcyJfMLSRpfU3nh8Z2m81FJD4sUY3nXCdGe61k1HAp8T1" + }, + {53, + "5a10cca900ee47a7f412cd661b29f5ab356d6a1951884593bb170b5ec8b6f2e83b1da411527d062c9fedeb2dad669f2f5585a00a88462b8c95c809a630e5734c", + "9vacMKaj8JJV6MnwDzh2oNVdwTLJfTDyNRiB6NzV9TT7fqvzLivH2dB8Tv7VYR3ncn8vCb3KdNMJzQWrPAF1otYJ9cPKpkr" + }, + }; + + uint8_t rawn[512]; + char strn[512]; + int r; + uint64_t tag; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + const char *raw = tests[i].v1; + const char *str = tests[i].v2; + const size_t len = strlen(raw) / 2; + + memcpy(rawn, fromhex(raw), len); + + r = xmr_base58_addr_encode_check(tests[i].tag, rawn, len, strn, sizeof(strn)); + ck_assert_int_eq((size_t) r, strlen(str)); + ck_assert_mem_eq(strn, str, r); + + r = xmr_base58_addr_decode_check(strn, r, &tag, rawn, len); + ck_assert_int_eq(r, len); + ck_assert_mem_eq(rawn, fromhex(raw), len); + } +} +END_TEST + + +START_TEST(test_xmr_getset256_modm) +{ + static const struct { + uint64_t val; + int r; + char *a; + } tests[] = { + {0x0, 1, + "0000000000000000000000000000000000000000000000000000000000000000"}, + {0x7fffffffULL, 1, + "ffffff7f00000000000000000000000000000000000000000000000000000000"}, + {0x7fffffffffffffffULL, 1, + "ffffffffffffff7f000000000000000000000000000000000000000000000000"}, + {0xdeadc0deULL, 1, + "dec0adde00000000000000000000000000000000000000000000000000000000"}, + {0x0, 0, + "dec0adde000000000000000000000000000000000000000000000000000000ff"}, + {0x0, 0, + "ffffffffffffffffff0000000000000000000000000000000000000000000000"}, + }; + + uint8_t rawn[32]; + uint64_t v1; + bignum256modm a1 = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + int get_res = tests[i].r; + if (get_res) { + set256_modm(a1, tests[i].val); + ck_assert_int_eq(get256_modm(&v1, a1), 1); + ck_assert(v1 == tests[i].val); + + contract256_modm(rawn, a1); + ck_assert_mem_eq(rawn, fromhex(tests[i].a), 32); + + } else { + expand256_modm(a1, fromhex(tests[i].a), 32); + ck_assert_int_eq(get256_modm(&v1, a1), 0); + } + } +} +END_TEST + + +START_TEST(test_xmr_cmp256_modm) +{ + static const struct { + char *a; + char *b; + int res_eq; + int res_cmp; + int res_is_zero_a; + } tests[] = { + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + 1, 0, 1 + }, + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0100000000000000000000000000000000000000000000000000000000000000", + 0, -1, 1 + }, + { + "dec0adde00000000000000000000000000000000000000000000000000000000", + "dec0adde00000000000000000000000000000000000000000000000000000000", + 1, 0, 0 + }, + { + "863346d8863c461cde2ec7c2759352c2b952228f33a86ca06bb79574bbe5c30d", + "3ddbd65a6d3ba5e2ab120603685a353a27ce3fd21dfdbea7952d2dd26f1ca00a", + 0, 1, 0 + }, + { + "f7667f392edbea6e224b1aa9fbf2a3b238b4f977fb4a8f39130cc45f49b5c40a", + "b41b9b1e7e80be71cf290ed4bded58924086b8ac6bdfa1faa0c80c255f074d07", + 0, 1, 0 + }, + { + "0e4005c7826de8f9978749903f40efd140e4ae6d3bed09e558fcce8367b27501", + "0e4005c7826de8f9978749903f40efd140e4ae6d3bed09e558fcce8367b27504", + 0, -1, 0 + }, + { + "0e4005c7826de8f9978749903f40efd140e4ae6d3bed09e558fcce8367b27504", + "0e4005c7826de8f9978749903f40efd140e4ae6d3bed09e558fcce8367b27504", + 1, 0, 0 + }, + { + "0e4005c7826de8f9978749903f40efd140e4ae6d3bed09e558fcce8367b27504", + "0e4005c7826de8f9978749903f41efd140e4ae6d3bed09e558fcce8367b27504", + 0, -1, 0 + }, + }; + + bignum256modm a1 = {0}, a2 = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a1, fromhex(tests[i].a), 32); + expand256_modm(a2, fromhex(tests[i].b), 32); + + ck_assert_int_eq(eq256_modm(a1, a2), tests[i].res_eq); + ck_assert_int_eq(cmp256_modm(a1, a2), tests[i].res_cmp); + ck_assert_int_eq(iszero256_modm(a1), tests[i].res_is_zero_a); + } +} +END_TEST + + +START_TEST(test_xmr_copy_check_modm) +{ + static const struct { + int check; + char *a; + } tests[] = { + {0, + "0000000000000000000000000000000000000000000000000000000000000000"}, + {1, + "ffffff7f00000000000000000000000000000000000000000000000000000000"}, + {1, + "ffffffffffffff7f000000000000000000000000000000000000000000000000"}, + {1, + "dec0adde00000000000000000000000000000000000000000000000000000000"}, + {0, + "dec0adde000000000000000000000fffffffffffffffffffffffffffffffffff"}, + }; + + bignum256modm a1 = {0}, a2 = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand_raw256_modm(a1, fromhex(tests[i].a)); + copy256_modm(a2, a1); + ck_assert_int_eq(eq256_modm(a1, a2), 1); + ck_assert_int_eq(check256_modm(a1), tests[i].check); + } + +} +END_TEST + + +START_TEST(test_xmr_mulsub256_modm) +{ + static const struct { + char *a; + char *b; + char *c; + char *r; + } tests[] = { + { + "713c199348cf7d14b67ae6265ea49c02c8647f07afcbcb6f8d3254b3db972e02", + "4e48a7b7a03ab1106fdfa9441a03c97c644395a12ac4b8effac7344e0719c200", + "1a5711b8c43bcab0161a620368d82727e1d027dc248f420d9bb4db2486c16405", + "6edcc08aa6ec3a5b3d333b5f826be7de9c268be8aaf9521586fbcccbed3b1c0c", + }, + { + "d4ade2c62d34af8cfd9daec6f46bf7e57962a8aa46935cb11fab64fa599b4700", + "22ea7989a9f4d34cd8c9442e03b5062dfe8493757cd18a63411cb1a25e44960f", + "772053e613f0859387badcefeb7fbe551a05b00b9337539c8d72661de5929806", + "a5063258df4520b33e97c0a46d80feeace5c251fc7ef7a938d160b8f25795106", + }, + { + "01fd2ef25c8221277a2b6daf1f1642bacb8d6ac0dd4f62731cdd73e26eb77900", + "0611b9357530aa638428002769ce0ad553421e971bea1f10d7009bf26d9af805", + "dfece232068b2f8059ca569f345baaed13ab464eb3bebb99de5625dc90a8cf03", + "85752e62bd8085c7c02d5edeb74969d22f1a5bb34349258d2e96de300176bb07", + }, + }; + + bignum256modm a = {0}, b = {0}, c = {0}, r = {0}, r2 = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i].a), 32); + expand256_modm(b, fromhex(tests[i].b), 32); + expand256_modm(c, fromhex(tests[i].c), 32); + expand256_modm(r, fromhex(tests[i].r), 32); + mulsub256_modm(r2, a, b, c); + ck_assert_int_eq(eq256_modm(r, r2), 1); + } +} +END_TEST + + +START_TEST(test_xmr_muladd256_modm) +{ + static const struct { + char *a; + char *b; + char *c; + char *r; + } tests[] = { + { + "7c3fd8abfbe2be3739d91679ac8dbda086961b941e0d4a00561f758927d8aa09", + "ac2d8d37e4f344aa4040d0f0fc29d45423ab7e69ecacb94ca9fc36819e0e990e", + "2f03f1bac09bc7d002848b68be069dc98b2db028390ae37e13a5166fcae08105", + "dce113add3392f08e3b38b7d31e237eba5066e5a95a1fdbf755b92d05e1ec70b", + }, + { + "6979b70f6198d043f4b14e2069f7b89cc9f09e3465e71d472946443989e0e80c", + "8dd5177bc8d7c5bd58c0be74b336952a73ac259ebb812ac8cd755773c6aab807", + "d7658e508a7454ccfb29e2890d6156ac10e18ebe6e00cc5a2d2d87a5080c7f06", + "51b33f6263772781cdbab26ef48870eaf94899894a437dac39496f15b9d0ae00", + }, + { + "ebfdb4eabedb1fb9a45b3204735b0511871e20358392fa16a851c519e3a29b09", + "59d98831e9f9e24260158986c4d4035438de9b8876cc11bdcf4c364c75f72908", + "93bce4764eee97dc67f2e37da40bc5641f2cdc637285d273287a3d4383b68f02", + "21547ca6855c85d5adcd673b9d801d0cb0f10dced8f8b68a8c2f74163defde0e", + }, + }; + + bignum256modm a = {0}, b = {0}, c = {0}, r = {0}, r2 = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i].a), 32); + expand256_modm(b, fromhex(tests[i].b), 32); + expand256_modm(c, fromhex(tests[i].c), 32); + expand256_modm(r, fromhex(tests[i].r), 32); + muladd256_modm(r2, a, b, c); + ck_assert_int_eq(eq256_modm(r, r2), 1); + } + +} +END_TEST + + +START_TEST(test_xmr_curve25519_set) +{ + static const struct { + uint32_t val; + char *a; + } tests[] = { + {0x0, + "0000000000000000000000000000000000000000000000000000000000000000"}, + {0x1, + "0100000000000000000000000000000000000000000000000000000000000000"}, + {0xdeadc0deUL, + "dec0adde00000000000000000000000000000000000000000000000000000000"}, + }; + + unsigned char buff[32]; + bignum25519 a = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + curve25519_set(a, tests[i].val); + curve25519_contract(buff, a); + ck_assert_mem_eq(buff, fromhex(tests[i].a), 32); + } +} +END_TEST + + +START_TEST(test_xmr_curve25519_consts) +{ + char *d = "a3785913ca4deb75abd841414d0a700098e879777940c78c73fe6f2bee6c0352"; + char *d2 = "59f1b226949bd6eb56b183829a14e00030d1f3eef2808e19e7fcdf56dcd90624"; + char *sqrtneg1 = "b0a00e4a271beec478e42fad0618432fa7d7fb3d99004d2b0bdfc14f8024832b"; + + unsigned char buff[32]; + bignum25519 a = {0}; + + curve25519_set_d(a); + curve25519_contract(buff, a); + ck_assert_mem_eq(buff, fromhex(d), 32); + + curve25519_set_2d(a); + curve25519_contract(buff, a); + ck_assert_mem_eq(buff, fromhex(d2), 32); + + curve25519_set_sqrtneg1(a); + curve25519_contract(buff, a); + ck_assert_mem_eq(buff, fromhex(sqrtneg1), 32); +} +END_TEST + + +START_TEST(test_xmr_curve25519_tests) +{ + static const struct { + char *a; + int res_neg; + int res_nonzero; + } tests[] = { + { + "0000000000000000000000000000000000000000000000000000000000000000", + 0, 0, + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + 1, 1, + }, + { + "05737aa6100ee54283dc0d483b8e39e61846f6b3736908243d0c824d250b3139", + 1, 1, + }, + { + "95587a5ef6900fa8e32d6a41bd8090b1e33e694284323d1d1f02d69865f2bc15", + 1, 1, + }, + { + "02587a5ef6900fa8e32d6a41bd8090b1e33e694284323d1d1f02d69865f2bc15", + 0, 1, + }, + }; + + bignum25519 a = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + curve25519_expand(a, fromhex(tests[i].a)); + ck_assert_int_eq(curve25519_isnegative(a), tests[i].res_neg); + ck_assert_int_eq(curve25519_isnonzero(a), tests[i].res_nonzero); + } +} +END_TEST + + +START_TEST(test_xmr_curve25519_expand_reduce) +{ + static const struct { + char *a; + char *b; + } tests[] = { + { + "dec0adde00000000000000000000000000000000000000000000000000000000", + "dec0adde00000000000000000000000000000000000000000000000000000000" + }, + { + "95587a5ef6900fa8e32d6a41bd8090b1e33e694284323d1d1f02d69865f2bc15", + "95587a5ef6900fa8e32d6a41bd8090b1e33e694284323d1d1f02d69865f2bc15" + }, + { + "95587a5ef6900fa8e32d6a41bd8090b1e33e694284323d1d1f02d69865f2bcff", + "a8587a5ef6900fa8e32d6a41bd8090b1e33e694284323d1d1f02d69865f2bc7f" + }, + { + "95587a5ef6900fa8e32d6affbd8090b1e33e694284323fffff02d69865f2bcff", + "a8587a5ef6900fa8e32d6affbd8090b1e33e694284323fffff02d69865f2bc7f" + }, + }; + + unsigned char buff[32]; + bignum25519 a = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + curve25519_expand_reduce(a, fromhex(tests[i].a)); + curve25519_contract(buff, a); + ck_assert_mem_eq(buff, fromhex(tests[i].b), 32); + } + +} +END_TEST + + +START_TEST(test_xmr_ge25519_base) +{ + unsigned char buff[32]; + char *base = "5866666666666666666666666666666666666666666666666666666666666666"; + ge25519 b; + ge25519_set_base(&b); + ge25519_pack(buff, &b); + ck_assert_mem_eq(buff, fromhex(base), 32); +} +END_TEST + + +START_TEST(test_xmr_ge25519_check) +{ + static const struct { + char *x; + char *y; + char *z; + char *t; + int r; + } tests[] = { + { + "4ff97748221f954414f836d84e8e7e207786bcd20eb67044756dca307e792c60", + "2c7be86ab07488ba43e8e03d85a67625cfbf98c8544de4c877241b7aaafc7f63", + "0100000000000000000000000000000000000000000000000000000000000000", + "3ec65b03954ce7432525b9b3f4a9f5747f57b40903d1bf8892527366325fe036", 1 + }, + { + "358fd25e4b84397d207e23cf3a75819bd6b2254cabc990b31ad63873cc38fc7c", + "ca48045f790145a1eec3946dfd73747fde0fdb4238607e0a203f8ef5bef90e0e", + "0100000000000000000000000000000000000000000000000000000000000000", + "6c5e5cbae4b05e149d0aca50bf7b4112acbbe6233ace9c8bd5bcedf34df9ce0b", 1 + }, + { + "4ff97748221f954414f836d84e8e7e207786bcd20eb6704475ffca307e792c60", + "2c7be86ab07488ba43e8e03d85a67625cfbf98c8544de4c877241b7aaafc7f63", + "0100000000000000000000000000000000000000000000000000000000000000", + "3ec65b03954ce7432525b9b3f4a9f5747f57b40903d1bf8892527366325fe036", 0 + }, + { + "358fd25e4b84397d207e23cf3a75819bd6b2254cabc990b31ad63873cc38fc7c", + "ca48045f790145a1eec3946dfd73747fdfffdb4238607e0a203f8ef5bef90e0e", + "0100000000000000000000000000000000000000000000000000000000000000", + "6c5e5cbae4b05e149d0aca50bf7b4112acbbe6233ace9c8bd5bcedf34df9ce0b", 0 + }, + { + "358fd25e4b84397d207e23cf3a75819bd6b2254cabc990b31ad63873cc38fc7c", + "ca48045f790145a1eec3946dfd73747fdfffdb4238607e0a203f8ef5bef90e0e", + "0100000000000000000000000000000000000000000000000000000000000000", + "6c5e5ffae4b05e149d0aca50bf7b4112acbbe6233ace9c8bd5bcedf34df9ce0b", 0 + }, + }; + + struct ge25519_t p; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + curve25519_expand_reduce(p.x, fromhex(tests[i].x)); + curve25519_expand_reduce(p.y, fromhex(tests[i].y)); + curve25519_expand_reduce(p.z, fromhex(tests[i].z)); + curve25519_expand_reduce(p.t, fromhex(tests[i].t)); + ck_assert_int_eq(ge25519_check(&p), tests[i].r); + } +} +END_TEST + + +START_TEST(test_xmr_ge25519_scalarmult_base_wrapper) +{ + static const struct { + char *sc; + char *pt; + } tests[] = { + { + "40be740e26bd1c84f5a8fec737c0ed30e87bd45adfcd91e320f8dfb68b1a870e", + "b7a8b2f3dbfd41b38d20aec733a316dbfc2633503799cd36f38570cafc8ea887", + }, + { + "1b3746add992215d427e43a58354c11ff9e6dfa1c187250938f7f9334fa41d05", + "e2a1bfbe38a9749fe6ede79d923b778fa4c89393473d633bec01fa68617d0828", + }, + { + "69af25c54090a9746d3f6043348452429ffd53c1530fa114fd0055b70d61020f", + "6bf1783b0a7495d5f6c36605dca95e723ca120a306c255084787f09b12771124", + }, + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0100000000000000000000000000000000000000000000000000000000000000", + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "5866666666666666666666666666666666666666666666666666666666666666", + }, + { + "0800000000000000000000000000000000000000000000000000000000000000", + "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", + }, + { + "ffffffffffffffff000000000000000000000000000000000000000000000000", + "e185757a3fdc6519a6e7bebd97aa52bdc999e4c87d5c3aad0d995763ab6c6985", + }, + }; + + ge25519 pt, pt2; + bignum256modm sc; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(sc, fromhex(tests[i].sc), 32); + ge25519_unpack_vartime(&pt, fromhex(tests[i].pt)); + ge25519_scalarmult_base_wrapper(&pt2, sc); + ck_assert_int_eq(ge25519_eq(&pt, &pt2), 1); + } +} +END_TEST + + +START_TEST(test_xmr_ge25519_scalarmult_wrapper) +{ + static const struct { + char *sc; + char *pt; + char *pt2; + } tests[] = { + { + "0000000000000000000000000000000000000000000000000000000000000000", + "5cbb3b2784c16f0e7eb4f2a7f93288552bb24ec51c5e01504c1e6885cfbca6d0", + "0100000000000000000000000000000000000000000000000000000000000000", + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "f39b6770008d069acb92eb95329dec2cb0054da024e437a1bdf1ae06527deff6", + "f39b6770008d069acb92eb95329dec2cb0054da024e437a1bdf1ae06527deff6", + }, + { + "3930000000000000000000000000000000000000000000000000000000000000", + "2835b3983e3cc01a640fd188bf6bbbafbf997a3344d800eed22e4e82a412941c", + "2fe8b2dd0f23e02fca6989e170135584d684583c0a44f6a7d3ebd964685d36c7", + }, + { + "ffffffffffffffff000000000000000000000000000000000000000000000000", + "bb8af7a53a8f1b477c810e833a84cdc789a6b81a6b6417be4f97ffd9ae0fe0b8", + "3a5c9a7dacca9dd8827881f38c36aad7d402a5efc2cab58c7553b903876e1491", + }, + { + "864203a09e1c788a482685c739af07355ebb2c840b7de6af87eff5f19ee3b807", + "d404a9bbf351e7320ea6d11cdeeccaf505f706731cb5e5d839b950edb7ba6286", + "11e09c89e0be7663e0e2d4a01fb05d6a3fd84a78a6fa4fd7daaacf2d19311a38", + }, + { + "3e01f05920a238e33766814d10f0c3a3e975072399ad90a823d4808db1d85209", + "52a2d35798a0ac209b8fa194fe398b869aba5f20d80ee3d8ca77759a8e0bae0d", + "4256addc2f036150f3fdc0a7905f01285239d6dd4eecc4be8e3b134eef4639fe", + }, + { + "ad63d591716a9e89a024a074bc6ce661268d1bb3665f91e8b981f189b1a49507", + "3928bde7a92e1341c3dfee35a66fa5639204f5b9747963278af430145028648d", + "9c959003ba91004956df98800a5024d94031db5ac659675b26350657d93c34f9", + }, + }; + + ge25519 pt, pt2, pt3; + bignum256modm sc; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(sc, fromhex(tests[i].sc), 32); + ge25519_unpack_vartime(&pt, fromhex(tests[i].pt)); + ge25519_unpack_vartime(&pt2, fromhex(tests[i].pt2)); + ge25519_scalarmult_wrapper(&pt3, &pt, sc); + ck_assert_int_eq(ge25519_eq(&pt3, &pt2), 1); + } +} +END_TEST + + +START_TEST(test_xmr_ge25519_ops) +{ + int tests[] = {1, 2, 7, 8, 637, 9912, 12345}; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + struct ge25519_t a, b, c, d; + bignum256modm s1 = {0}, s2 = {0}, s3 = {0}; + + set256_modm(s1, tests[i]); + set256_modm(s2, 8 * tests[i]); + set256_modm(s3, 8); + + ge25519_scalarmult_base_wrapper(&a, s1); + ge25519_mul8(&b, &a); + ge25519_scalarmult_base_wrapper(&c, s2); + ck_assert_int_eq(ge25519_eq(&b, &c), 1); + + ge25519_scalarmult_wrapper(&d, &a, s3); + ck_assert_int_eq(ge25519_eq(&d, &c), 1); + + ge25519_copy(&a, &b); + ge25519_neg_full(&b); + ck_assert_int_eq(ge25519_eq(&b, &c), 0); + + ge25519_add(&c, &a, &b, 0); + set256_modm(s2, 0); + ge25519_scalarmult_base_wrapper(&a, s2); + ck_assert_int_eq(ge25519_eq(&a, &c), 1); + } +} +END_TEST + + +START_TEST(test_xmr_check_point) +{ + static const struct { + char *p; + bool on; + } tests[] = { + {"001000a93e0e6937b4feaf079e418a028ca85459aa39ac3871b94076f88ca608", true}, + {"54863a0464c008acc99cffb179bc6cf34eb1bbdf6c29f7a070a7c6376ae30ab5", true}, + {"bebe3c84092c0f7a92704cafb16562cc45c47f45e84baec8d4bba3559d1c1808", true}, + {"00000000000000c60073ec000000000000ff0000000000000000000000000080", false}, + {"00000000000000004e0000000000000000000000000000000000000000000000", false}, + {"0000008b0000000000000000b200000000000000000000000000000000000080", false}, + {"a0953eebe2f676256c37af4f6f84f32d397aaf3b73606e96c5ddfcecbb1ceec8", false}, + {"a82cd837efee505ec8425769ea925bee869ec3c78a57708c64c2ef2bd6ad3b88", false}, + {"031c56cfc99758f6f025630e77c6dea0b853c3ab0bf6cf8c8dab03d1a4618178", false}, + }; + + ge25519 tmp; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + int res = ge25519_unpack_negative_vartime(&tmp, fromhex(tests[i].p)); + ck_assert_int_eq(ge25519_check(&tmp), tests[i].on); + ck_assert_int_eq(res, tests[i].on); + } +} +END_TEST + + +START_TEST(test_xmr_h) +{ + char *H = "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"; + ge25519 H2, Z; + ge25519_p1p1 P_11; + ge25519_pniels P_ni; + uint8_t buff[32] = {0}; + + ge25519_pack(buff, &xmr_h); + ck_assert_mem_eq(buff, fromhex(H), 32); + + int res = ge25519_unpack_vartime(&H2, buff); + ck_assert_int_eq(res, 1); + ck_assert_int_eq(ge25519_eq(&xmr_h, &xmr_h), 1); + ck_assert_int_eq(ge25519_eq(&H2, &xmr_h), 1); + + res = ge25519_unpack_negative_vartime(&H2, buff); + ck_assert_int_eq(res, 1); + ck_assert_int_eq(ge25519_eq(&H2, &xmr_h), 0); + ge25519_neg_full(&H2); + ck_assert_int_eq(ge25519_eq(&H2, &xmr_h), 1); + + ge25519_full_to_pniels(&P_ni, &xmr_h); + ge25519_pnielsadd_p1p1(&P_11, &H2, &P_ni, 1); + ge25519_p1p1_to_full(&H2, &P_11); + ge25519_set_neutral(&Z); + ck_assert_int_eq(ge25519_eq(&Z, &H2), 1); +} +END_TEST + + +START_TEST(test_xmr_fast_hash) +{ + uint8_t hash[32]; + char tests[][2][65] = { + {"", "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"}, + {"00", "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"}, + {"000102", "f84a97f1f0a956e738abd85c2e0a5026f8874e3ec09c8f012159dfeeaab2b156"}, + {"000102030405", "51e8babe8b42352100dffa7f7b3843c95245d3d545c6cbf5052e80258ae80627"}, + {"000102030406", "74e7a0111ee2390dc68269a549a76dcfb553ca1260035eae982d669ff6494f32"}, + {"000102030407", "3a81c5d02a87786343f88414aae150a09f6933b1d3bb660d0a9ac54e12e5cd86"}, + {"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff64", "7fb4d1c8e32f7414fe8c7b2774ec05bff6845e4278565d17f95559513a244da2"}, + {"44caa1c26187afe8dacc5d91cb8a51282334d9308a818fe4d3607275e2a61f05", "2998fe52f8b9883149babd9c546912c3edfbd3cd98896a0e57b1b5929fa5ff7b"}, + }; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + xmr_fast_hash(hash, fromhex(tests[i][0]), strlen(tests[i][0]) / 2); + ck_assert_mem_eq(hash, fromhex(tests[i][1]), 32); + } +} +END_TEST + + +START_TEST(test_xmr_hasher) +{ + Hasher hasher; + uint8_t hash[32]; + + static const struct { + char * chunk[3]; + char * hash; + } tests[] = { + { {"00", "01", "02"}, + "f84a97f1f0a956e738abd85c2e0a5026f8874e3ec09c8f012159dfeeaab2b156"}, + { {"001122334455667788", "00", ""}, + "72a228ee8d0d01c815f112ce315cfc215a0594abcec24162304ae0ffda139d9e"}, + { {"001000a93e0e6937b4feaf079e418a028ca85459aa39ac3871b94076f88ca608", "", "00112233445566"}, + "c3deafd96ff10cc190c6024548c344f6401cfe5151ab2fcd40df7cc501147e01"}, + }; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + xmr_hasher_init(&hasher); + for(int j = 0; j < 3; j++){ + xmr_hasher_update(&hasher, fromhex(tests[i].chunk[j]), strlen(tests[i].chunk[j]) / 2); + } + xmr_hasher_final(&hasher, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), 32); + } +} +END_TEST + + +START_TEST(test_xmr_hash_to_scalar) +{ + bignum256modm a1; + unsigned char out[32]; + char tests[][2][65] = { + {"", "4a078e76cd41a3d3b534b83dc6f2ea2de500b653ca82273b7bfad8045d85a400"}, + {"00", "5497c9b6a7059553835f85118dc089d66512f7b477d66591ff96a9e064bcc90a"}, + {"000102", "5727ca206dbafa2e099b022ed528f5bdf7874e3ec09c8f012159dfeeaab2b106"}, + {"000102030405", "7740cf04577c107153a50b3abe44859f5245d3d545c6cbf5052e80258ae80607"}, + {"000102030406", "ad6bbffaceb8020543ac82bcadb9d090b553ca1260035eae982d669ff6494f02"}, + {"000102030407", "d2e116e9576ee5a29011c8fcb41259f99e6933b1d3bb660d0a9ac54e12e5cd06"}, + {"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff64", "3d6d3727dc50bca39e6ccfc9c12950eef5845e4278565d17f95559513a244d02"}, + {"44caa1c26187afe8dacc5d91cb8a51282334d9308a818fe4d3607275e2a61f05", "aecc45c83f0408c96c70f8273e94f930edfbd3cd98896a0e57b1b5929fa5ff0b"}, + }; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + xmr_hash_to_scalar(a1, fromhex(tests[i][0]), strlen(tests[i][0]) / 2); + contract256_modm(out, a1); + ck_assert_mem_eq(out, fromhex(tests[i][1]), 32); + } +} +END_TEST + + +START_TEST(test_xmr_hash_to_ec) +{ + ge25519 p1; + unsigned char out[32]; + char tests[][2][65] = { + {"", "d6d7d783ab18e1be65586adb7902a4175b737ef0b902875e1d1d5c5cf0478c0b"}, + {"00", "8e2fecb36320bc4e192e10ef54afc7c83fbeb0c38b7debd4fea51301f0bd4f3d"}, + {"000102", "73b233e2e75d81b9657a857e38e7ab2bc3600e5c56622b9fe4b976ff312220fa"}, + {"000102030405", "bebe3c84092c0f7a92704cafb16562cc45c47f45e84baec8d4bba3559d1c1808"}, + {"000102030406", "525567a6a40a94f2d916bc1efea234bbd3b9162403ec2faba871a90f8d0d487e"}, + {"000102030407", "99b1be2a92cbd22b24b48fb7a9daadd4d13a56915c4f6ed696f271ad5bdbc149"}, + {"42f6835bf83114a1f5f6076fe79bdfa0bd67c74b88f127d54572d3910dd09201", "54863a0464c008acc99cffb179bc6cf34eb1bbdf6c29f7a070a7c6376ae30ab5"}, + {"44caa1c26187afe8dacc5d91cb8a51282334d9308a818fe4d3607275e2a61f05", "001000a93e0e6937b4feaf079e418a028ca85459aa39ac3871b94076f88ca608"}, + }; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + xmr_hash_to_ec(&p1, fromhex(tests[i][0]), strlen(tests[i][0]) / 2); + ge25519_pack(out, &p1); + ck_assert_mem_eq(out, fromhex(tests[i][1]), 32); + } +} +END_TEST + + +START_TEST(test_xmr_derivation_to_scalar) +{ + static const struct { + char *pt; + uint32_t idx; + char *sc; + } tests[] = { + { + "c655b2d9d2670a1c9f26f7586b6d6b1ec5173b8b33bca64c3d305a42d66738b1", 0, + "ca7ce31b273dd1ac00dc3553e654fb66036804800e27c826bd2b78649243900b", + }, + { + "2b1dbd7a007dcc4d729fa8359705595599737fcef60afb36b379fe033095dca7", 1, + "60afd5a63b14845d3b92d16eac386713e4ff617fdc5c1a07c3212098c1f5610c", + }, + { + "a48ed3797225dab4b4316b5e40107b6bd63e5f4dc517ba602774d703576ec771", 24, + "fe81804091e50a5c2233faa6277360fbe1948ea15dddbae62c1d40bbd1918606", + }, + { + "fa27b5b39741f5341b4e89269e3a05ff7e76ec7739843872468fc4bec8475410", 65537, + "1ba36841f57aa8b799c4dd02b39d53e5fb7780d3f09f91a57a86dcb418d8d506", + }, + }; + + ge25519 pt; + bignum256modm sc, sc2; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(sc, fromhex(tests[i].sc), 32); + ge25519_unpack_vartime(&pt, fromhex(tests[i].pt)); + + xmr_derivation_to_scalar(sc2, &pt, tests[i].idx); + ck_assert_int_eq(eq256_modm(sc, sc2), 1); + + xmr_derivation_to_scalar(sc2, &pt, tests[i].idx + 1); + ck_assert_int_eq(eq256_modm(sc, sc2), 0); + } +} +END_TEST + + +START_TEST(test_xmr_generate_key_derivation) +{ + static const struct { + char *pt; + char *sc; + char *r; + } tests[] = { + { + "38f94f27c8037aff025e365275ed1029fd636dda5f69e5f98fdcf92e0a28f31a", + "8f1c73ee5327a43264a7b60b9e7882312b582f33e89846a8694dbf094bb3a90a", + "1fbfe4dcc8c824c274649545f297fa320cd4c1689b1d0ff4887567c4d4a75649", + }, + { + "26785c3941a32f194228eb659c5ee305e63868896defc50ee6c4e0e92d1e246a", + "dbbffec4686ba8ab25e2f1b04c0e7ae51c5143c91353bfb5998430ebe365a609", + "cca34db8dd682ec164d8973b555253934596b77849ef7709d9321121c25aba02", + }, + { + "43505a8ce7248f70d3aae4f57fb59c254ce2b2a0cc2bcf50f2344e51d59b36b3", + "19a802e35f6ff94efe96ec016effe04e635bbd9c1ce2612d5ba2ee4659456b06", + "fc6c93a93f77ff89c18b9abf95b28ec8591ab97eee8e4afee93aa766a4bd3934", + }, + }; + + ge25519 pt, pt2, pt3; + bignum256modm sc; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(sc, fromhex(tests[i].sc), 32); + ge25519_unpack_vartime(&pt, fromhex(tests[i].pt)); + ge25519_unpack_vartime(&pt2, fromhex(tests[i].r)); + xmr_generate_key_derivation(&pt3, &pt, sc); + ck_assert_int_eq(ge25519_eq(&pt3, &pt2), 1); + ck_assert_int_eq(ge25519_eq(&pt3, &pt), 0); + } +} +END_TEST + + +START_TEST(test_xmr_derive_private_key) +{ + static const struct { + char *pt; + uint32_t idx; + char *base; + char *r; + } tests[] = { + { + "0541d8f069e5e80a892e39bbf1944ef578008cf9ecf1d100760a05858c1b709e", 0, + "76967eeb0a3d181bb0b384be71c680a4287599f27b2ddbd07f8e06ab6f2c880e", + "45728c5cb658e470790f124a01699d2126832b7e5c6b7760b6f11119b96ad603", + }, + { + "fc6e0bd785a84e62c9ac8a97e0e604a79494bc2cf7b3b38ef8af7791c87b5bb8", 1, + "32fbe149562b7ccb34bc4105b87b2a834024799336c8eea5e94df77f1ae9a807", + "64508e83bbadf63f8ecfae4d9dcdd39a4ba23508a545e1a37026f0fa2539d601", + }, + { + "f6bd7a72dc9444dc7e09a0eb4d312d36fe173693d6405b132a5b090297a04ea9", 65537, + "333a8fcce6726457e4222a87b9b475c1fcf985f756c2029fcb39184c0a5c4804", + "37c16a22da4c0082ebf4bf807403b169f75142a9bd8560ed45f3f9347218260e", + }, + }; + + ge25519 pt; + bignum256modm base, res, res_exp; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(base, fromhex(tests[i].base), 32); + expand256_modm(res_exp, fromhex(tests[i].r), 32); + ge25519_unpack_vartime(&pt, fromhex(tests[i].pt)); + + xmr_derive_private_key(res, &pt, tests[i].idx, base); + ck_assert_int_eq(eq256_modm(res, res_exp), 1); + ck_assert_int_eq(eq256_modm(res, base), 0); + } +} +END_TEST + + +START_TEST(test_xmr_derive_public_key) +{ + static const struct { + char *pt; + uint32_t idx; + char *base; + char *r; + } tests[] = { + { + "653f03e7766d472826aa49793bc0cfde698e6745ae5e4217980ba307739f2ed9", 0, + "2a393f0858732970ac8dea003b17e1ce9371f0a045bd9b7af0d998262739f4cc", + "f7a3db27c45f265f6a68a30137ca44289a6cf1a6db2cf482c59ebfb0142ad419", + }, + { + "338e93f61e6470a5cc71c07b8caedd1a9a28da037aab65c1ca5538501b012c81", 1, + "af3a1d39397d778731c4510110fd117dc02f756e390713d58f94a06203ce39eb", + "779e2a043c881f06aba1952741fd753098615c4fafa8f62748467ab9bac43241", + }, + { + "7735e9476440927b89b18d7a1e0645b218a1a6d28c642aebb16c1dba0926d5e4", 65537, + "62c3eed062bd602f7f2164c69ad0b5a8eb3ea560c930f6b41abfc1c4839ea432", + "6da4ebd29498d16c4e813abb3e328c83f9b01a7ba1da6e818071f8ec563626c8", + }, + }; + + ge25519 pt, base, res, res_exp; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + ge25519_unpack_vartime(&pt, fromhex(tests[i].pt)); + ge25519_unpack_vartime(&base, fromhex(tests[i].base)); + ge25519_unpack_vartime(&res_exp, fromhex(tests[i].r)); + + xmr_derive_public_key(&res, &pt, tests[i].idx, &base); + + ck_assert_int_eq(ge25519_eq(&res, &res_exp), 1); + ck_assert_int_eq(ge25519_eq(&res, &base), 0); + } +} +END_TEST + + +START_TEST(test_xmr_add_keys2) +{ + static const struct { + char *a; + char *b; + char *B; + char *r; + } tests[] = { + { + "631238da9578d7cb8db16fc4322671bfcb251cc5228b060664800ec1895be608", + "f9a73fca0be058415a148f9e2871be59e1fc7ae6f6193199125237e0d7c1630f", + "ef5ca4fc90f330e825adcdc953da0b3becd853aa819219842790bb39775f2255", + "06623fd0e7a3d787a4d224f6ca2fdab2dcd9d1221578515974b9c4dee65fdcf5", + }, + { + "dac2e629e5c75c312253b19d1d3a0a423158fdd9cdcf4c7a7bf2717d0b748602", + "0483d98d750d4977b499cefd558a0a61580823a37da2b011501e24718e6c7f0a", + "51fd3cd2f1a603ec7be3b35da9c105d91c4304e6a63facf48d7730712cedc0ee", + "f7a5d645ba01a5b7ccbe9636d14422bb587fc529317b23761f0e39222b783b87", + }, + { + "817c4d2fd3e841d860bdab6b7ccf098f3e637eca468d0a3825c50b71f61d0e0c", + "1f6c4795d7fb0d53b5775874ac4c0963607d2b7bd11a7c5d10735badc4a27207", + "bef0e0ed09d602bbe1dd38358b5f8fca27fcad60a69440f104441c3fc68df9c7", + "bc0fc824d74eca0e10eacd0bc2f3322e0bcb02a44ce53f2f5f1fc472f99be8d2", + }, + }; + + bignum256modm a, b; + ge25519 B, res, res_exp; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i].a), 32); + expand256_modm(b, fromhex(tests[i].b), 32); + ge25519_unpack_vartime(&B, fromhex(tests[i].B)); + ge25519_unpack_vartime(&res_exp, fromhex(tests[i].r)); + + xmr_add_keys2(&res, a, b, &B); + ck_assert_int_eq(ge25519_eq(&res, &res_exp), 1); + ck_assert_int_eq(ge25519_eq(&res, &B), 0); + + xmr_add_keys2_vartime(&res, a, b, &B); + ck_assert_int_eq(ge25519_eq(&res, &res_exp), 1); + ck_assert_int_eq(ge25519_eq(&res, &B), 0); + } +} +END_TEST + + +START_TEST(test_xmr_add_keys3) +{ + static const struct { + char *a; + char *A; + char *b; + char *B; + char *r; + } tests[] = { + { + "7048b8c4603ae194c502fa458b0e11a4c7a330852bbef66b7c1d67e9f919f509", + "9167c5b182758699baeb421e7f1200272fc775e4c7c7c183cc47261dccbb569f", + "c2cb2bc0249fc7be8eb9b3bed7d37aa6f2c3f433abb3a4a00b13bed64b61f30b", + "b3ec53b07a1be70ac8d0fa365b86f0d6d4cbf98641e7704b3d684558e2ea59ef", + "4dc016d702d599bde5eaeb2bf0c2d0d3f6b9cede961bc539bcb369c3b3086358", + }, + { + "e9794a6652940474958936f07f3904d514228553247633cfb7ae8ffa9fa0f406", + "0e51cea6df2f6f56a9935689364f0d295a7c89f51d40efb2518c17d1b9db792b", + "c132e7be08afdd93984c52c6e1c596edc6b8fc8f1faed95f55e2f819ee806706", + "1a0e03c6858f6cf1b43f4b8456c03144af553bbbd050e152834fd1615b577cb3", + "088f19c6727f8704373d391a36c230395d386f69edb4151ecf8afcd27793fff5", + }, + { + "88920b0c96b15cc04e879f53a76f85f3c7a2a5f275b2772b5b74ee83372aea00", + "e95731ab61a98fedcded475cf21b4ecf2ef9f1adecefba8fdc476a5bb1cf60f9", + "c86026b66c1045fb69e4f24ff6c15d4fad4d565e646938a2ffb7db37ccb4100d", + "d80cbf2986c12e4c7ebac1e55abbdfc4212c00aec8bc90c965becf863262a074", + "047cebaeb3ec2132e7386ba52531b04070206ba1106565c0fbd7d7280694568a", + }, + }; + + bignum256modm a, b; + ge25519 A, B, res, res_exp; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i].a), 32); + expand256_modm(b, fromhex(tests[i].b), 32); + ge25519_unpack_vartime(&A, fromhex(tests[i].A)); + ge25519_unpack_vartime(&B, fromhex(tests[i].B)); + ge25519_unpack_vartime(&res_exp, fromhex(tests[i].r)); + + xmr_add_keys3(&res, a, &A, b, &B); + ck_assert_int_eq(ge25519_eq(&res, &res_exp), 1); + ck_assert_int_eq(ge25519_eq(&res, &B), 0); + + xmr_add_keys3_vartime(&res, a, &A, b, &B); + ck_assert_int_eq(ge25519_eq(&res, &res_exp), 1); + ck_assert_int_eq(ge25519_eq(&res, &B), 0); + } +} +END_TEST + + +START_TEST(test_xmr_get_subaddress_secret_key) +{ + static const struct { + uint32_t major, minor; + char *m; + char *r; + } tests[] = { + { + 0, 0, + "36fad9f7bff465c15a755f1482fb2ecc3a4e434303df906882234e42b5813207", + "8a510a9fe1824b49abbae05958084f9c9098775f29e15427309177882471cf01", + }, + { + 0, 1, + "36fad9f7bff465c15a755f1482fb2ecc3a4e434303df906882234e42b5813207", + "2bbc9366c04abb0523e2b2d6e709670ffe6645bacedfee968d9c6bc8eefe9c0f", + }, + { + 100, 100, + "36fad9f7bff465c15a755f1482fb2ecc3a4e434303df906882234e42b5813207", + "c3837d41fedeaed126cf4fc1a5ea47b8b7f38f6a64aa534e3dd45a3c93f37600", + }, + }; + + bignum256modm m, res, res_exp; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(m, fromhex(tests[i].m), 32); + expand256_modm(res_exp, fromhex(tests[i].r), 32); + xmr_get_subaddress_secret_key(res, tests[i].major, tests[i].minor, m); + + ck_assert_int_eq(eq256_modm(res, res_exp), 1); + ck_assert_int_eq(eq256_modm(res, m), 0); + } +} +END_TEST + + +START_TEST(test_xmr_gen_c) +{ + static const struct { + char *a; + uint64_t amount; + char *r; + } tests[] = { + { + "e3e6558c291bbb98aa691d068b67d59dc520afb23fdd51bf65283626fc2ad903", 0, + "ef19d73bdf3749240b80ee7695f53ad7c2fc2cf868a93209799f41212d099750", + }, + { + "6788c9579c377f3228680bd0e6d01b1ee0c763b35ed39d36fa2146cc2ee16e0e", 1, + "4913b9af4f2725d87a4404c22cf366597d1c1e6a1f510ae14081d8b7c5a9de77", + }, + { + "ad9e89d67012935540427c241756d6a9d260c5e134603c41d31e24f8651bef08", 65537, + "f005721da08f24e68314abed3ddfd94165e4be3813398fb126e3f366820b9c90", + }, + { + "fdbb70ff07be24d98de3bffa0a33756646497224318fb7fe136f0e7789d12607", 0xffffffffffffffffULL, + "a9c38927f299c5f14c98a1a9c9981e59c606ff597274b9b709e1356f12e1498c", + }, + }; + + bignum256modm a; + ge25519 res, res_exp; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i].a), 32); + ge25519_unpack_vartime(&res_exp, fromhex(tests[i].r)); + xmr_gen_c(&res, a, tests[i].amount); + + ck_assert_int_eq(ge25519_eq(&res, &res_exp), 1); + } +} +END_TEST + + +START_TEST(test_xmr_varint) +{ + static const struct { + uint64_t x; + char *r; + } tests[] = { + { + 0, + "00", + }, + { + 24, + "18", + }, + { + 65535, + "ffff03", + }, + { + 65537, + "818004", + }, + { + 0x7fffffffULL, + "ffffffff07", + }, + { + 0xffffffffULL, + "ffffffff0f", + }, + { + 0xffffffffffffffffULL, + "ffffffffffffffffff01", + }, + { + 0xdeadc0deULL, + "de81b7f50d", + }, + }; + + uint64_t val; + unsigned char buff[64]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + int s1 = xmr_size_varint(tests[i].x); + int written = 0; + int read = 0; + + ck_assert_int_eq(s1, strlen(tests[i].r)/2); + written = xmr_write_varint(buff, sizeof(buff), tests[i].x); + ck_assert_int_eq(s1, written); + ck_assert_mem_eq(buff, fromhex(tests[i].r), strlen(tests[i].r)/2); + + read = xmr_read_varint(buff, sizeof(buff), &val); + ck_assert_int_eq(read, written); + ck_assert(tests[i].x == val); + } +} +END_TEST + + +START_TEST(test_xmr_gen_range_sig) +{ + uint64_t tests[] = { + 0, 1, 65535, 65537, 0xffffffffffffffffULL, 0xdeadc0deULL, + }; + + unsigned char buff[32]; + xmr_range_sig_t sig; + ge25519 C, Ctmp, Cb, Ch, P1, P2, LL; + bignum256modm mask, hsh, ee, s, ee_comp; + Hasher hasher; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + xmr_gen_range_sig(&sig, &C, mask, tests[i], NULL); + + ge25519_set_neutral(&Ctmp); + for(int j = 0; j < XMR_ATOMS; j++){ + ge25519_unpack_vartime(&Cb, sig.Ci[j]); + ge25519_add(&Ctmp, &Ctmp, &Cb, 0); + } + + ck_assert_int_eq(ge25519_eq(&C, &Ctmp), 1); + + xmr_hasher_init(&hasher); + ge25519_set_xmr_h(&Ch); + expand256_modm(ee, sig.asig.ee, 32); + + for(int j = 0; j < XMR_ATOMS; j++){ + ge25519_unpack_vartime(&P1, sig.Ci[j]); + ge25519_add(&P2, &P1, &Ch, 1); + expand256_modm(s, sig.asig.s0[j], 32); + + xmr_add_keys2(&LL, s, ee, &P1); + ge25519_pack(buff, &LL); + xmr_hash_to_scalar(hsh, buff, 32); + + expand256_modm(s, sig.asig.s1[j], 32); + xmr_add_keys2(&LL, s, hsh, &P2); + + ge25519_pack(buff, &LL); + xmr_hasher_update(&hasher, buff, 32); + + ge25519_double(&Ch, &Ch); + } + + xmr_hasher_final(&hasher, buff); + expand256_modm(ee_comp, buff, 32); + ck_assert_int_eq(eq256_modm(ee, ee_comp), 1); + } +} +END_TEST +#endif