github.com/ledgerwatch/erigon-lib@v1.0.0/pedersen_hash/prime_field_element.cc (about) 1 #include "prime_field_element.h" 2 3 namespace starkware { 4 5 PrimeFieldElement PrimeFieldElement::RandomElement(Prng* prng) { 6 constexpr size_t kMostSignificantLimb = ValueType::LimbCount() - 1; 7 static_assert( 8 kModulus[kMostSignificantLimb] != 0, "We assume kModulus[kMostSignificantLimb] is not zero"); 9 constexpr uint64_t kBitsMask = (Pow2(Log2Floor(kModulus[kMostSignificantLimb]) + 1)) - 1; 10 11 PrimeFieldElement random_element = PrimeFieldElement::Zero(); 12 do { 13 random_element.value_ = ValueType::RandomBigInt(prng); 14 random_element.value_[kMostSignificantLimb] &= kBitsMask; 15 } while (random_element.value_ >= kModulus); // Required to enforce uniformity. 16 17 return random_element; 18 } 19 20 PrimeFieldElement PrimeFieldElement::Pow(const std::vector<bool>& exponent_bits) const { 21 return GenericPow( 22 *this, exponent_bits, PrimeFieldElement::One(), 23 [](const PrimeFieldElement& multiplier, PrimeFieldElement* dst) { 24 *dst = *dst * multiplier; 25 }); 26 } 27 28 PrimeFieldElement PrimeFieldElement::Pow(const uint64_t exponent) const { 29 return Pow(BigInt<1>(exponent).ToBoolVector()); 30 } 31 32 bool PrimeFieldElement::IsSquare() const { 33 if (*this == PrimeFieldElement::Zero()) { 34 return true; 35 } 36 37 // value is a square if and only if value^((p-1) / 2) = 1. 38 return Pow(kHalfMultiplicativeGroupSize.ToBoolVector()) == PrimeFieldElement::One(); 39 } 40 41 PrimeFieldElement PrimeFieldElement::Sqrt() const { 42 if (*this == PrimeFieldElement::Zero()) { 43 return PrimeFieldElement::Zero(); 44 } 45 46 // We use the following algorithm to compute the square root of the element: 47 // Let v be the input, let +r and -r be the roots of v and consider the ring 48 // R := F[x] / (x^2 - v). 49 // 50 // This ring is isomorphic to the ring F x F where the isomorphism is given by the map 51 // a*x + b --> (ar + b, -ar + b) (recall that we don't know r, so we cannot compute this map). 52 // 53 // Pick a random element x + b in R, and compute (x + b)^((p-1)/2). Let's say that the result is 54 // c*x + d. 55 // Taking a random element in F to the power of (p-1)/2 gives +1 or -1 with probability 56 // 0.5. Since R is isomorphic to F x F (where multiplication is pointwise), the result of the 57 // computation will be one of the four pairs: 58 // (+1, +1), (-1, -1), (+1, -1), (-1, +1). 59 // 60 // If the result is (+1, +1) or (-1, -1) (which are the elements (0*x + 1) and (0*x - 1) in R) - 61 // try again with another random element. 62 // 63 // If the result is (+1, -1) then cr + d = 1 and -cr + d = -1. Therefore r = c^{-1} and d=0. In 64 // the second case -r = c^{-1}. In both cases c^{-1} will be the returned root. 65 66 // Store an element in R as a pair: first * x + second. 67 using RingElement = std::pair<PrimeFieldElement, PrimeFieldElement>; 68 const RingElement one{PrimeFieldElement::Zero(), PrimeFieldElement::One()}; 69 const RingElement minus_one{PrimeFieldElement::Zero(), -PrimeFieldElement::One()}; 70 71 auto mult = [this](const RingElement& multiplier, RingElement* dst) { 72 // Compute res * multiplier in the ring. 73 auto res_first = multiplier.first * dst->second + multiplier.second * dst->first; 74 auto res_second = multiplier.first * dst->first * *this + multiplier.second * dst->second; 75 *dst = {res_first, res_second}; 76 }; 77 78 // Compute q = (p - 1) / 2 and get its bits. 79 const std::vector<bool> q_bits = kHalfMultiplicativeGroupSize.ToBoolVector(); 80 81 Prng prng; 82 while (true) { 83 // Pick a random element (x + b) in R. 84 RingElement random_element{PrimeFieldElement::One(), PrimeFieldElement::RandomElement(&prng)}; 85 86 // Compute the exponentiation: random_element ^ ((p-1) / 2). 87 RingElement res = GenericPow(random_element, q_bits, one, mult); 88 89 // If res is either 1 or -1, try again. 90 if (res == one || res == minus_one) { 91 continue; 92 } 93 94 const PrimeFieldElement root = res.first.Inverse(); 95 96 ASSERT(root * root == *this, "value does not have a square root."); 97 98 return root; 99 } 100 } 101 102 } // namespace starkware