While informing myself on the inner-working of Monero, I stumbled on Curve25519.

The CryptoNote White Paper states:

l: a prime order of the base point; l = 2^252 + 27742317777372353535851937790883648493;
private ec-key is a standard elliptic curve private key: a number a ∈ [1, l − 1];

   Another Stack Exchange answer states the following:

All you need protocol-wise is a private spend key and a private view key, where a private key is just some number smaller than l where l is a prime order of the EC curve basepoint l=2^252 + 27742317777372353535851937790883648493. Anything bigger than that will get wrapped around ie a and a+l are equivalent.

From my understanding this means that a private key a has the same derived public key as private key a+l.

I've however been unable to reproduce the said "wrapping".

Using a very random a=0x1111111111111111111111111111111111111111111111111111111111111111 I calculated a+l=0x2111111111111111111111111111111125f00aefb408ade76923742b6e06e4fe.

I then tried to use a and a+l on llcoins as Hexadecimal Seed, as Private Spend Key or as Private View Key. They never generated the same results.

Does the wrapping occurring on a ,when greater or equal to l, occur at another step? Does it only occur sometimes?

1 Answer 1


It will get wrapped around because it's later passed through sc_reduce32 function which performs mod l operation on the input.

See below example using silent Matt's big int library which is loaded with llcoins. Easiest to just open the developer tab and type stuff into JS console.

To calculate 2^252 + 27742317777372353535851937790883648493:

l = JSBigInt(2).pow(252).add(JSBigInt.parse("27742317777372353535851937790883648493",10));

To print out the number in base16:


That's the l. If you swap byes and feed it as seed to llcoins, you should get 0x0 as seed. Try inputting EDD3F55C1A631258D69CF7A2DEF9DE1400000000000000000000000000000010 (little endian) as seed and you'll get 0000000000000000000000000000000000000000000000000000000000000000 as private key.

Now, we can also do the addition from your example:

a = JSBigInt.parse("0x1111111111111111111111111111111111111111111111111111111111111111");
BigInteger {_d: Array(11), _s: 1}
c = l.add(a);
BigInteger {_d: Array(11), _s: 1}

Performing mod l will give you:


observe how you don't get the value of a because a itself is bigger than l:


so adding l to this will give you the original number:


If you swap endian of c and feed it as seed to llcoins, you'll get the same remainder but with little endian byte order:



  • Add which step? I discovered that using the a+l as seed is the same as using the private key generated by a+l as seed. However this private key is not the mod l of a+l.
    – Maxithi
    Commented Dec 27, 2017 at 23:00
  • 1
    It should be. If you're doing it by other means, pay attention to byte endianness, though. Using your calc, l=FFFFFFFFFFFFFFFFFFFFFFFF0BCA1161B7C687A4B21172333C8170C38FC05F0D, l+a=11111111111111111111111101cdb2272c8d798b5c32283444d9281d4a0d1701e and mod l = 1111111111111111111111111111111111111111111111111111111111111111.
    – JollyMort
    Commented Dec 27, 2017 at 23:15
  • How did you find l=FFFFFFFFFFFFFFFFFFFFFFFF0BCA1161B7C687A4B21172333C8170C38F‌​C05F0D? When does the switch of endianness occur?
    – Maxithi
    Commented Dec 28, 2017 at 13:12
  • 1
    I used JS BigInt lib to play with this a while ago and computed hex value of l from whitepaper definition. See my code here: github.com/JollyMort/monero-wallet-generator/blob/master/…
    – JollyMort
    Commented Dec 28, 2017 at 13:28
  • 1
    Check my updated answer with some examples, I have no idea how I got the FFFFFFFFFFFFFFFFFFFFFFFF0BCA1161B7C687A4B21172333C8170C38F‌​C05F0D, must have messed up somewhere. The updated answer has correct values :)
    – JollyMort
    Commented Dec 28, 2017 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.