In other words, is it a bad idea to simply do this?

signingkey = crypto_random_bytes();
verifykey = Ed25519.verifykey_from_signingkey(signingkey);

Are there weak keys that standard libraries protect you from?


There are a few different formats for the standard parts of an Ed25519 private key, which are usually stored as 32-byte or 64-byte strings, so you need to pay attention to the choices made by the system you use it with. Everyone agrees on how to compute an Ed25519 signature given a secret scalar, which can be a uniform random 256-bit integer, and a PRF secret, which is a uniform random 256-bit string, but the scalar and PRF secret are stored or derived differently in different contexts.

In some exotic protocols where you share a secret scalar between Ed25519 and the X25519 Diffie–Hellman function, the secret scalar may need to be clamped for DH security. Some libraries do this automatically, particularly those that use a 32-byte pre-master secret; others do not, particularly those that involve hierarchical key derivation.

For example, in libsodium, crypto_sign_keypair generates a 64-byte secret key consisting of the 32-byte pre-master secret seed and the 32-byte public key, with clamping of the scalar. crypto_sign_keypair_seed derives that same 64-byte secret key from a 32-byte pre-master secret seed.

For the most part, an Ed25519 private key—meaning secret scalar and PRF secret—really is just a uniform random string of either 32 or 64 bytes. But different libraries may have slightly different rules, so pay attention to the rules of the library.

  • $\begingroup$ As I recall libsodium's implementation it's not really a random 64-byte string, rather the "secret key" is a combination of the 32-byte representation of the clamped secret key value along with the 32-byte representation of the public key (the Y coordinate as I recall). Nevertheless, the right way to create a key is either to use crypto_sign_keypair() to generate a new random key or crypto_sign_seed_keypair() to generate based on a 32-byte seed value. The result will be an appropriate clamped value. $\endgroup$ – jadb Jul 16 '18 at 23:29
  • $\begingroup$ @jadb Not quite. The first 32 bytes are the (clamped) scalar. The last 32 bytes are the PRF key. The public key—which is the encoding of a point on the curve—is stored separately. You could generate a private key in this format just as well by generating 64 bytes uniformly at random. (Clamping is not necessary for Ed25519 security.) $\endgroup$ – Squeamish Ossifrage Jul 16 '18 at 23:49
  • $\begingroup$ I should add, however, that I don't recommend just blithely generating 64 bytes uniformly at random. I can't rule out the possibility that an implementation might assume the scalar is clamped and fail in some way, or disagree with another implementation that does not assume the scalar is clamped. $\endgroup$ – Squeamish Ossifrage Jul 17 '18 at 0:24
  • $\begingroup$ So, what's the difference between the public key and the PRF key? reading github.com/jedisct1/libsodium/blob/master/src/libsodium/… it looks like the last 32 bytes are the result of a scalar multiplication and then converting the y coordinate to bytes. Or that's how I read it. Am I missing something? $\endgroup$ – jadb Jul 17 '18 at 0:33
  • 1
    $\begingroup$ @R.. Correct. See the cited answer for more details. Everyone agrees on how to compute the Ed25519 signature for a message given a secret scalar and a PRF secret, but how to store or derive the secret scalar and PRF secret varies from system to system, for specific technical reasons depending on what they're doing. $\endgroup$ – Squeamish Ossifrage Jul 17 '18 at 1:24

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.