void calculateGroupParamLengths(uint32_t maxPLen, uint32_t securityLevel, uint32_t *pLen, uint32_t *qLen) { *pLen = *qLen = 0; if (securityLevel < 80) { throw ZerocoinException("Security level must be at least 80 bits."); } else if (securityLevel == 80) { *qLen = 256; *pLen = 1024; } else if (securityLevel <= 112) { *qLen = 256; *pLen = 2048; } else if (securityLevel <= 128) { *qLen = 320; *pLen = 3072; } else { throw ZerocoinException("Security level not supported."); } if (*pLen > maxPLen) { throw ZerocoinException("Modulus size is too small for this security level."); } }
Bignum calculateGroupGenerator(Bignum serialNumber, uint256 seed, uint256 pSeed, uint256 qSeed, Bignum modulus, Bignum groupOrder, uint32_t index) { Bignum result; // Verify that 0 <= index < 256 if (index > 255) { throw ZerocoinException("Invalid index for group generation"); } // Compute e = (modulus - 1) / groupOrder Bignum e = (modulus - Bignum(1)) / groupOrder; // Loop until we find a generator for (uint32_t count = 1; count < MAX_GENERATOR_ATTEMPTS; count++) { // hash = Hash(seed || pSeed || qSeed || “ggen” || index || count uint256 hash = (serialNumber > 0) ? calculateGeneratorSeed(serialNumber, "ggen", index, count) : calculateGeneratorSeed(seed, pSeed, qSeed, "ggen", index, count); Bignum W(hash); // Compute result = W^e mod p result = W.pow_mod(e, modulus); // If result > 1, we have a generator if (result > 1) { return result; } } // We only get here if we failed to find a generator throw ZerocoinException("Unable to find a generator, too many attempts"); }
IntegerGroupParams deriveIntegerGroupFromOrder(Bignum &groupOrder) { IntegerGroupParams result; // Set the order to "groupOrder" result.groupOrder = groupOrder; // Try possible values for "modulus" of the form "groupOrder * 2 * i" where // "p" is prime and i is a counter starting at 1. for (uint32_t i = 1; i < NUM_SCHNORRGEN_ATTEMPTS; i++) { // Set modulus equal to "groupOrder * 2 * i" result.modulus = (result.groupOrder * Bignum(i*2)) + Bignum(1); // Test the result for primality // TODO: This is a probabilistic routine and thus not the right choice if (result.modulus.isPrime(256)) { // Success. // // Calculate the generators "g", "h" using the process described in // NIST FIPS 186-3, Appendix A.2.3. This algorithm takes ("p", "q", // "domain_parameter_seed", "index"). We use "index" value 1 // to generate "g" and "index" value 2 to generate "h". uint256 seed = calculateSeed(groupOrder, "", 128, ""); uint256 pSeed = calculateHash(seed); uint256 qSeed = calculateHash(pSeed); result.g(calculateGroupGenerator(Bignum(0), seed, pSeed, qSeed, result.modulus, result.groupOrder, 1)); result.h(calculateGroupGenerator(Bignum(0), seed, pSeed, qSeed, result.modulus, result.groupOrder, 2)); // Perform some basic tests to make sure we have good parameters if (!(result.modulus.isPrime()) || // modulus is prime !(result.groupOrder.isPrime()) || // order is prime !((result.g().pow_mod(result.groupOrder, result.modulus)).isOne()) || // g^order mod modulus = 1 !((result.h().pow_mod(result.groupOrder, result.modulus)).isOne()) || // h^order mod modulus = 1 ((result.g().pow_mod(Bignum(100), result.modulus)).isOne()) || // g^100 mod modulus != 1 ((result.h().pow_mod(Bignum(100), result.modulus)).isOne()) || // h^100 mod modulus != 1 result.g() == result.h() || // g != h result.g().isOne()) { // g != 1 // If any of the above tests fail, throw an exception throw ZerocoinException("Group parameters are not valid"); } return result; } } // If we reached this point group generation has failed. Throw an exception. throw ZerocoinException("Too many attempts to generate Schnorr group."); }
//Accumulator class Accumulator::Accumulator(const AccumulatorAndProofParams* p, const CoinDenomination d): params(p), denomination(d) { if (!(params->initialized)) { throw ZerocoinException("Invalid parameters for accumulator"); } this->value = this->params->accumulatorBase; }
void PrivateCoin::mintCoin(const CoinDenomination denomination) { // 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++) { // 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 Pedersen commitment to the serial number "s" Commitment coin(¶ms->coinCommitmentGroup, s); // Now 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 (coin.getCommitmentValue().isPrime(ZEROCOIN_MINT_PRIME_PARAM) && coin.getCommitmentValue() >= params->accumulatorParams.minCoinValue && coin.getCommitmentValue() <= params->accumulatorParams.maxCoinValue) { // Found a valid coin. Store it. this->serialNumber = s; this->randomness = coin.getRandomness(); this->publicCoin = PublicCoin(params,coin.getCommitmentValue(), denomination); // Success! We're done. return; } } // 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)"); }
Accumulator::Accumulator(const Params* p, const CoinDenomination d) { this->params = &(p->accumulatorParams); this->denomination = d; if (!(params->initialized)) { throw ZerocoinException("Invalid parameters for accumulator"); } this->value = this->params->accumulatorBase; }
/// \brief Deterministically derives coin commitment group generators g & h from a serial number (and group modulus and order). /// \param serialNumber Serial number of the ZC spend. /// \param modulus Prime modulus for the field. /// \param groupOrder Order of the group. /// \param g_out Out param for g generator. /// \param h_out Out param for h generator. /// \throws A ZerocoinException if error. /// /// The purpose of having different generators for each ZC spend is to prevent /// one solution of the discrete log problem from allowing infinite double spends. /// See "Rational Zero" by Garman et al., section 4.4 for more. /// /// Unlike the other functions in this file, this is called after initial setup /// of Zerocoin parameters (i.e., it is called during minting, spending, and verifying). void deriveGeneratorsFromSerialNumber(Bignum serialNumber, Bignum modulus, Bignum groupOrder, Bignum& g_out, Bignum& h_out) { Bignum g, h; g = calculateGroupGenerator(serialNumber, 0, 0, 0, modulus, groupOrder, 1); h = calculateGroupGenerator(serialNumber, 0, 0, 0, modulus, groupOrder, 2); if (g == h) { throw ZerocoinException("g == h for coin commitment group generators derived from serial number"); } g_out = g; h_out = h; }
void Accumulator::accumulate(const PublicCoin& coin) { // Make sure we're initialized if(!(this->value)) { throw ZerocoinException("Accumulator is not initialized"); } if(this->denomination != coin.getDenomination()) { //std::stringstream msg; std::string msg; msg = "Wrong denomination for coin. Expected coins of denomination: "; msg += this->denomination; msg += ". Instead, got a coin of denomination: "; msg += coin.getDenomination(); throw ZerocoinException(msg); } if(coin.validate()) { // Compute new accumulator = "old accumulator"^{element} mod N this->value = this->value.pow_mod(coin.getValue(), this->params->accumulatorModulus); } else { throw ZerocoinException("Coin is not valid"); } }
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)"); }
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;; }
IntegerGroupParams deriveIntegerGroupParams(uint256 seed, uint32_t pLen, uint32_t qLen) { IntegerGroupParams result; Bignum p; Bignum q; uint256 pSeed, qSeed; // Calculate "p" and "q" and "domain_parameter_seed" from the // "seed" buffer above, using the procedure described in NIST // FIPS 186-3, Appendix A.1.2. calculateGroupModulusAndOrder(seed, pLen, qLen, &(result.modulus), &(result.groupOrder), &pSeed, &qSeed); // Calculate the generators "g", "h" using the process described in // NIST FIPS 186-3, Appendix A.2.3. This algorithm takes ("p", "q", // "domain_parameter_seed", "index"). We use "index" value 1 // to generate "g" and "index" value 2 to generate "h". result.g(calculateGroupGenerator(Bignum(0), seed, pSeed, qSeed, result.modulus, result.groupOrder, 1)); result.h(calculateGroupGenerator(Bignum(0), seed, pSeed, qSeed, result.modulus, result.groupOrder, 2)); // Perform some basic tests to make sure we have good parameters if ((uint32_t)(result.modulus.bitSize()) < pLen || // modulus is pLen bits long (uint32_t)(result.groupOrder.bitSize()) < qLen || // order is qLen bits long !(result.modulus.isPrime()) || // modulus is prime !(result.groupOrder.isPrime()) || // order is prime !((result.g().pow_mod(result.groupOrder, result.modulus)).isOne()) || // g^order mod modulus = 1 !((result.h().pow_mod(result.groupOrder, result.modulus)).isOne()) || // h^order mod modulus = 1 ((result.g().pow_mod(Bignum(100), result.modulus)).isOne()) || // g^100 mod modulus != 1 ((result.h().pow_mod(Bignum(100), result.modulus)).isOne()) || // h^100 mod modulus != 1 result.g() == result.h() || // g != h result.g().isOne()) { // g != 1 // If any of the above tests fail, throw an exception throw ZerocoinException("Group parameters are not valid"); } return result; }
Bignum calculateRawUFO(uint32_t ufoIndex, uint32_t numBits) { Bignum result(0); uint32_t hashes = numBits / HASH_OUTPUT_BITS; if (numBits != HASH_OUTPUT_BITS * hashes) { throw ZerocoinException("numBits must be divisible by HASH_OUTPUT_BITS"); // not implemented } for (uint32_t i = 0; i < hashes; i++) { CHashWriter hasher(0,0); hasher << ufoIndex; hasher << string("||"); hasher << numBits; hasher << string("||"); hasher << i; uint256 hash = hasher.GetHash(); result <<= HASH_OUTPUT_BITS; result += Bignum(hash); } return result; }
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)"); }
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; }
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"); }