# Representations of secret keys on Curve25519

https://tools.ietf.org/html/draft-josefsson-tls-curve25519-06#appendix-A.2 gives the following as a secret key / public key combo:

         S_a = 0x6A2CB91DA5FB77B12A99C0EB872F4CDF
4566B25172C1163C7DA518730A6D0770

P_a = 85 20 F0 09 89 30 A7 54 74 8B 7D DC B4 3E F7 5A
0D BF 3A 0D 26 38 1A F4 EB A4 A9 8E AA 9B 4E 6A


https://tools.ietf.org/html/rfc7748#section-6.1 gives another secret key / public key combo:

Alice's private key, a:
77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
Alice's public key, X25519(a, 9):
8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a


Note how the public key is the same for both but the secret key is not.

sodium_crypto_box _publickey_from_secretkey seems to concur with the RFC but not the IETF Draft:

https://3v4l.org/i61aE

My question is... what is the RFC doing that the draft is not?

I ask because I have an implementation that is giving me the correct public key from the secret key in the IETF Draft and I'm wanting to make it so that I can use that same implementation to get the public key from the secret key in the IETF RFC. But, more than that, I'm just curious.

Thanks!

I'm pretty sure this is an endianness issue. Specifically, taking the S_a value from the Josefsson draft and reversing the order of the bytes (i.e. pairs of hex digits) in it gives:

70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a


which is almost the same as the value of a given in RFC 7748 § 6.1. In fact, XORing the values shows that they differ at only four bits:

  70076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c6a
⊕ 77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
------------------------------------------------------------------
= 0700000000000000000000000000000000000000000000000000000000000040


The remaining difference seems to come down to canonicalization; in particular, RFC 7748 § 5 says (emphasis mine):

For X25519, in order to decode 32 random bytes as an integer scalar, set the three least significant bits of the first byte and the most significant bit of the last to zero, set the second most significant bit of the last byte to 1 and, finally, decode as little-endian.

The S_a value given in the Josefsson draft appears to have this canonicalization correctly pre-applied (after accounting for the byte reversal, that is), whereas the a value in RFC 7748 § 6.1 has the three least significant bits of the first byte set to one, and second most significant bit of the last byte set to zero.

Alternatively, one could simply interpret this as a from RFC 7748 § 6.1 being the raw input sequence of 32 random bytes (written as 32 pairs of hex digits concatenated together), whereas S_a from the Josefsson draft is the corresponding 256-bit number (written as a hexadecimal integer literal) obtained after those random bytes have been canonicalized as described above and decoded in little-endian order.