/********************************************************************** * Copyright (c) 2014, 2015 Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ #ifndef _SECP256K1_BORROMEAN_IMPL_H_ #define _SECP256K1_BORROMEAN_IMPL_H_ #include "scalar.h" #include "field.h" #include "group.h" #include "ecmult.h" #include "ecmult_gen.h" #include "borromean.h" #include #ifdef WORDS_BIGENDIAN #define BE32(x) (x) #else #define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #endif SECP256K1_INLINE static void secp256k1_borromean_hash(unsigned char *hash, const unsigned char *m, int mlen, const unsigned char *e, int elen, int ridx, int eidx) { uint32_t ring; uint32_t epos; secp256k1_sha256_t sha256_en; secp256k1_sha256_initialize(&sha256_en); ring = BE32((uint32_t)ridx); epos = BE32((uint32_t)eidx); secp256k1_sha256_write(&sha256_en, e, elen); secp256k1_sha256_write(&sha256_en, m, mlen); secp256k1_sha256_write(&sha256_en, (unsigned char*)&ring, 4); secp256k1_sha256_write(&sha256_en, (unsigned char*)&epos, 4); secp256k1_sha256_finalize(&sha256_en, hash); } /** "Borromean" ring signature. * Verifies nrings concurrent ring signatures all sharing a challenge value. * Signature is one s value per pubkey and a hash. * Verification equation: * | m = H(P_{0..}||message) (Message must contain pubkeys or a pubkey commitment) * | For each ring i: * | | en = to_scalar(H(e0||m||i||0)) * | | For each pubkey j: * | | | r = s_i_j G + en * P_i_j * | | | e = H(r||m||i||j) * | | | en = to_scalar(e) * | | r_i = r * | return e_0 ==== H(r_{0..i}||m) */ int secp256k1_borromean_verify(const secp256k1_ecmult_context_t* ecmult_ctx, secp256k1_scalar_t *evalues, const unsigned char *e0, const secp256k1_scalar_t *s, const secp256k1_gej_t *pubs, const int *rsizes, int nrings, const unsigned char *m, int mlen) { secp256k1_gej_t rgej; secp256k1_ge_t rge; secp256k1_scalar_t ens; secp256k1_sha256_t sha256_e0; unsigned char tmp[33]; int i; int j; int count; int size; int overflow; VERIFY_CHECK(ecmult_ctx != NULL); VERIFY_CHECK(e0 != NULL); VERIFY_CHECK(s != NULL); VERIFY_CHECK(pubs != NULL); VERIFY_CHECK(rsizes != NULL); VERIFY_CHECK(nrings > 0); VERIFY_CHECK(m != NULL); count = 0; secp256k1_sha256_initialize(&sha256_e0); for (i = 0; i < nrings; i++) { VERIFY_CHECK(INT_MAX - count > rsizes[i]); secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); secp256k1_scalar_set_b32(&ens, tmp, &overflow); for (j = 0; j < rsizes[i]; j++) { if (overflow || secp256k1_scalar_is_zero(&s[count]) || secp256k1_scalar_is_zero(&ens) || secp256k1_gej_is_infinity(&pubs[count])) { return 0; } if (evalues) { /*If requested, save the challenges for proof rewind.*/ evalues[count] = ens; } secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count], &ens, &s[count]); if (secp256k1_gej_is_infinity(&rgej)) { return 0; } /* OPT: loop can be hoisted and split to use batch inversion across all the rings; this would make it much faster. */ secp256k1_ge_set_gej_var(&rge, &rgej); secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); if (j != rsizes[i] - 1) { secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); secp256k1_scalar_set_b32(&ens, tmp, &overflow); } else { secp256k1_sha256_write(&sha256_e0, tmp, size); } count++; } } secp256k1_sha256_write(&sha256_e0, m, mlen); secp256k1_sha256_finalize(&sha256_e0, tmp); return memcmp(e0, tmp, 32) == 0; } int secp256k1_borromean_sign(const secp256k1_ecmult_context_t* ecmult_ctx, const secp256k1_ecmult_gen_context_t *ecmult_gen_ctx, unsigned char *e0, secp256k1_scalar_t *s, const secp256k1_gej_t *pubs, const secp256k1_scalar_t *k, const secp256k1_scalar_t *sec, const int *rsizes, const int *secidx, int nrings, const unsigned char *m, int mlen) { secp256k1_gej_t rgej; secp256k1_ge_t rge; secp256k1_scalar_t ens; secp256k1_sha256_t sha256_e0; unsigned char tmp[33]; int i; int j; int count; int size; int overflow; VERIFY_CHECK(ecmult_ctx != NULL); VERIFY_CHECK(ecmult_gen_ctx != NULL); VERIFY_CHECK(e0 != NULL); VERIFY_CHECK(s != NULL); VERIFY_CHECK(pubs != NULL); VERIFY_CHECK(k != NULL); VERIFY_CHECK(sec != NULL); VERIFY_CHECK(rsizes != NULL); VERIFY_CHECK(secidx != NULL); VERIFY_CHECK(nrings > 0); VERIFY_CHECK(m != NULL); secp256k1_sha256_initialize(&sha256_e0); count = 0; for (i = 0; i < nrings; i++) { VERIFY_CHECK(INT_MAX - count > rsizes[i]); secp256k1_ecmult_gen(ecmult_gen_ctx, &rgej, &k[i]); secp256k1_ge_set_gej(&rge, &rgej); if (secp256k1_gej_is_infinity(&rgej)) { return 0; } secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); for (j = secidx[i] + 1; j < rsizes[i]; j++) { secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j); secp256k1_scalar_set_b32(&ens, tmp, &overflow); if (overflow || secp256k1_scalar_is_zero(&ens)) { return 0; } /** The signing algorithm as a whole is not memory uniform so there is likely a cache sidechannel that * leaks which members are non-forgeries. That the forgeries themselves are variable time may leave * an additional privacy impacting timing side-channel, but not a key loss one. */ secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count + j], &ens, &s[count + j]); if (secp256k1_gej_is_infinity(&rgej)) { return 0; } secp256k1_ge_set_gej_var(&rge, &rgej); secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); } secp256k1_sha256_write(&sha256_e0, tmp, size); count += rsizes[i]; } secp256k1_sha256_write(&sha256_e0, m, mlen); secp256k1_sha256_finalize(&sha256_e0, e0); count = 0; for (i = 0; i < nrings; i++) { VERIFY_CHECK(INT_MAX - count > rsizes[i]); secp256k1_borromean_hash(tmp, m, mlen, e0, 32, i, 0); secp256k1_scalar_set_b32(&ens, tmp, &overflow); if (overflow || secp256k1_scalar_is_zero(&ens)) { return 0; } for (j = 0; j < secidx[i]; j++) { secp256k1_ecmult(ecmult_ctx, &rgej, &pubs[count + j], &ens, &s[count + j]); if (secp256k1_gej_is_infinity(&rgej)) { return 0; } secp256k1_ge_set_gej_var(&rge, &rgej); secp256k1_eckey_pubkey_serialize(&rge, tmp, &size, 1); secp256k1_borromean_hash(tmp, m, mlen, tmp, 33, i, j + 1); secp256k1_scalar_set_b32(&ens, tmp, &overflow); if (overflow || secp256k1_scalar_is_zero(&ens)) { return 0; } } secp256k1_scalar_mul(&s[count + j], &ens, &sec[i]); secp256k1_scalar_negate(&s[count + j], &s[count + j]); secp256k1_scalar_add(&s[count + j], &s[count + j], &k[i]); if (secp256k1_scalar_is_zero(&s[count + j])) { return 0; } count += rsizes[i]; } secp256k1_scalar_clear(&ens); secp256k1_ge_clear(&rge); secp256k1_gej_clear(&rgej); memset(tmp, 0, 33); return 1; } #endif