Bignum Bignum::operator*(const Bignum & bn) const { Bignum tmp; short symbol = 0, bit; const Bignum & self = *this; Bignum::Info info = Bignum::Info(self, bn); if (self[info.a_index] < 0) { symbol ^= 1; } if (bn[info.b_index] < 0) { symbol ^= 1; } self[info.a_index] = llabs(self[info.a_index]); bn[info.b_index] = llabs(bn[info.b_index]); for (int i = bignum_len - 1; i >= info.a_index; i--) { for (int j = bignum_len - 1; j >= info.b_index; j--) { bit = i - ( (bignum_len - 1) - j); tmp[bit] += (self[i] * bn[j]); tmp.carry(bit); } } if (symbol) { tmp[-tmp.bignum_used_len()] *= -1; } return tmp; }
bool CommitmentProofOfKnowledge::Verify(const Bignum& A, const Bignum& B) const { // TODO: First verify that the values // S1, S2 and S3 and "challenge" are in the correct ranges if((this->challenge < Bignum(0)) || (this->challenge > (Bignum(2).pow(256) - Bignum(1)))){ return false; } // Compute T1 = g1^S1 * h1^S2 * inverse(A^{challenge}) mod p1 Bignum 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 Bignum 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 Bignum computedChallenge = calculateChallenge(A, B, T1, T2); // Return success if the computed challenge matches the incoming challenge if(computedChallenge == this->challenge){ return true; } // Otherwise return failure return false; }
int rb_big_sign(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Bignum* big = c_as<Bignum>(env->get_object(obj)); return big->mp_val()->sign != MP_NEG; }
void test_coerce_bignum() { Fixnum* one = Fixnum::from(1); Bignum* e = Bignum::create(state, one); Array* ary = one->coerce(state, e); Fixnum* a = try_as<Fixnum>(ary->get(state, 0)); Fixnum* b = try_as<Fixnum>(ary->get(state, 1)); TS_ASSERT_EQUALS(2, ary->size()); TS_ASSERT(a); TS_ASSERT(b); TS_ASSERT_EQUALS(one, a); TS_ASSERT_EQUALS(one, b); Bignum* f = Bignum::from(state, 9223372036854775807LL); ary = one->coerce(state, f); Bignum* c = try_as<Bignum>(ary->get(state, 0)); Bignum* d = try_as<Bignum>(ary->get(state, 1)); TS_ASSERT_EQUALS(2, ary->size()); TS_ASSERT(c); TS_ASSERT(d); TS_ASSERT_EQUALS(cTrue, c->equal(state, f)); TS_ASSERT_EQUALS(cTrue, d->equal(state, e)); }
int rb_big_bytes_used(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Bignum* big = c_as<Bignum>(env->get_object(obj)); return big->size(env->state())->to_native(); }
bool Test_GenerateGroupParams() { uint32_t pLen = 1024, qLen = 256, count; IntegerGroupParams group; for (count = 0; count < 1; count++) { try { group = deriveIntegerGroupParams(calculateSeed(GetTestModulus(), "test", ZEROCOIN_DEFAULT_SECURITYLEVEL, "TEST GROUP"), pLen, qLen); } catch (std::runtime_error e) { cout << "Caught exception " << e.what() << endl; return false; } // Now perform some simple tests on the resulting parameters if (group.groupOrder.bitSize() < qLen || group.modulus.bitSize() < pLen) { return false; } Bignum c = group.g.pow_mod(group.groupOrder, group.modulus); //cout << "g^q mod p = " << c << endl; if (!(c.isOne())) return false; // Try at multiple parameter sizes pLen = pLen * 1.5; qLen = qLen * 1.5; } return true; }
void test_mul_with_bignum() { Fixnum* one = as<Fixnum>(Fixnum::from(2)); Bignum* two = Bignum::from(state, (native_int)FIXNUM_MAX + 10); Integer* three = one->mul(state, two); TS_ASSERT_EQUALS(three->class_object(state), G(bignum)); Bignum* expected = as<Bignum>(two->mul(state, Fixnum::from(2))); TS_ASSERT_EQUALS(cTrue, as<Bignum>(three)->equal(state, expected)); }
void test_get_type() { TS_ASSERT_EQUALS(Qnil->get_type(), NilType); TS_ASSERT_EQUALS(Qtrue->get_type(), TrueType); TS_ASSERT_EQUALS(Qfalse->get_type(), FalseType); TS_ASSERT_EQUALS(state->symbol("blah")->get_type(), SymbolType); Object* obj = util_new_object(); Bignum* big = Bignum::from(state, (native_int)13); TS_ASSERT_EQUALS(obj->get_type(), ObjectType); TS_ASSERT_EQUALS(big->get_type(), BignumType); }
double rb_big2dbl(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Bignum* big = c_as<Bignum>(env->get_object(obj)); double d = big->to_double(env->state()); if(std::isinf(d)) { rb_warn("Bignum out of Float range"); d = HUGE_VAL; } return d; }
bool Test_MintAndSpend() { try { // This test assumes a list of coins were generated in Test_MintCoin() if (gCoins[0] == NULL) { // No coins: mint some. Test_MintCoin(); if (gCoins[0] == NULL) { return false; } } // Accumulate the list of generated coins into a fresh accumulator. // The first one gets marked as accumulated for a witness, the // others just get accumulated normally. Accumulator acc(&g_Params->accumulatorParams); AccumulatorWitness wAcc(g_Params, acc, gCoins[0]->getPublicCoin()); for (uint32_t i = 0; i < TESTS_COINS_TO_ACCUMULATE; i++) { acc += gCoins[i]->getPublicCoin(); wAcc +=gCoins[i]->getPublicCoin(); } // Now spend the coin SpendMetaData m(1,1); CDataStream cc(SER_NETWORK, PROTOCOL_VERSION); cc << *gCoins[0]; PrivateCoin myCoin(g_Params,cc); CoinSpend spend(g_Params, myCoin, acc, wAcc, m); // Serialize the proof and deserialize into newSpend CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << spend; gProofSize = ss.size(); CoinSpend newSpend(g_Params, ss); // See if we can verify the deserialized proof (return our result) bool ret = newSpend.Verify(acc, m); // Extract the serial number Bignum serialNumber = newSpend.getCoinSerialNumber(); gSerialNumberSize = ceil((double)serialNumber.bitSize() / 8.0); return ret; } catch (runtime_error &e) { cout << e.what() << endl; return false; } return false; }
double rb_big2dbl(VALUE obj) { NativeMethodEnvironment* env = NativeMethodEnvironment::get(); Bignum* big = c_as<Bignum>(env->get_object(obj)); double d = big->to_double(env->state()); if(isinf(d)) { rb_warn("Bignum out of Float range"); if(big->mp_val()->sign == MP_NEG) { d = -HUGE_VAL; } else { d = HUGE_VAL; } } return d; }
bool Test_InvalidCoin() { Bignum coinValue; try { // Pick a random non-prime Bignum for (uint32_t i = 0; i < NON_PRIME_TESTS; i++) { coinValue = Bignum::randBignum(g_Params->coinCommitmentGroup.modulus); coinValue = coinValue * 2; if (!coinValue.isPrime()) break; } PublicCoin pubCoin(g_Params); if (pubCoin.validate()) { // A blank coin should not be valid! return false; } PublicCoin pubCoin2(g_Params, coinValue, ZQ_LOVELACE); if (pubCoin2.validate()) { // A non-prime coin should not be valid! return false; } PublicCoin pubCoin3 = pubCoin2; if (pubCoin2.validate()) { // A copy of a non-prime coin should not be valid! return false; } // Serialize and deserialize the coin CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << pubCoin; PublicCoin pubCoin4(g_Params, ss); if (pubCoin4.validate()) { // A deserialized copy of a non-prime coin should not be valid! return false; } } catch (runtime_error &e) { cout << "Caught exception: " << e.what() << endl; return false; } return true; }
void PrivateCoin::mintCoinFast(const CoinDenomination denomination) { // Generate a random serial number in the range 0...{q-1} where // "q" is the order of the commitment group. Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); // Generate a random number "r" in the range 0...{q-1} Bignum r = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); // Manually compute a Pedersen commitment to the serial number "s" under randomness "r" // C = g^s * h^r mod p Bignum commitmentValue = this->params->coinCommitmentGroup.g.pow_mod(s, this->params->coinCommitmentGroup.modulus).mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus); // Repeat this process up to MAX_COINMINT_ATTEMPTS times until // we obtain a prime number for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) { // First verify that the commitment is a prime number // in the appropriate range. If not, we'll throw this coin // away and generate a new one. if (commitmentValue.isPrime(ZEROCOIN_MINT_PRIME_PARAM) && commitmentValue >= params->accumulatorParams.minCoinValue && commitmentValue <= params->accumulatorParams.maxCoinValue) { // Found a valid coin. Store it. this->serialNumber = s; this->randomness = r; this->publicCoin = PublicCoin(params, commitmentValue, denomination); // Success! We're done. return; } // Generate a new random "r_delta" in 0...{q-1} Bignum r_delta = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); // The commitment was not prime. Increment "r" and recalculate "C": // r = r + r_delta mod q // C = C * h mod p r = (r + r_delta) % this->params->coinCommitmentGroup.groupOrder; commitmentValue = commitmentValue.mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r_delta, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus); } // We only get here if we did not find a coin within // MAX_COINMINT_ATTEMPTS. Throw an exception. throw ZerocoinException("Unable to mint a new Zerocoin (too many attempts)"); }
Bignum::Bignum(const Bignum & bn) { init(); decimal_point = bn.decimal_point; bignum_len = bn.bignum_len; for (int i = bignum_len - bn.bignum_used_len(); i < bignum_len; i++) { bignum[i] = bn[i]; } }
/** Verifies that a commitment c is accumulated in accumulator a */ bool AccumulatorProofOfKnowledge:: Verify(const Accumulator& a, const Bignum& valueOfCommitmentToCoin) const { Bignum sg = params->accumulatorPoKCommitmentGroup.g; Bignum sh = params->accumulatorPoKCommitmentGroup.h; Bignum g_n = params->accumulatorQRNCommitmentGroup.g; Bignum 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; Bignum c = Bignum(hasher.GetHash()); //this hash should be of length k_prime bits Bignum 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; Bignum 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; Bignum 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; Bignum 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; Bignum 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; Bignum 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; Bignum 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 = false; 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 * Bignum(2).pow(params->k_prime + params->k_dprime + 1))) && (s_alpha <= (params->maxCoinValue * Bignum(2).pow(params->k_prime + params->k_dprime + 1)))); result = result_st1 && result_st2 && result_st3 && result_t1 && result_t2 && result_t3 && result_t4 && result_range; return result; }
///////////////////////////////////// // // Operator Computing // //////////////////////////////////// Bignum Bignum::operator+(const Bignum & bn) const { Bignum tmp; const Bignum & self = *this; Bignum::Info info = Bignum::Info(self, bn); if (bn[info.b_index] < 0) { tmp = bn; tmp[info.b_index] = llabs(tmp[info.b_index]); return self - bn; } else if (self[info.a_index] < 0) { tmp = self; tmp[info.a_index] = llabs(tmp[info.a_index]); return bn - tmp; } for (int i = bignum_len - 1; i >= 0; i--) { tmp[i] += self[i] + bn[i]; tmp.carry(i); } return tmp; }
void CalculateParams(Params ¶ms, Bignum N, string aux, uint32_t securityLevel) { cout << "GNOSIS DEBUG: CalculateParams in ParamGeneration.cpp" << endl; params.initialized = false; params.accumulatorParams.initialized = false; cout << "GNOSIS DEBUG: aux is " << aux << endl; // Verify that |N| is > 1023 bits. uint32_t NLen = N.bitSize(); cout << "GNOSIS DEBUG: NLen is " << NLen << endl; if (NLen < 1023) { throw ZerocoinException("Modulus must be at least 1023 bits"); } // Verify that "securityLevel" is at least 80 bits (minimum). if (securityLevel < 80) { throw ZerocoinException("Security level must be at least 80 bits."); } cout << "GNOSIS DEBUG: securityLevel is " << securityLevel << endl; // Set the accumulator modulus to "N". params.accumulatorParams.accumulatorModulus = N; // Calculate the required size of the field "F_p" into which // we're embedding the coin commitment group. This may throw an // exception if the securityLevel is too large to be supported // by the current modulus. uint32_t pLen = 0; uint32_t qLen = 0; calculateGroupParamLengths(NLen - 2, securityLevel, &pLen, &qLen); // Calculate candidate parameters ("p", "q") for the coin commitment group // using a deterministic process based on "N", the "aux" string, and // the dedicated string "COMMITMENTGROUP". params.coinCommitmentGroup = deriveIntegerGroupParams(calculateSeed(N, aux, securityLevel, STRING_COMMIT_GROUP), pLen, qLen); // g and h are invalid, since they are now different for each coin; see // "Rational Zero" by Garman et al., section 4.4. params.coinCommitmentGroup.invalidateGenerators(); PRINT_BIGNUM("params.coinCommitmentGroup.groupOrder", params.coinCommitmentGroup.groupOrder); PRINT_BIGNUM("params.coinCommitmentGroup.modulus", params.coinCommitmentGroup.modulus); // Next, we derive parameters for a second Accumulated Value commitment group. // This is a Schnorr group with the specific property that the order of the group // must be exactly equal to "q" from the commitment group. We set // the modulus of the new group equal to "2q+1" and test to see if this is prime. params.serialNumberSoKCommitmentGroup = deriveIntegerGroupFromOrder(params.coinCommitmentGroup.modulus); PRINT_GROUP_PARAMS(params.serialNumberSoKCommitmentGroup); // Calculate the parameters for the internal commitment // using the same process. params.accumulatorParams.accumulatorPoKCommitmentGroup = deriveIntegerGroupParams(calculateSeed(N, aux, securityLevel, STRING_AIC_GROUP), qLen + 300, qLen + 1); PRINT_GROUP_PARAMS(params.accumulatorParams.accumulatorPoKCommitmentGroup); // Calculate the parameters for the accumulator QRN commitment generators. This isn't really // a whole group, just a pair of random generators in QR_N. uint32_t resultCtr; params.accumulatorParams.accumulatorQRNCommitmentGroup.g(generateIntegerFromSeed(NLen - 1, calculateSeed(N, aux, securityLevel, STRING_QRNCOMMIT_GROUPG), &resultCtr).pow_mod(Bignum(2), N)); params.accumulatorParams.accumulatorQRNCommitmentGroup.h(generateIntegerFromSeed(NLen - 1, calculateSeed(N, aux, securityLevel, STRING_QRNCOMMIT_GROUPH), &resultCtr).pow_mod(Bignum(2), N)); PRINT_BIGNUM("params.accumulatorParams.accumulatorQRNCommitmentGroup.g", params.accumulatorParams.accumulatorQRNCommitmentGroup.g()); PRINT_BIGNUM("params.accumulatorParams.accumulatorQRNCommitmentGroup.h", params.accumulatorParams.accumulatorQRNCommitmentGroup.h()); // Calculate the accumulator base, which we calculate as "u = C**2 mod N" // where C is an arbitrary value. In the unlikely case that "u = 1" we increment // "C" and repeat. Bignum constant(ACCUMULATOR_BASE_CONSTANT); params.accumulatorParams.accumulatorBase = Bignum(1); for (uint32_t count = 0; count < MAX_ACCUMGEN_ATTEMPTS && params.accumulatorParams.accumulatorBase.isOne(); count++) { params.accumulatorParams.accumulatorBase = constant.pow_mod(Bignum(2), params.accumulatorParams.accumulatorModulus); } // Compute the accumulator range. The upper range is the largest possible coin commitment value. // The lower range is sqrt(upper range) + 1. Since OpenSSL doesn't have // a square root function we use a slightly higher approximation. params.accumulatorParams.maxCoinValue = params.coinCommitmentGroup.modulus; params.accumulatorParams.minCoinValue = Bignum(2).pow((params.coinCommitmentGroup.modulus.bitSize() / 2) + 3); // If all went well, mark params as successfully initialized. params.accumulatorParams.initialized = true; // If all went well, mark params as successfully initialized. params.initialized = true; }
int main(int argc, char **argv) { static Bignum resultModulus(0); uint32_t numBits = DEFAULT_MODULUS_SIZE; ofstream outfile; char* outfileName; bool writeToFile = false; while ((argc > 1) && (argv[1][0] == '-')) { switch (argv[1][1]) { case 'b': numBits = atoi(argv[2]); ++argv; --argc; break; case 'o': outfileName = argv[2]; writeToFile = true; break; case 'h': usage(); break; default: printf("Wrong Argument: %s\n", argv[1]); usage(); break; } ++argv; --argc; } if (numBits < MIN_MODULUS_SIZE) { cout << "Modulus is below minimum length (" << MIN_MODULUS_SIZE << ") bits" << endl; return(0); } PrintWarning(); cout << "Modulus size set to " << numBits << " bits." << endl; cout << "Generating parameters. This may take a few minutes..." << endl; // Generate two safe primes "p" and "q" Bignum *p, *q; p = new Bignum(0); q = new Bignum(0); *p = Bignum::generatePrime(numBits / 2, true); *q = Bignum::generatePrime(numBits / 2, true); // Multiply to compute N resultModulus = (*p) * (*q); // Wipe out the factors delete p; delete q; // Convert to a hexidecimal string std::string resultHex = resultModulus.ToString(16); cout << endl << "N = " << endl << resultHex << endl; if (writeToFile) { try { outfile.open (outfileName); outfile << resultHex; outfile.close(); cout << endl << "Result has been written to file '" << outfileName << "'." << endl; } catch (std::runtime_error &e) { cout << "Unable to write to file:" << e.what() << endl; } } }
Value RandomState::random(Value arg) { if (fixnump(arg)) { long n = xlong(arg); if (n > 0) { mpz_t limit; mpz_init_set_si(limit, n); mpz_t result; mpz_init(result); mpz_urandomm(result, _state, limit); return normalize(result); } } else if (bignump(arg)) { Bignum * b = the_bignum(arg); if (b->plusp()) { mpz_t result; mpz_init(result); mpz_urandomm(result, _state, b->_z); return normalize(result); } } else if (single_float_p(arg)) { float f = the_single_float(arg)->_f; if (f > 0) { mpz_t fixnum_limit; mpz_init_set_si(fixnum_limit, MOST_POSITIVE_FIXNUM); mpz_t fixnum_result; mpz_init(fixnum_result); mpz_urandomm(fixnum_result, _state, fixnum_limit); double double_result = mpz_get_si(fixnum_result); double_result /= MOST_POSITIVE_FIXNUM; return make_value(new SingleFloat(double_result * f)); } } else if (double_float_p(arg)) { double d = the_double_float(arg)->_d; if (d > 0) { mpz_t fixnum_limit; mpz_init_set_si(fixnum_limit, MOST_POSITIVE_FIXNUM); mpz_t fixnum_result; mpz_init(fixnum_result); mpz_urandomm(fixnum_result, _state, fixnum_limit); double double_result = mpz_get_si(fixnum_result); double_result /= MOST_POSITIVE_FIXNUM; return make_value(new DoubleFloat(double_result * d)); } } return signal_type_error(arg, list3(S_or, list2(S_integer, list1(FIXNUM_ZERO)), list2(S_float, list1(FIXNUM_ZERO)))); }
void calculateGroupModulusAndOrder(uint256 seed, uint32_t pLen, uint32_t qLen, Bignum *resultModulus, Bignum *resultGroupOrder, uint256 *resultPseed, uint256 *resultQseed) { // Verify that the seed length is >= qLen if (qLen > (sizeof(seed)) * 8) { // TODO: The use of 256-bit seeds limits us to 256-bit group orders. We should probably change this. // throw ZerocoinException("Seed is too short to support the required security level."); } #ifdef ZEROCOIN_DEBUG cout << "calculateGroupModulusAndOrder: pLen = " << pLen << endl; #endif // Generate a random prime for the group order. // This may throw an exception, which we'll pass upwards. // Result is the value "resultGroupOrder", "qseed" and "qgen_counter". uint256 qseed; uint32_t qgen_counter; *resultGroupOrder = generateRandomPrime(qLen, seed, &qseed, &qgen_counter); // Using ⎡pLen / 2 + 1⎤ as the length and qseed as the input_seed, use the random prime // routine to obtain p0 , pseed, and pgen_counter. We pass exceptions upward. uint32_t p0len = ceil((pLen / 2.0) + 1); uint256 pseed; uint32_t pgen_counter; Bignum p0 = generateRandomPrime(p0len, qseed, &pseed, &pgen_counter); // Set x = 0, old_counter = pgen_counter uint32_t old_counter = pgen_counter; // Generate a random integer "x" of pLen bits uint32_t iterations; Bignum x = generateIntegerFromSeed(pLen, pseed, &iterations); pseed += (iterations + 1); // Set x = 2^{pLen−1} + (x mod 2^{pLen–1}). Bignum powerOfTwo = Bignum(2).pow(pLen-1); x = powerOfTwo + (x % powerOfTwo); // t = ⎡x / (2 * resultGroupOrder * p0)⎤. // TODO: we don't have a ceiling function Bignum t = x / (Bignum(2) * (*resultGroupOrder) * p0); // Now loop until we find a valid prime "p" or we fail due to // pgen_counter exceeding ((4*pLen) + old_counter). for ( ; pgen_counter <= ((4*pLen) + old_counter) ; pgen_counter++) { // If (2 * t * resultGroupOrder * p0 + 1) > 2^{pLen}, then // t = ⎡2^{pLen−1} / (2 * resultGroupOrder * p0)⎤. powerOfTwo = Bignum(2).pow(pLen); Bignum prod = (Bignum(2) * t * (*resultGroupOrder) * p0) + Bignum(1); if (prod > powerOfTwo) { // TODO: implement a ceil function t = Bignum(2).pow(pLen-1) / (Bignum(2) * (*resultGroupOrder) * p0); } // Compute a candidate prime resultModulus = 2tqp0 + 1. *resultModulus = (Bignum(2) * t * (*resultGroupOrder) * p0) + Bignum(1); // Verify that resultModulus is prime. First generate a pseudorandom integer "a". Bignum a = generateIntegerFromSeed(pLen, pseed, &iterations); pseed += iterations + 1; // Set a = 2 + (a mod (resultModulus–3)). a = Bignum(2) + (a % ((*resultModulus) - Bignum(3))); // Set z = a^{2 * t * resultGroupOrder} mod resultModulus Bignum z = a.pow_mod(Bignum(2) * t * (*resultGroupOrder), (*resultModulus)); // If GCD(z–1, resultModulus) == 1 AND (z^{p0} mod resultModulus == 1) // then we have found our result. Return. if ((resultModulus->gcd(z - Bignum(1))).isOne() && (z.pow_mod(p0, (*resultModulus))).isOne()) { // Success! Return the seeds and primes. *resultPseed = pseed; *resultQseed = qseed; return; } // This prime did not work out. Increment "t" and try again. t = t + Bignum(1); } // loop continues until pgen_counter exceeds a limit // We reach this point only if we exceeded our maximum iteration count. // Throw an exception. throw ZerocoinException("Unable to generate a prime modulus for the group"); }
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)); }
Bignum generateRandomPrime(uint32_t primeBitLen, uint256 in_seed, uint256 *out_seed, uint32_t *prime_gen_counter) { // Verify that primeBitLen is not too small if (primeBitLen < 2) { throw ZerocoinException("Prime length is too short"); } // If primeBitLen < 33 bits, perform the base case. if (primeBitLen < 33) { Bignum result(0); // Set prime_seed = in_seed, prime_gen_counter = 0. uint256 prime_seed = in_seed; (*prime_gen_counter) = 0; // Loop up to "4 * primeBitLen" iterations. while ((*prime_gen_counter) < (4 * primeBitLen)) { // Generate a pseudorandom integer "c" of length primeBitLength bits uint32_t iteration_count; Bignum c = generateIntegerFromSeed(primeBitLen, prime_seed, &iteration_count); #ifdef ZEROCOIN_DEBUG cout << "generateRandomPrime: primeBitLen = " << primeBitLen << endl; cout << "Generated c = " << c << endl; #endif prime_seed += (iteration_count + 1); (*prime_gen_counter)++; // Set "intc" to be the least odd integer >= "c" we just generated uint32_t intc = c.getulong(); intc = (2 * floor(intc / 2.0)) + 1; #ifdef ZEROCOIN_DEBUG cout << "Should be odd. c = " << intc << endl; cout << "The big num is: c = " << c << endl; #endif // Perform trial division on this (relatively small) integer to determine if "intc" // is prime. If so, return success. if (primalityTestByTrialDivision(intc)) { // Return "intc" converted back into a Bignum and "prime_seed". We also updated // the variable "prime_gen_counter" in previous statements. result = intc; *out_seed = prime_seed; // Success return result; } } // while() // If we reached this point there was an error finding a candidate prime // so throw an exception. throw ZerocoinException("Unable to find prime in Shawe-Taylor algorithm"); // END OF BASE CASE } // If primeBitLen >= 33 bits, perform the recursive case. else { // Recurse to find a new random prime of roughly half the size uint32_t newLength = ceil((double)primeBitLen / 2.0) + 1; Bignum c0 = generateRandomPrime(newLength, in_seed, out_seed, prime_gen_counter); // Generate a random integer "x" of primeBitLen bits using the output // of the previous call. uint32_t numIterations; Bignum x = generateIntegerFromSeed(primeBitLen, *out_seed, &numIterations); (*out_seed) += numIterations + 1; // Compute "t" = ⎡x / (2 * c0⎤ // TODO no Ceiling call Bignum t = x / (Bignum(2) * c0); // Repeat the following procedure until we find a prime (or time out) for (uint32_t testNum = 0; testNum < MAX_PRIMEGEN_ATTEMPTS; testNum++) { // If ((2 * t * c0) + 1 > 2^{primeBitLen}), // then t = ⎡2^{primeBitLen} – 1 / (2 * c0)⎤. if ((Bignum(2) * t * c0) > (Bignum(2).pow(Bignum(primeBitLen)))) { t = ((Bignum(2).pow(Bignum(primeBitLen))) - Bignum(1)) / (Bignum(2) * c0); } // Set c = (2 * t * c0) + 1 Bignum c = (Bignum(2) * t * c0) + Bignum(1); // Increment prime_gen_counter (*prime_gen_counter)++; // Test "c" for primality as follows: // 1. First pick an integer "a" in between 2 and (c - 2) Bignum a = generateIntegerFromSeed(c.bitSize(), (*out_seed), &numIterations); a = Bignum(2) + (a % (c - Bignum(3))); (*out_seed) += (numIterations + 1); // 2. Compute "z" = a^{2*t} mod c Bignum z = a.pow_mod(Bignum(2) * t, c); // 3. Check if "c" is prime. // Specifically, verify that gcd((z-1), c) == 1 AND (z^c0 mod c) == 1 // If so we return "c" as our result. if (c.gcd(z - Bignum(1)).isOne() && z.pow_mod(c0, c).isOne()) { // Return "c", out_seed and prime_gen_counter // (the latter two of which were already updated) return c; } // 4. If the test did not succeed, increment "t" and loop t = t + Bignum(1); } // end of test loop } // We only reach this point if the test loop has iterated MAX_PRIMEGEN_ATTEMPTS // and failed to identify a valid prime. Throw an exception. throw ZerocoinException("Unable to generate random prime (too many tests)"); }
bool ZerocoinTutorial() { // The following simple code illustrates the call flow for Zerocoin // applications. In a real currency network these operations would // be split between individual payers/payees, network nodes and miners. // // For each call we specify the participant who would use it. // Zerocoin uses exceptions (based on the runtime_error class) // to indicate all sorts of problems. Always remember to catch them! try { /********************************************************************/ // What is it: Parameter loading // Who does it: ALL ZEROCOIN PARTICIPANTS // What it does: Loads a trusted Zerocoin modulus "N" and // generates all associated parameters from it. // We use a hardcoded "N" that we generated using // the included 'paramgen' utility. /********************************************************************/ // Load a test modulus from our hardcoded string (above) Bignum testModulus; testModulus.SetHex(std::string(TUTORIAL_TEST_MODULUS)); // Set up the Zerocoin Params object libzerocoin::Params* params = new libzerocoin::Params(testModulus); cout << "Successfully loaded parameters." << endl; /********************************************************************/ // What is it: Coin generation // Who does it: ZEROCOIN CLIENTS // What it does: Generates a new 'zerocoin' coin using the // public parameters. Once generated, the client // will transmit the public portion of this coin // in a ZEROCOIN_MINT transaction. The inputs // to this transaction must add up to the zerocoin // denomination plus any transaction fees. /********************************************************************/ // The following constructor does all the work of minting a brand // new zerocoin. It stores all the private values inside the // PrivateCoin object. This includes the coin secrets, which must be // stored in a secure location (wallet) at the client. libzerocoin::PrivateCoin newCoin(params); // Get a copy of the 'public' portion of the coin. You should // embed this into a Zerocoin 'MINT' transaction along with a series // of currency inputs totaling the assigned value of one zerocoin. libzerocoin::PublicCoin pubCoin = newCoin.getPublicCoin(); cout << "Successfully minted a zerocoin." << endl; // Serialize the public coin to a CDataStream object. CDataStream serializedCoin(SER_NETWORK, PROTOCOL_VERSION); serializedCoin << pubCoin; /********************************************************************/ // What is it: Coin verification // Who does it: TRANSACTION VERIFIERS // What it does: Verifies the structure of a zerocoin obtained from // a ZEROCOIN_MINT transaction. All coins must be // verified before you operate on them. // Note that this is only part of the transaction // verification process! The client must also check // that (1) the inputs to the transaction are valid // and add up to the value of one zerocoin, (2) that // this particular zerocoin has not been minted before. /********************************************************************/ // Deserialize the public coin into a fresh object. This will // automatically validate that the coin is correctly structured and // will throw an exception if it isn't. (You need to handle those.) libzerocoin::PublicCoin pubCoinNew(params, serializedCoin); cout << "Deserialized and verified the coin." << endl; /********************************************************************/ // What is it: Accumulator computation // Who does it: ZEROCOIN CLIENTS & TRANSACTION VERIFIERS // What it does: Collects a number of PublicCoin values drawn from // the block chain and calculates an accumulator. // This accumulator is incrementally computable; // you can stop and serialize it at any point // then continue accumulating new transactions. // The accumulator is also order-independent, so // the same coins can be accumulated in any order // to give the same result. // WARNING: do not accumulate the same coin twice! /********************************************************************/ // Create an empty accumulator object libzerocoin::Accumulator accumulator(params); // Add several coins to it (we'll generate them here on the fly). for (uint32_t i = 0; i < COINS_TO_ACCUMULATE; i++) { libzerocoin::PrivateCoin testCoin(params); accumulator += testCoin.getPublicCoin(); } // Serialize the accumulator object. // // If you're using Accumulator Checkpoints, each miner would // start by deserializing the accumulator checkpoint from the // previous block (or creating a new Accumulator if no previous // block exists). It will then add all the coins in the new block, // then serialize the resulting Accumulator object to obtain the // new checkpoint. All block verifiers should do the same thing // to check their work. CDataStream serializedAccumulator(SER_NETWORK, PROTOCOL_VERSION); serializedAccumulator << accumulator; // Deserialize the accumulator object libzerocoin::Accumulator newAccumulator(params, serializedAccumulator); // We can now continue accumulating things into the accumulator // we just deserialized. For example, let's put in the coin // we generated up above. newAccumulator += pubCoinNew; cout << "Successfully accumulated coins." << endl; /********************************************************************/ // What is it: Coin spend // Who does it: ZEROCOIN CLIENTS // What it does: Create a new transaction that spends a Zerocoin. // The user first authors a transaction specifying // a set of destination addresses (outputs). They // next compute an Accumulator over all coins in the // block chain (see above) and a Witness based on the // coin to be spent. Finally they instantiate a CoinSpend // object that 'signs' the transaction with a special // zero knowledge signature of knowledge over the coin // data, Witness and Accumulator. /********************************************************************/ // We are going to spend the coin "newCoin" that we generated at the // top of this function. // // We'll use the accumulator we constructed above. This contains // a set of coins, but does NOT include the coin "newCoin". // // To generate the witness, we start with this accumulator and // add the public half of the coin we want to spend. libzerocoin::AccumulatorWitness witness(params, accumulator, newCoin.getPublicCoin()); // Add the public half of "newCoin" to the Accumulator itself. accumulator += newCoin.getPublicCoin(); // At this point we should generate a ZEROCOIN_SPEND transaction to // send to the network. This network should include a set of outputs // totalling to the value of one zerocoin (minus transaction fees). // // The format of this transaction is up to the implementer. Here we'll // assume you've formatted this transaction and placed the hash into // "transactionHash". We'll also assume "accumulatorHash" contains the // hash of the last block whose transactions are in the accumulator. uint256 transactionHash = DUMMY_TRANSACTION_HASH; uint256 accumulatorID = DUMMY_ACCUMULATOR_ID; // Place "transactionHash" and "accumulatorBlockHash" into a new // SpendMetaData object. libzerocoin::SpendMetaData metaData(accumulatorID, transactionHash); // Construct the CoinSpend object. This acts like a signature on the // transaction. libzerocoin::CoinSpend spend(params, newCoin, accumulator, witness, metaData); // This is a sanity check. The CoinSpend object should always verify, // but why not check before we put it onto the wire? if (!spend.Verify(accumulator, metaData)) { cout << "ERROR: Our new CoinSpend transaction did not verify!" << endl; return false; } // Serialize the CoinSpend object into a buffer. CDataStream serializedCoinSpend(SER_NETWORK, PROTOCOL_VERSION); serializedCoinSpend << spend; cout << "Successfully generated a coin spend transaction." << endl; /********************************************************************/ // What is it: Coin spend verification // Who does it: ALL PARTIES // What it does: Verifies that a CoinSpend signature is correct // with respect to a ZEROCOIN_SPEND transaction hash. // The client must also extract the serial number from // the CoinSpend and verify that this serial number has // not previously appeared in another ZEROCOIN_SPEND // transaction. /********************************************************************/ // Deserialize the CoinSpend intro a fresh object libzerocoin::CoinSpend newSpend(params, serializedCoinSpend); // Create a new metadata object to contain the hash of the received // ZEROCOIN_SPEND transaction. If we were a real client we'd actually // compute the hash of the received transaction here. libzerocoin::SpendMetaData newMetadata(accumulatorID, transactionHash); // If we were a real client we would now re-compute the Accumulator // from the information given in the ZEROCOIN_SPEND transaction. // For our purposes we'll just use the one we calculated above. // // Verify that the spend is valid with respect to the Accumulator // and the Metadata if (!newSpend.Verify(accumulator, newMetadata)) { cout << "ERROR: The CoinSpend transaction did not verify!" << endl; return false; } // Pull the serial number out of the CoinSpend object. If we // were a real Zerocoin client we would now check that the serial number // has not been spent before (in another ZEROCOIN_SPEND) transaction. // The serial number is stored as a Bignum. Bignum serialNumber = newSpend.getCoinSerialNumber(); cout << "Successfully verified a coin spend transaction." << endl; cout << endl << "Coin serial number is:" << endl << serialNumber << endl; // We're done return true; } catch (runtime_error &e) { cout << e.what() << endl; return false; } return false; }