1

In the Zero to Monero book, I am reading about Schnorr signatures. Section 2.3.4 references the random32_unbiased() function from src/crypto/crypto.cpp of the codebase. My understsanding is that this function generates a random integer between 1 and l-1 (both inclusive), where l is a large integer.

That function is:

void random32_unbiased(unsigned char *bytes)
  {
    // l = 2^252 + 27742317777372353535851937790883648493.
    // l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
    static const unsigned char limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
    while(1)
    {
      generate_random_bytes_thread_safe(32, bytes);
      if (!less32(bytes, limit))
        continue;
      sc_reduce32(bytes);
      if (sc_isnonzero(bytes))
        break;
    }
  }

What purpose does the line with static const unsigned char limit[32] have?

My main question is the above one, but in general, I do not deeply understand how function works, so an explanation would be appreciated too.

zabop
  • 6,750
  • 3
  • 39
  • 84
  • 2
    I'm guessing based on the comment that it's 15*l. This method implements a subtlety about picking random integers between two limits using a source of random bits (or bytes). If you just took 32 random bytes and reduced it mod l it would introduce a slight bias. If you took 32 random bytes and threw out any value >= l it would eliminate the bias but it would throw away most values and would take too long. By throwing away everything >= 15*l and then reducing the result mod l you eliminate the bias and, on average, take less than 2 trips through the loop. – President James K. Polk Jul 23 '21 at 12:14

1 Answers1

3

Monero uses edwards25519 as the underlying elliptic curve which it uses to produce EdDSA (Edwards digital signatures), to create transactions on the Monero blockchain.

edwards25519 is a curve which is of composite order, that is, it's not a prime order curve like secp256k1, which is used by Bitcoin.

Owing to this fact, in cryptographic contexts we must work in a prime order subgroup of the curve, so our group is prime, for security reasons.

That subgroup for Monero is ~8-times smaller than the order of the actual curve! Thus the subgroup size is l, i.e. 2^252 + 27742317777372353535851937790883648493

So, there can only be 2^252 + 27742317777372353535851937790883648493 valid public keys for Monero, or anything else using edwards25519

As James K. Polk points out, we want to ensure we stay inside the cyclic subgroup, but not introduce bias in the key material.

Interestingly, I stuck l into sagemath, multiplied it by 15, and indeed it does fit in 256-bits.

255.906890595609 to be exact, 16 times would require 256.000000000000000000000000000000000000005 bits. Math yo.

Woodstock
  • 22,184
  • 15
  • 80
  • 118