6

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?

Daffy
  • 2,389
  • 17
  • 29

1 Answers1

9

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.

Squeamish Ossifrage
  • 48,392
  • 3
  • 116
  • 223
  • 1
    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. – jadb Jul 16 '18 at 23:29
  • @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.) – Squeamish Ossifrage Jul 16 '18 at 23:49
  • 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. – Squeamish Ossifrage Jul 17 '18 at 00:24
  • So, what's the difference between the public key and the PRF key? reading https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c 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? – jadb Jul 17 '18 at 00:33
  • This answer leaves me somewhat confused saying it "depends on the library's rules", since the cryptographic primitive is well-defined and deterministic, and should work with public and private keys that were generated by a different implementation than its own. Isn't it more a matter of how the primitive is used in larger protocols, or something like that? – R.. GitHub STOP HELPING ICE Jul 17 '18 at 01:08
  • @jadb Sorry, we were both misreading. In libsodium, the first 32 bytes of the private key are the pre-master secret (or ‘seed’), not the secret scalar clamped or otherwise, and the last 32 bytes are the encoding of the public key as the y coordinate with the sign of the x coordinate. (In NaCl's draft signature scheme predating Ed25519, the private key is as I said: 32 bytes of clamped scalar followed by 32 bytes of PRF key.) – Squeamish Ossifrage Jul 17 '18 at 01:09
  • @R.. Since making a signature involves hashing the message together with the public key, it is convenient to store the public key alongside the secrets, even though it can be derived from the 32-byte secret scalar or from the 32-byte secret seed. So libsodium stores the 32-byte secret seed and the 32-byte public key together as a ‘private key’. The 32-byte secret seed is also what RFC 8032 describes as the ‘secret key’. – Squeamish Ossifrage Jul 17 '18 at 01:16
  • OK, so it's more like "the format of the data structure people are calling 'the private key' differs between implementations" (and would require conversion to move from one to another)? – R.. GitHub STOP HELPING ICE Jul 17 '18 at 01:20
  • 1
    @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. – Squeamish Ossifrage Jul 17 '18 at 01:24
  • Further, it seems the PRF secret part isn't even part of the secret key, in the sense that it would still work if you substituted any other value for it. Rather it's just a substitute for having a working csprng for the nonce generation. – R.. GitHub STOP HELPING ICE Jul 17 '18 at 02:41
  • @R.. The PRF secret is part of the definition of Ed25519. It's true that a verifier can't verify it, but an implementor can use the standard test vectors to check for conformance, and if the implementor follows the standard, the signer need not worry about having an entropy source. Note that the PRF is in effect used as a CSPRNG. Deviating from that standard exposes you to errors that have been repeatedly exploited over the years with serious consequences for millions of people, including subjecting all Sony PS3 users to malicious firmware updates over the internet. – Squeamish Ossifrage Jul 17 '18 at 03:03