16

Why/how does monero generate public ed25519 keys without using the standard public key derivation provided by ed25519 libraries?

Inspecting the code for mininero (https://github.com/monero-project/mininero/blob/master/mininero.py), it seems that it doesn't call the ed25519 publickey method to generate public keys. Instead, it defines its own public_key method that does this:

ed25519.encodepoint(ed25519.scalarmultbase(sk))

Mininero's copy of the ed25519 library (from http://ed25519.cr.yp.to/software.html) provides a function called "publickey", which appears to be identical in purpose to the python pynacl and warner/python-ed25519 libraries. All three produce the same resulting public keys. Neither of those other libraries appear to provide an alternative mechanism to derive a public key the way mininero does, nor do they provide any methods to mirror the functionality of mininero's ed25519.encodepoint. This leads me to believe that minero is deriving the public spendkey in an alternative way.

Why doesn't it just use the normal public key methods provided by these libraries? What is the reason for deriving the public key this way?

My hunch is that there is a reason to use a different public key generation system because the intent is to use ed25519 for more than just signing something. For background, I'm writing some monero address generation code as a learning project and I noticed that with the same seed, my public keys are not being generated the same way as valid monero code. My code takes the seed:

awning ramped obedient frown vaults voice dash sunken talent myriad soggy pumpkins buffet vigilant yields foggy wayside rabbits unplugs sarcasm behind lopped tycoon uttered frown

Which produces a spendkey of

77fadbe52830d30438ff68036374c0e3fb755d0d983743bcbfb6a45962f50a09

My code uses python libraries to generate an ed25519 public spend key. I wrote the code to use either https://github.com/pyca/pynacl or https://github.com/warner/python-ed25519 , which both produce the same result. For my spendkey above:

9235e2ecf938d462e79fe993bfdb1717353c87a0b0ed7cb1d58c9692035336d0

This doesn't match what the mininero code and https://xmr.llcoins.net/addresstests.html produce for the same spendkey. They both produce 0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d

Here is a snippet of code that shows the 9235e2ecf938d462e79fe993bfdb1717353c87a0b0ed7cb1d58c9692035336d0 generated public key (ed25519 from warner/python-ed25519):

import ed25519
import binascii
spendkey_hex = b'77fadbe52830d30438ff68036374c0e3fb755d0d983743bcbfb6a45962f50a09'
sk = binascii.unhexlify(spendkey_hex)

def sc_reduce32(n):
    n = int.from_bytes(n, byteorder='little')
    l = (2**252 + 27742317777372353535851937790883648493)
    reduced = n % l
    newbytes = reduced.to_bytes(32, 'little')
    return newbytes


reduced_sk = sc_reduce32(sk)
sec = ed25519.SigningKey(reduced_sk)
pub = sec.get_verifying_key()

print('pub key: ' + str(pub.to_ascii(encoding="hex")))

Here is a snipped of code that produces 9235e2ecf938d462e79fe993bfdb1717353c87a0b0ed7cb1d58c9692035336d0 using ed25519 from the mininero copy of ed25519.py

import ed25519 as ed25519
import binascii
spendkey_hex = b'77fadbe52830d30438ff68036374c0e3fb755d0d983743bcbfb6a45962f50a09'
mininero_pub_spend = ed25519.publickey(binascii.unhexlify(spendkey_hex))
print('pub key: ' + str(binascii.hexlify(mininero_pub_spend)))
19

Monero doesn't use EdDSA, which all of those libraries are specifically set up for. We don't use SHA512 at all, but rather Keccak (~SHA3). We don't use secret keys as seeds like EdDSA does, but rather as scalars. If you look at ed25519.py on L63, you can see what I'm talking about.

Change the function to look like this:

def publickey(sk):
  a = decodeint(sk)
  A = scalarmult(B,a)
  return encodepoint(A)

Now you'll get the correct result: 0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d. I use binascii.hexlify to get the output converted to hex (and also unhexlify to use the secret key as input to publickey(sk)).

  • 1
    Thank you. I've added a snippet above. I have written a version to use the same functions that mininero uses, and I do get the same public key in that case. Public key generation is pretty slow using a pure python route, and I had to make changes to allow it to work in python3. That's why I was looking for a library to help. It doesn't look like one exists. – Ryan Oct 13 '16 at 23:18
  • 1
    I see references occasionally to EdDSA over in reference to monero. If monero doesn't use EdDSA, is there a name for what we do use? I'm guessing this is something based on cryptonote research? – Ryan Oct 13 '16 at 23:22
  • 2
    Monero uses the curve Ed25519, which EdDSA (reference) also uses. For signatures, Monero uses FS ring signatures currently, but will switch to Shen Noether's MLSAGs (along with some others) with RingCT. Monero also uses standard Schnorr sigs (also with Ed25519) for certain (off-blockchain) signing operations. – Luigi Oct 14 '16 at 15:54
  • 1
    Reading more of those linked libraries, it does seem many have equated Ed25519 and EdDSA. It makes sense to me for Ed25519 to refer to the curve (as that's what it is) and for EdDSA to refer to the reference signature scheme using it. pynacl also has a lot of Curve25519 stuff, so maybe that public key you were getting was on that curve? – Luigi Oct 14 '16 at 16:10
  • I can get pynacl, warner/python-ed25519, and mininero's ed25519 to all generate 9235e2ecf938d462e79fe993bfdb1717353c87a0b0ed7cb1d58c9692035336d0 The code snippet didn't format very well in the comment, so I'm adding it to my post above. – Ryan Oct 14 '16 at 16:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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