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