CRYPTO_X25519(3MONOCYPHER) | 3MONOCYPHER | CRYPTO_X25519(3MONOCYPHER) |

# NAME

X25519 key exchange (Public Key Cryptography)# SYNOPSIS

```
#include
<monocypher.h>
```

`void`

`crypto_x25519`

(`uint8_t
raw_shared_secret[32]`, `const uint8_t
your_secret_key[32]`, `const uint8_t
their_public_key[32]`);

`void`

`crypto_x25519_public_key`

(`uint8_t
your_public_key[32]`, `const uint8_t
your_secret_key[32]`);

`void`

`crypto_x25519_dirty_fast`

(`uint8_t
your_public_key[32]`, `const uint8_t
your_secret_key[32]`);

`void`

`crypto_x25519_dirty_small`

(`uint8_t
your_public_key[32]`, `const uint8_t
your_secret_key[32]`);

`void`

`crypto_x25519_inverse`

(`uint8_t
blind_salt[32]`, `const uint8_t private_key[32]`,
`const uint8_t curve_point[32]`);

`void`

`crypto_x25519_to_eddsa`

(`uint8_t
eddsa[32]`, `const uint8_t x25519[32]`);

# DESCRIPTION

`crypto_x25519`

()
performs an X25519 key exchange between
`your_secret_key` and
`their_public_key`. It is a low-level building block for
protocols such as X3DH.
`crypto_x25519_public_key`

()
Generates a public key from a secret key. The arguments are:

`raw_shared_secret`- The shared secret, known only to those who know a relevant secret key
(yours or theirs). It is not cryptographically random. Do not use it
directly as a key. Hash it concatenated with
`your_public_key`and`their_public_key`using crypto_blake2b() for key derivation. `your_secret_key`- A 32-byte secret random number. See intro() for advice about generating random bytes (use the operating system's random number generator).
`your_public_key`- Your public key, generated by
`crypto_x25519_public_key`

(). `their_public_key`- The public key of the other party.

`raw_shared_secret` and
`your_secret_key` may overlap if your secret is no
longer required.

Some protocols, such as some
password-authenticated key exchange (PAKE) protocols and oblivious
pseudo-random functions (OPRF), may require “contributory”
behaviour, which ensures that no untrusted party forces the shared secret to
a known constant. If a protocol requires contributory behaviour, compare the
output of
`crypto_x25519`

()
to an all-zero buffer using
crypto_verify32(), then
abort the protocol if the output and the all-zero buffer are equal.

Do not use the same secret key for
both key exchanges and signatures. The public keys are different and
revealing both may leak information. If there really is no room to store or
derive two different secret keys, consider generating a key pair for
signatures and then converting the private half with
crypto_blake2b() or
crypto_sha512(), and the
public half with
crypto_eddsa_to_x25519().
Or go the other way and implement XEdDSA with the help of
`crypto_x25519_to_eddsa`

().

# RETURN VALUES

`crypto_x25519`

() and
`crypto_x25519_public_key`

() return nothing.

# EXAMPLES

The following example assumes the existence of
`arc4random_buf`

(), which fills the given buffer with
cryptographically secure random bytes. If
`arc4random_buf`

() does not exist on your system, see
intro() for advice about how to
generate cryptographically secure random bytes.

Generate a pair of shared keys with your secret key and their public key (this can help nonce management for full duplex communications).

const uint8_t their_pk [32]; /* Their public key */ uint8_t your_sk [32]; /* Your secret key */ uint8_t your_pk [32]; /* Your public key */ uint8_t shared_secret[32]; /* Shared secret (NOT a key) */ arc4random_buf(your_sk, 32); crypto_x25519_public_key(your_pk, your_sk); crypto_x25519(shared_secret, your_sk, their_pk); /* Wipe secrets if they are no longer needed */ crypto_wipe(your_sk, 32); uint8_t shared_keys[64]; /* Two shared session keys */ crypto_blake2b_ctx ctx; crypto_blake2b_init (&ctx, 64); crypto_blake2b_update(&ctx, shared_secret, 32); crypto_blake2b_update(&ctx, your_pk , 32); crypto_blake2b_update(&ctx, their_pk , 32); crypto_blake2b_final (&ctx, shared_keys); const uint8_t *key_1 = shared_keys; /* Shared key 1 */ const uint8_t *key_2 = shared_keys + 32; /* Shared key 2 */ /* Wipe secrets if they are no longer needed */ crypto_wipe(shared_secret, 32);

# INVERSE SCALAR MULTIPLICATION

The
`crypto_x25519_inverse`

()
function performs the scalar multiplication of the multiplicative inverse of
a scalar for X25519. It is basically the reverse of
`crypto_x25519`

():

uint8_t b [32]; /* Random scalar */ uint8_t base [32]; /* Point on the prime order subgroup */ crypto_x25519_public_key(base, b); uint8_t private_key[32]; /* Random secret key */ uint8_t curve_point[32]; /* Point on the prime order subgroup */ uint8_t blind_salt [32]; crypto_x25519(curve_point, private_key, base); crypto_x25519_inverse(blind_salt, private_key, curve_point); assert(memcmp(blind_salt, base, 32) == 0); /* blind_salt == base */

We can think of it as a scalar division that also clears the cofactor. The arguments are:

`blind_salt`- The output point. Guaranteed to be on the prime order subgroup. The only possible low order result is a buffer full of zeroes.
`private_key`- The private key (scalar) to use. It is clamped, inverted modulo the prime
order of Curve25519, cleared of its cofactor, and finally used to multiply
`curve_point`. `curve_point`- The curve point to divide by
`private_key`.

# DIRTY PUBLIC KEY GENERATION

`crypto_x25519_dirty_fast`

()
and
`crypto_x25519_dirty_small`

()
do the same as `crypto_x25519_public_key`

(), with one
key difference: they also add a low order point to the public key. Such
public keys can be on the
*whole*
curve, rather than just the main prime-order subgroup. Yet they are fully
compatible with `crypto_x25519`

(), and will generate
the same shared secrets as regular public keys.
*They do however
leak information about the private key*. Only use them for ephemeral
keys that need to be hidden as random noise, in conjunction with
crypto_elligator_rev().

Both functions do the same with
different code size and memory characteristics:
`crypto_x25519_dirty_fast`

()
uses multiple large temporary variables and functions that are normally used
internally for
crypto_eddsa_sign().
Accordingly, it uses both more stack memory and more code (unless the
signing code is already compiled in elsewhere).
`crypto_x25519_dirty_small`

()
yields the same result with less code, less memory, and about twice as much
time as `crypto_x25519_dirty_fast`

().

# CONVERSION TO EDDSA

The `crypto_x25519_to_eddsa`

() converts an
X25519 public key to the corresponding EdDSA public key. The sign bit of the
resulting EdDSa key is set to zero (positive). This can be used to implement
the XEdDSA protocol from Signal.

# SEE ALSO

# STANDARDS

This function implements X25519, described in RFC 7748.

# HISTORY

The `crypto_x25519`

() and
`crypto_x25519_public_key`

() functions first appeared
in Monocypher 0.1. The `crypto_x25519_inverse`

(),
`crypto_x25519_dirty_fast`

(),
`crypto_x25519_dirty_small`

(), and
`crypto_x25519_to_eddsa`

() functions first appeared in
Monocypher 3.1.0.

# CAVEATS

Monocypher does not perform any input validation. Any deviation
from the specified input and output length ranges results in
**undefined
behaviour**. Make sure your inputs are correct.

# SECURITY CONSIDERATIONS

If either of the long-term secret keys leaks, it may compromise
*all past
messages*. This can be avoided by using protocols that provide
forward secrecy, such as the X3DH key agreement protocol.

Many (private, public) key pairs produce the same shared secret. Therefore, not including the public keys in the key derivation can lead to subtle vulnerabilities. This can be avoided by hashing the shared secret concatenated with both public keys. For example,

# IMPLEMENTATION DETAILS

The most significant bit of the public key is systematically ignored. It is not needed because every public key should be smaller than 2^255-19, which fits in 255 bits. If another implementation of X25519 gives you a key that is not fully reduced and has its high bit set, the computation will fail. On the other hand, it also means you may use this bit for other purposes (such as parity flipping for Ed25519 compatibility).

January 26, 2023 | Debian |