Example #1
0
CoinSpend::CoinSpend(const Params* p, const PrivateCoin& coin,
                     Accumulator& a, const AccumulatorWitness& witness, const SpendMetaData& m):
                             denomination(coin.getPublicCoin().getDenomination()),
                             coinSerialNumber((coin.getSerialNumber())),
                             accumulatorPoK(&p->accumulatorParams),
                             serialNumberSoK(p),
                             commitmentPoK(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup),
                             metadata(m) {
    // 1: Generate two separate commitments to the public coin (C), each under
    // a different set of public parameters. We do this because the RSA accumulator
    // has specific requirements for the commitment parameters that are not
    // compatible with the group we use for the serial number proof.
    // Specifically, are serial number proof requires the order of the commitment group
    // to be the same as the modulus of the upper group.
    const Commitment fullCommitmentToCoinUnderSerialParams(&p->serialNumberSoKCommitmentGroup, coin.getPublicCoin().getValue());
    this->serialCommitmentToCoinValue = fullCommitmentToCoinUnderSerialParams.getCommitmentValue();

    const Commitment fullCommitmentToCoinUnderAccParams(&p->accumulatorParams.accumulatorPoKCommitmentGroup, coin.getPublicCoin().getValue());
    this->accCommitmentToCoinValue = fullCommitmentToCoinUnderAccParams.getCommitmentValue();
    
    // 2. Generate a ZK proof that the two commitments contain the same public coin.
    this->commitmentPoK = CommitmentProofOfKnowledge(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup, fullCommitmentToCoinUnderSerialParams, fullCommitmentToCoinUnderAccParams);
    
    // Now generate the two core ZK proofs:
    // 3. Proves that the committed public coin is in the Accumulator (PoK of "witness")
    this->accumulatorPoK = AccumulatorProofOfKnowledge(&p->accumulatorParams, fullCommitmentToCoinUnderAccParams, witness, a);
    
    // 4. Proves that the coin is correct w.r.t. serial number and hidden coin secret
    // (This proof is bound to the coin 'metadata', i.e., transaction hash)
    this->serialNumberSoK = SerialNumberSignatureOfKnowledge(p, coin, fullCommitmentToCoinUnderSerialParams, signatureHash());
}
Example #2
0
CoinSpend::CoinSpend(const Params* p, const PrivateCoin& coin,
                     Accumulator& a, const AccumulatorWitness& witness, const SpendMetaData& m):
	params(p),
	denomination(coin.getPublicCoin().getDenomination()),
	coinSerialNumber((coin.getSerialNumber())),
	accumulatorPoK(&p->accumulatorParams),
	serialNumberSoK(p),
	commitmentPoK(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup) {

	// Sanity check: let's verify that the Witness is valid with respect to
	// the coin and Accumulator provided.
	if (!(witness.VerifyWitness(a, coin.getPublicCoin()))) {
		throw ZerocoinException("Accumulator witness does not verify");
	}

	// 1: Generate two separate commitments to the public coin (C), each under
	// a different set of public parameters. We do this because the RSA accumulator
	// has specific requirements for the commitment parameters that are not
	// compatible with the group we use for the serial number proof.
	// Specifically, our serial number proof requires the order of the commitment group
	// to be the same as the modulus of the upper group. The Accumulator proof requires a
	// group with a significantly larger order.
	const Commitment fullCommitmentToCoinUnderSerialParams(&p->serialNumberSoKCommitmentGroup, coin.getPublicCoin().getValue());
	this->serialCommitmentToCoinValue = fullCommitmentToCoinUnderSerialParams.getCommitmentValue();

	const Commitment fullCommitmentToCoinUnderAccParams(&p->accumulatorParams.accumulatorPoKCommitmentGroup, coin.getPublicCoin().getValue());
	this->accCommitmentToCoinValue = fullCommitmentToCoinUnderAccParams.getCommitmentValue();

	// 2. Generate a ZK proof that the two commitments contain the same public coin.
	this->commitmentPoK = CommitmentProofOfKnowledge(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup, fullCommitmentToCoinUnderSerialParams, fullCommitmentToCoinUnderAccParams);
	cout << "GNOSIS DEBUG: commitmentPoK is " << this->commitmentPoK.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) << " bytes" << endl;;

	// Now generate the two core ZK proofs:
	// 3. Proves that the committed public coin is in the Accumulator (PoK of "witness")
	this->accumulatorPoK = AccumulatorProofOfKnowledge(&p->accumulatorParams, fullCommitmentToCoinUnderAccParams, witness, a);
	cout << "GNOSIS DEBUG: accPoK is " << this->accumulatorPoK.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) << " bytes" << endl;;

	// 4. Proves that the coin is correct w.r.t. serial number and hidden coin secret
	// (This proof is bound to the coin 'metadata', i.e., transaction hash)
	this->serialNumberSoK = SerialNumberSignatureOfKnowledge(p, coin, fullCommitmentToCoinUnderSerialParams, signatureHash(m));
	cout << "GNOSIS DEBUG: snSoK is " << this->serialNumberSoK.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) << " bytes" << endl;;
}
Example #3
0
// TODO: get parameters from the commitment group
CommitmentProofOfKnowledge::CommitmentProofOfKnowledge(const IntegerGroupParams* aParams,
        const IntegerGroupParams* bParams, const Commitment& a, const Commitment& b):
	ap(aParams),bp(bParams)
{
	CBigNum r1, r2, r3;

	// First: make sure that the two commitments have the
	// same contents.
	if (a.getContents() != b.getContents()) {
		throw std::runtime_error("Both commitments must contain the same value");
	}

	// Select three random values "r1, r2, r3" in the range 0 to (2^l)-1 where l is:
	// length of challenge value + max(modulus 1, modulus 2, order 1, order 2) + margin.
	// We set "margin" to be a relatively generous  security parameter.
	//
	// We choose these large values to ensure statistical zero knowledge.
	uint32_t randomSize = 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()));
	CBigNum maxRange = (CBigNum(2).pow(randomSize) - CBigNum(1));

	r1 = CBigNum::randBignum(maxRange);
	r2 = CBigNum::randBignum(maxRange);
	r3 = CBigNum::randBignum(maxRange);

	// Generate two random, ephemeral commitments "T1, T2"
	// of the form:
	// T1 = g1^r1 * h1^r2 mod p1
	// T2 = g2^r1 * h2^r3 mod p2
	//
	// Where (g1, h1, p1) are from "aParams" and (g2, h2, p2) are from "bParams".
	CBigNum T1 = this->ap->g.pow_mod(r1, this->ap->modulus).mul_mod((this->ap->h.pow_mod(r2, this->ap->modulus)), this->ap->modulus);
	CBigNum T2 = this->bp->g.pow_mod(r1, this->bp->modulus).mul_mod((this->bp->h.pow_mod(r3, this->bp->modulus)), this->bp->modulus);

	// Now hash commitment "A" with commitment "B" as well as the
	// parameters and the two ephemeral commitments "T1, T2" we just generated
	this->challenge = calculateChallenge(a.getCommitmentValue(), b.getCommitmentValue(), T1, T2);

	// Let "m" be the contents of the commitments "A, B". We have:
	// A =  g1^m  * h1^x  mod p1
	// B =  g2^m  * h2^y  mod p2
	// T1 = g1^r1 * h1^r2 mod p1
	// T2 = g2^r1 * h2^r3 mod p2
	//
	// Now compute:
	//  S1 = r1 + (m * challenge)   -- note, not modular arithmetic
	//  S2 = r2 + (x * challenge)   -- note, not modular arithmetic
	//  S3 = r3 + (y * challenge)   -- note, not modular arithmetic
	this->S1 = r1 + (a.getContents() * this->challenge);
	this->S2 = r2 + (a.getRandomness() * this->challenge);
	this->S3 = r3 + (b.getRandomness() * this->challenge);

	// We're done. The proof is S1, S2, S3 and "challenge", all of which
	// are stored in member variables.
}
Example #4
0
// TODO: get parameters from the commitment group
CommitmentProofOfKnowledge::CommitmentProofOfKnowledge(const IntegerGroupParams* aParams,
        const IntegerGroupParams* bParams, const Commitment& a, const Commitment& b):
        ap(aParams),bp(bParams) {
    Bignum r1;
    
    // First: make sure that the two commitments have the
    // same contents.
    if(a.getContents() != b.getContents()){
        throw std::invalid_argument("Both commitments must contain the same value");
    }
    
    // In order to ensure statistical zero knowledge, we pick "r1" out of the
    // largest possible range. In this case, the smaller of the two group orders.
    if(this->ap->groupOrder < this->bp->groupOrder){
        r1 = Bignum::randBignum(ap->groupOrder);
    }else{
        r1 = Bignum::randBignum(bp->groupOrder);
    }
    
    // Generate two random, ephemeral commitments "T1, T2" to "r1" under the two different
    // sets of commitment parameters.
    Commitment t1(aParams, r1);
    Commitment t2(bParams, r1);
    Bignum T1 = t1.getCommitmentValue();
    Bignum T2 = t2.getCommitmentValue();
    
    // Now hash commitment "A" with commitment "B" as well as the
    // parameters and the two ephemeral commitments "T1, T2" we just generated
    this->challenge = calculateChallenge(a.getCommitmentValue(), b.getCommitmentValue(), T1, T2);
    
    // Let "m" be the contents of the commitments. We'll implicitly define
    // A =  g1^m  * h1^x  mod p1
    // B =  g2^m  * h2^y  mod p2
    // T1 = g1^r1 * h1^r2 mod p1
    // T2 = g2^r1 * h2^r3 mod p2
    //
    // Now compute:
    //  S1 = r1 + (m * challenge)
    //  S2 = r2 + (x * challenge)
    //  S3 = r3 + (y * challenge)
    S1 = t1.getContents() + (a.getContents() * challenge);
    S2 = t1.getRandomness() + (a.getRandomness() * challenge);
    S3 = t2.getRandomness() + (b.getRandomness() * challenge);
    
    // We're done. The proof is S1, S2, S3 and "challenge".
}
AccumulatorProofOfKnowledge::AccumulatorProofOfKnowledge(const AccumulatorAndProofParams* p,
        const Commitment& commitmentToCoin, const AccumulatorWitness& witness,
        Accumulator& a): params(p) {

	Bignum sg = params->accumulatorPoKCommitmentGroup.g;
	Bignum sh = params->accumulatorPoKCommitmentGroup.h;

	Bignum g_n = params->accumulatorQRNCommitmentGroup.g;
	Bignum h_n = params->accumulatorQRNCommitmentGroup.h;

	Bignum e = commitmentToCoin.getContents();
	Bignum r = commitmentToCoin.getRandomness();

	Bignum r_1 = Bignum::randBignum(params->accumulatorModulus/4);
	Bignum r_2 = Bignum::randBignum(params->accumulatorModulus/4);
	Bignum r_3 = Bignum::randBignum(params->accumulatorModulus/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);

	Bignum r_alpha = Bignum::randBignum(params->maxCoinValue * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_alpha = 0-r_alpha;
	}

	Bignum r_gamma = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_phi = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_psi = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_sigma = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_xi = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);

	Bignum r_epsilon =  Bignum::randBignum((params->accumulatorModulus/4) * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_epsilon = 0-r_epsilon;
	}
	Bignum r_eta = Bignum::randBignum((params->accumulatorModulus/4) * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_eta = 0-r_eta;
	}
	Bignum r_zeta = Bignum::randBignum((params->accumulatorModulus/4) * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_zeta = 0-r_zeta;
	}

	Bignum r_beta = Bignum::randBignum((params->accumulatorModulus/4) * params->accumulatorPoKCommitmentGroup.modulus * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_beta = 0-r_beta;
	}
	Bignum r_delta = Bignum::randBignum((params->accumulatorModulus/4) * params->accumulatorPoKCommitmentGroup.modulus * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(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.
	Bignum c = Bignum(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));
		}
	}
}