/** Verifies that a commitment c is accumulated in accumulator a */ bool AccumulatorProofOfKnowledge:: Verify(const Accumulator& a, const CBigNum& valueOfCommitmentToCoin) const { CBigNum sg = params->accumulatorPoKCommitmentGroup.g; CBigNum sh = params->accumulatorPoKCommitmentGroup.h; CBigNum g_n = params->accumulatorQRNCommitmentGroup.g; CBigNum h_n = params->accumulatorQRNCommitmentGroup.h; //According to the proof, this hash should be of length k_prime bits. It is currently greater than that, which should not be a problem, but we should check this. CHashWriter hasher(0,0); hasher << *params << sg << sh << g_n << h_n << valueOfCommitmentToCoin << C_e << C_u << C_r << st_1 << st_2 << st_3 << t_1 << t_2 << t_3 << t_4; CBigNum c = CBigNum(hasher.GetHash()); //this hash should be of length k_prime bits CBigNum st_1_prime = (valueOfCommitmentToCoin.pow_mod(c, params->accumulatorPoKCommitmentGroup.modulus) * sg.pow_mod(s_alpha, params->accumulatorPoKCommitmentGroup.modulus) * sh.pow_mod(s_phi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus; CBigNum st_2_prime = (sg.pow_mod(c, params->accumulatorPoKCommitmentGroup.modulus) * ((valueOfCommitmentToCoin * sg.inverse(params->accumulatorPoKCommitmentGroup.modulus)).pow_mod(s_gamma, params->accumulatorPoKCommitmentGroup.modulus)) * sh.pow_mod(s_psi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus; CBigNum st_3_prime = (sg.pow_mod(c, params->accumulatorPoKCommitmentGroup.modulus) * (sg * valueOfCommitmentToCoin).pow_mod(s_sigma, params->accumulatorPoKCommitmentGroup.modulus) * sh.pow_mod(s_xi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus; CBigNum t_1_prime = (C_r.pow_mod(c, params->accumulatorModulus) * h_n.pow_mod(s_zeta, params->accumulatorModulus) * g_n.pow_mod(s_epsilon, params->accumulatorModulus)) % params->accumulatorModulus; CBigNum t_2_prime = (C_e.pow_mod(c, params->accumulatorModulus) * h_n.pow_mod(s_eta, params->accumulatorModulus) * g_n.pow_mod(s_alpha, params->accumulatorModulus)) % params->accumulatorModulus; CBigNum t_3_prime = ((a.getValue()).pow_mod(c, params->accumulatorModulus) * C_u.pow_mod(s_alpha, params->accumulatorModulus) * ((h_n.inverse(params->accumulatorModulus)).pow_mod(s_beta, params->accumulatorModulus))) % params->accumulatorModulus; CBigNum t_4_prime = (C_r.pow_mod(s_alpha, params->accumulatorModulus) * ((h_n.inverse(params->accumulatorModulus)).pow_mod(s_delta, params->accumulatorModulus)) * ((g_n.inverse(params->accumulatorModulus)).pow_mod(s_beta, params->accumulatorModulus))) % params->accumulatorModulus; bool result_st1 = (st_1 == st_1_prime); bool result_st2 = (st_2 == st_2_prime); bool result_st3 = (st_3 == st_3_prime); bool result_t1 = (t_1 == t_1_prime); bool result_t2 = (t_2 == t_2_prime); bool result_t3 = (t_3 == t_3_prime); bool result_t4 = (t_4 == t_4_prime); bool result_range = ((s_alpha >= -(params->maxCoinValue * CBigNum(2).pow(params->k_prime + params->k_dprime + 1))) && (s_alpha <= (params->maxCoinValue * CBigNum(2).pow(params->k_prime + params->k_dprime + 1)))); return result_st1 && result_st2 && result_st3 && result_t1 && result_t2 && result_t3 && result_t4 && result_range; }
bool SerialNumberSignatureOfKnowledge::Verify(const CBigNum& coinSerialNumber, const CBigNum& valueOfCommitmentToCoin, const uint256 msghash) const { CBigNum a = params->coinCommitmentGroup.g; CBigNum b = params->coinCommitmentGroup.h; CBigNum g = params->serialNumberSoKCommitmentGroup.g; CBigNum h = params->serialNumberSoKCommitmentGroup.h; CHashWriter hasher(0,0); hasher << *params << valueOfCommitmentToCoin << coinSerialNumber << msghash; vector<CBigNum> tprime(params->zkp_iterations); unsigned char *hashbytes = (unsigned char*) &this->hash; for(uint32_t i = 0; i < params->zkp_iterations; i++) { int bit = i % 8; int byte = i / 8; bool challenge_bit = ((hashbytes[byte] >> bit) & 0x01); if(challenge_bit) { tprime[i] = challengeCalculation(coinSerialNumber, s_notprime[i], SeedTo1024(sprime[i].getuint256())); } else { CBigNum exp = b.pow_mod(s_notprime[i], params->serialNumberSoKCommitmentGroup.groupOrder); tprime[i] = ((valueOfCommitmentToCoin.pow_mod(exp, params->serialNumberSoKCommitmentGroup.modulus) % params->serialNumberSoKCommitmentGroup.modulus) * (h.pow_mod(sprime[i], params->serialNumberSoKCommitmentGroup.modulus) % params->serialNumberSoKCommitmentGroup.modulus)) % params->serialNumberSoKCommitmentGroup.modulus; } } for(uint32_t i = 0; i < params->zkp_iterations; i++) { hasher << tprime[i]; } return hasher.GetHash() == hash; }
inline CBigNum SerialNumberSignatureOfKnowledge::challengeCalculation(const CBigNum& a_exp,const CBigNum& b_exp, const CBigNum& h_exp) const { CBigNum a = params->coinCommitmentGroup.g; CBigNum b = params->coinCommitmentGroup.h; CBigNum g = params->serialNumberSoKCommitmentGroup.g; CBigNum h = params->serialNumberSoKCommitmentGroup.h; CBigNum exponent = (a.pow_mod(a_exp, params->serialNumberSoKCommitmentGroup.groupOrder) * b.pow_mod(b_exp, params->serialNumberSoKCommitmentGroup.groupOrder)) % params->serialNumberSoKCommitmentGroup.groupOrder; return (g.pow_mod(exponent, params->serialNumberSoKCommitmentGroup.modulus) * h.pow_mod(h_exp, params->serialNumberSoKCommitmentGroup.modulus)) % params->serialNumberSoKCommitmentGroup.modulus; }
bool CommitmentProofOfKnowledge::Verify(const CBigNum& A, const CBigNum& B) const { // Compute the maximum range of S1, S2, S3 and verify that the given values are // in a correct range. This might be an unnecessary check. uint32_t maxSize = 64 * (COMMITMENT_EQUALITY_CHALLENGE_SIZE + COMMITMENT_EQUALITY_SECMARGIN + std::max(std::max(this->ap->modulus.bitSize(), this->bp->modulus.bitSize()), std::max(this->ap->groupOrder.bitSize(), this->bp->groupOrder.bitSize()))); if ((uint32_t)this->S1.bitSize() > maxSize || (uint32_t)this->S2.bitSize() > maxSize || (uint32_t)this->S3.bitSize() > maxSize || this->S1 < CBigNum(0) || this->S2 < CBigNum(0) || this->S3 < CBigNum(0) || this->challenge < CBigNum(0) || this->challenge > (CBigNum(2).pow(COMMITMENT_EQUALITY_CHALLENGE_SIZE) - CBigNum(1))) { // Invalid inputs. Reject. return false; } // Compute T1 = g1^S1 * h1^S2 * inverse(A^{challenge}) mod p1 CBigNum T1 = A.pow_mod(this->challenge, ap->modulus).inverse(ap->modulus).mul_mod( (ap->g.pow_mod(S1, ap->modulus).mul_mod(ap->h.pow_mod(S2, ap->modulus), ap->modulus)), ap->modulus); // Compute T2 = g2^S1 * h2^S3 * inverse(B^{challenge}) mod p2 CBigNum T2 = B.pow_mod(this->challenge, bp->modulus).inverse(bp->modulus).mul_mod( (bp->g.pow_mod(S1, bp->modulus).mul_mod(bp->h.pow_mod(S3, bp->modulus), bp->modulus)), bp->modulus); // Hash T1 and T2 along with all of the public parameters CBigNum computedChallenge = calculateChallenge(A, B, T1, T2); // Return success if the computed challenge matches the incoming challenge return computedChallenge == this->challenge; }
AccumulatorProofOfKnowledge::AccumulatorProofOfKnowledge(const AccumulatorAndProofParams* p, const Commitment& commitmentToCoin, const AccumulatorWitness& witness): params(p) { CBigNum sg = params->accumulatorPoKCommitmentGroup.g; CBigNum sh = params->accumulatorPoKCommitmentGroup.h; CBigNum g_n = params->accumulatorQRNCommitmentGroup.g; CBigNum h_n = params->accumulatorQRNCommitmentGroup.h; CBigNum e = commitmentToCoin.getContents(); CBigNum r = commitmentToCoin.getRandomness(); CBigNum aM_4 = params->accumulatorModulus/CBigNum((long)4); CBigNum aR = CBigNum(2).pow(params->k_prime + params->k_dprime); CBigNum aR_t_aM_4 = aM_4 * aR; CBigNum r_1 = CBigNum::randBignum(aM_4); CBigNum r_2 = CBigNum::randBignum(aM_4); CBigNum r_3 = CBigNum::randBignum(aM_4); this->C_e = g_n.pow_mod(e, params->accumulatorModulus) * h_n.pow_mod(r_1, params->accumulatorModulus); this->C_u = witness.getValue() * h_n.pow_mod(r_2, params->accumulatorModulus); this->C_r = g_n.pow_mod(r_2, params->accumulatorModulus) * h_n.pow_mod(r_3, params->accumulatorModulus); CBigNum r_alpha = CBigNum::randBignum(params->maxCoinValue * aR); if(!(CBigNum::randBignum(CBigNum(3)) % 2)) { r_alpha = 0-r_alpha; } CBigNum r_gamma = CBigNum::randBignum(params->accumulatorPoKCommitmentGroup.modulus); CBigNum r_phi = CBigNum::randBignum(params->accumulatorPoKCommitmentGroup.modulus); CBigNum r_psi = CBigNum::randBignum(params->accumulatorPoKCommitmentGroup.modulus); CBigNum r_sigma = CBigNum::randBignum(params->accumulatorPoKCommitmentGroup.modulus); CBigNum r_xi = CBigNum::randBignum(params->accumulatorPoKCommitmentGroup.modulus); CBigNum r_epsilon = CBigNum::randBignum(aR_t_aM_4); if(!(CBigNum::randBignum(CBigNum(3)) % 2)) { r_epsilon = 0-r_epsilon; } CBigNum r_eta = CBigNum::randBignum(aR_t_aM_4); if(!(CBigNum::randBignum(CBigNum(3)) % 2)) { r_eta = 0-r_eta; } CBigNum r_zeta = CBigNum::randBignum(aR_t_aM_4); if(!(CBigNum::randBignum(CBigNum(3)) % 2)) { r_zeta = 0-r_zeta; } CBigNum r_beta = CBigNum::randBignum(aR_t_aM_4 * params->accumulatorPoKCommitmentGroup.modulus); if(!(CBigNum::randBignum(CBigNum(3)) % 2)) { r_beta = 0-r_beta; } CBigNum r_delta = CBigNum::randBignum(aR_t_aM_4 * params->accumulatorPoKCommitmentGroup.modulus); if(!(CBigNum::randBignum(CBigNum(3)) % 2)) { r_delta = 0-r_delta; } this->st_1 = (sg.pow_mod(r_alpha, params->accumulatorPoKCommitmentGroup.modulus) * sh.pow_mod(r_phi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus; this->st_2 = (((commitmentToCoin.getCommitmentValue() * sg.inverse(params->accumulatorPoKCommitmentGroup.modulus)).pow_mod(r_gamma, params->accumulatorPoKCommitmentGroup.modulus)) * sh.pow_mod(r_psi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus; this->st_3 = ((sg * commitmentToCoin.getCommitmentValue()).pow_mod(r_sigma, params->accumulatorPoKCommitmentGroup.modulus) * sh.pow_mod(r_xi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus; this->t_1 = (h_n.pow_mod(r_zeta, params->accumulatorModulus) * g_n.pow_mod(r_epsilon, params->accumulatorModulus)) % params->accumulatorModulus; this->t_2 = (h_n.pow_mod(r_eta, params->accumulatorModulus) * g_n.pow_mod(r_alpha, params->accumulatorModulus)) % params->accumulatorModulus; this->t_3 = (C_u.pow_mod(r_alpha, params->accumulatorModulus) * ((h_n.inverse(params->accumulatorModulus)).pow_mod(r_beta, params->accumulatorModulus))) % params->accumulatorModulus; this->t_4 = (C_r.pow_mod(r_alpha, params->accumulatorModulus) * ((h_n.inverse(params->accumulatorModulus)).pow_mod(r_delta, params->accumulatorModulus)) * ((g_n.inverse(params->accumulatorModulus)).pow_mod(r_beta, params->accumulatorModulus))) % params->accumulatorModulus; CHashWriter hasher(0,0); hasher << *params << sg << sh << g_n << h_n << commitmentToCoin.getCommitmentValue() << C_e << C_u << C_r << st_1 << st_2 << st_3 << t_1 << t_2 << t_3 << t_4; //According to the proof, this hash should be of length k_prime bits. It is currently greater than that, which should not be a problem, but we should check this. CBigNum c = CBigNum(hasher.GetHash()); this->s_alpha = r_alpha - c*e; this->s_beta = r_beta - c*r_2*e; this->s_zeta = r_zeta - c*r_3; this->s_sigma = r_sigma - c*((e+1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder)); this->s_eta = r_eta - c*r_1; this->s_epsilon = r_epsilon - c*r_2; this->s_delta = r_delta - c*r_3*e; this->s_xi = r_xi + c*r*((e+1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder)); this->s_phi = (r_phi - c*r) % params->accumulatorPoKCommitmentGroup.groupOrder; this->s_gamma = r_gamma - c*((e-1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder)); this->s_psi = r_psi + c*r*((e-1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder)); }
SerialNumberSignatureOfKnowledge::SerialNumberSignatureOfKnowledge(const ZerocoinParams* p, const PrivateCoin& coin, const Commitment& commitmentToCoin, uint256 msghash):params(p), s_notprime(p->zkp_iterations), sprime(p->zkp_iterations) { // Sanity check: verify that the order of the "accumulatedValueCommitmentGroup" is // equal to the modulus of "coinCommitmentGroup". Otherwise we will produce invalid // proofs. if (params->coinCommitmentGroup.modulus != params->serialNumberSoKCommitmentGroup.groupOrder) { throw std::runtime_error("Groups are not structured correctly."); } CBigNum a = params->coinCommitmentGroup.g; CBigNum b = params->coinCommitmentGroup.h; CBigNum g = params->serialNumberSoKCommitmentGroup.g; CBigNum h = params->serialNumberSoKCommitmentGroup.h; CHashWriter hasher(0,0); hasher << *params << commitmentToCoin.getCommitmentValue() << coin.getSerialNumber() << msghash; vector<CBigNum> r(params->zkp_iterations); vector<CBigNum> v_seed(params->zkp_iterations); vector<CBigNum> v_expanded(params->zkp_iterations); vector<CBigNum> c(params->zkp_iterations); for(uint32_t i=0; i < params->zkp_iterations; i++) { r[i] = CBigNum::randBignum(params->coinCommitmentGroup.groupOrder); //use a random 256 bit seed that expands to 1024 bit for v[i] while (true) { uint256 hashRand = CBigNum::randBignum(CBigNum(~uint256(0))).getuint256(); CBigNum bnExpanded = SeedTo1024(hashRand); if(bnExpanded > params->serialNumberSoKCommitmentGroup.groupOrder) continue; v_seed[i] = CBigNum(hashRand); v_expanded[i] = bnExpanded; break; } } for(uint32_t i=0; i < params->zkp_iterations; i++) { // compute g^{ {a^x b^r} h^v} mod p2 c[i] = challengeCalculation(coin.getSerialNumber(), r[i], v_expanded[i]); } // We can't hash data in parallel either // because OPENMP cannot not guarantee loops // execute in order. for(uint32_t i=0; i < params->zkp_iterations; i++) { hasher << c[i]; } this->hash = hasher.GetHash(); unsigned char *hashbytes = (unsigned char*) &hash; for(uint32_t i = 0; i < params->zkp_iterations; i++) { int bit = i % 8; int byte = i / 8; bool challenge_bit = ((hashbytes[byte] >> bit) & 0x01); if (challenge_bit) { s_notprime[i] = r[i]; sprime[i] = v_seed[i]; } else { s_notprime[i] = r[i] - coin.getRandomness(); sprime[i] = v_expanded[i] - (commitmentToCoin.getRandomness() * b.pow_mod(r[i] - coin.getRandomness(), params->serialNumberSoKCommitmentGroup.groupOrder)); } } }