- elliptic-curves diffie-hellman libsodium x25519
- Updated Thu, 02 Jun 2022 20:29:50 GMT

https://datatracker.ietf.org/doc/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://www.rfc-editor.org/rfc/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:

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 1and, 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.

External links referenced by this document: