5

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?

jww
  • 103
  • 3
Maxithi
  • 577
  • 2
  • 15

1 Answers1

5

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:

l.toString(16);
"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"

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}
c.toString(16);
"2111111111111111111111111111111125F00AEFB408ADE76923742B6E06E4FE"

Performing mod l will give you:

c.remainder(l).toString(16);
"1111111111111111111111111111110FC3217326E19743AB8FEADF6B41B3D24"

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

a.remainder(l).toString(16);
"1111111111111111111111111111110FC3217326E19743AB8FEADF6B41B3D24"

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

a.remainder(l).add(l).toString(16);
"1111111111111111111111111111111111111111111111111111111111111111"

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:

seed=FEE4066E2B742369E7AD08B4EF0AF02511111111111111111111111111111121

privSpend=243d1bb4f6adfeb83a74196e321732fc10111111111111111111111111111101

JollyMort
  • 19,934
  • 3
  • 46
  • 105
  • 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 Dec 27 '17 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 Dec 27 '17 at 23:15
  • How did you find l=FFFFFFFFFFFFFFFFFFFFFFFF0BCA1161B7C687A4B21172333C8170C38F‌​C05F0D? When does the switch of endianness occur? – Maxithi Dec 28 '17 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: https://github.com/JollyMort/monero-wallet-generator/blob/master/README.md – JollyMort Dec 28 '17 at 13:28
  • I know it's a lot to ask but do you by any chance still have the code used to compute it or the motivation to make a quick runkit? I've been trying for 2 days to understand and achieve the conversion but I keep running into another hex representation of l. I've been searching without success another post explaining this through the [crypto.se] portal. I've tried to check llcoin's source but they link it to the C++ Monero library. – Maxithi Dec 28 '17 at 15:05
  • 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 Dec 28 '17 at 16:57