.\" This file is dual-licensed. Choose whichever you want. .\" .\" The first licence is a regular 2-clause BSD licence. The second licence .\" is the CC-0 from Creative Commons. It is intended to release Monocypher .\" to the public domain. The BSD licence serves as a fallback option. .\" .\" SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0 .\" .\" ---------------------------------------------------------------------------- .\" .\" Copyright (c) 2017-2021, 2022-2023 Loup Vaillant .\" Copyright (c) 2017-2018 Michael Savage .\" Copyright (c) 2017, 2019-2020, 2022 Fabio Scotoni .\" Copyright (c) 2020 Richard Walmsley .\" Copyright (c) 2022 Samuel Lucas .\" All rights reserved. .\" .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions are .\" met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the .\" distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR .\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT .\" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, .\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT .\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" ---------------------------------------------------------------------------- .\" .\" Written in 2017-2023 by Loup Vaillant, Michael Savage, Fabio Scotoni, .\" Richard Walmsley and Samuel Lucas .\" .\" To the extent possible under law, the author(s) have dedicated all copyright .\" and related neighboring rights to this software to the public domain .\" worldwide. This software is distributed without any warranty. .\" .\" You should have received a copy of the CC0 Public Domain Dedication along .\" with this software. If not, see .\" .\" .Dd January 26, 2023 .Dt CRYPTO_X25519 3MONOCYPHER .Os .Sh NAME .Nm crypto_x25519 , .Nm crypto_x25519_public_key , .Nm crypto_x25519_dirty_fast , .Nm crypto_x25519_dirty_small , .Nm crypto_x25519_inverse , .Nm crypto_x25519_to_eddsa .Nd X25519 key exchange (Public Key Cryptography) .Sh SYNOPSIS .In monocypher.h .Ft void .Fo crypto_x25519 .Fa "uint8_t raw_shared_secret[32]" .Fa "const uint8_t your_secret_key[32]" .Fa "const uint8_t their_public_key[32]" .Fc .Ft void .Fo crypto_x25519_public_key .Fa "uint8_t your_public_key[32]" .Fa "const uint8_t your_secret_key[32]" .Fc .Ft void .Fo crypto_x25519_dirty_fast .Fa "uint8_t your_public_key[32]" .Fa "const uint8_t your_secret_key[32]" .Fc .Ft void .Fo crypto_x25519_dirty_small .Fa "uint8_t your_public_key[32]" .Fa "const uint8_t your_secret_key[32]" .Fc .Ft void .Fo crypto_x25519_inverse .Fa "uint8_t blind_salt[32]" .Fa "const uint8_t private_key[32]" .Fa "const uint8_t curve_point[32]" .Fc .Ft void .Fo crypto_x25519_to_eddsa .Fa "uint8_t eddsa[32]" .Fa "const uint8_t x25519[32]" .Fc .Sh DESCRIPTION .Fn crypto_x25519 performs an X25519 key exchange between .Fa your_secret_key and .Fa their_public_key . It is a low-level building block for protocols such as X3DH. .Fn crypto_x25519_public_key Generates a public key from a secret key. The arguments are: .Bl -tag -width Ds .It Fa 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 .Fa your_public_key and .Fa their_public_key using .Xr crypto_blake2b 3monocypher for key derivation. .It Fa your_secret_key A 32-byte secret random number. See .Xr intro 3monocypher for advice about generating random bytes (use the operating system's random number generator). .It Fa your_public_key Your public key, generated by .Fn crypto_x25519_public_key . .It Fa their_public_key The public key of the other party. .El .Pp .Fa raw_shared_secret and .Fa your_secret_key may overlap if your secret is no longer required. .Pp Some protocols, such as some password-authenticated key exchange (PAKE) protocols and oblivious pseudo-random functions (OPRF), may require .Dq 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 .Fn crypto_x25519 to an all-zero buffer using .Xr crypto_verify32 3monocypher , then abort the protocol if the output and the all-zero buffer are equal. .Pp 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 .Xr crypto_blake2b 3monocypher or .Xr crypto_sha512 3monocypher , and the public half with .Xr crypto_eddsa_to_x25519 3monocypher . Or go the other way and implement XEdDSA with the help of .Fn crypto_x25519_to_eddsa . .Sh RETURN VALUES .Fn crypto_x25519 and .Fn crypto_x25519_public_key return nothing. .Sh EXAMPLES The following example assumes the existence of .Fn arc4random_buf , which fills the given buffer with cryptographically secure random bytes. If .Fn arc4random_buf does not exist on your system, see .Xr intro 3monocypher for advice about how to generate cryptographically secure random bytes. .Pp Generate a pair of shared keys with your secret key and their public key (this can help nonce management for full duplex communications). .Bd -literal -offset indent 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); .Ed .Sh INVERSE SCALAR MULTIPLICATION The .Fn crypto_x25519_inverse function performs the scalar multiplication of the multiplicative inverse of a scalar for X25519. It is basically the reverse of .Fn crypto_x25519 : .Bd -literal -offset indent 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 */ .Ed .Pp We can think of it as a scalar division that also clears the cofactor. The arguments are: .Bl -tag -width Ds .It Fa 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. .It Fa 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 .Fa curve_point . .It Fa curve_point The curve point to divide by .Fa private_key . .El .Sh DIRTY PUBLIC KEY GENERATION .Fn crypto_x25519_dirty_fast and .Fn crypto_x25519_dirty_small do the same as .Fn 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 .Em whole curve, rather than just the main prime-order subgroup. Yet they are fully compatible with .Fn crypto_x25519 , and will generate the same shared secrets as regular public keys. .Em 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 .Xr crypto_elligator_rev 3monocypher . .Pp Both functions do the same with different code size and memory characteristics: .Fn crypto_x25519_dirty_fast uses multiple large temporary variables and functions that are normally used internally for .Xr crypto_eddsa_sign 3monocypher . Accordingly, it uses both more stack memory and more code (unless the signing code is already compiled in elsewhere). .Fn crypto_x25519_dirty_small yields the same result with less code, less memory, and about twice as much time as .Fn crypto_x25519_dirty_fast . .Sh CONVERSION TO EDDSA The .Fn 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. .Sh SEE ALSO .Xr intro 3monocypher .Sh STANDARDS This function implements X25519, described in RFC 7748. .Sh HISTORY The .Fn crypto_x25519 and .Fn crypto_x25519_public_key functions first appeared in Monocypher 0.1. The .Fn crypto_x25519_inverse , .Fn crypto_x25519_dirty_fast , .Fn crypto_x25519_dirty_small , and .Fn crypto_x25519_to_eddsa functions first appeared in Monocypher 3.1.0. .Sh CAVEATS Monocypher does not perform any input validation. Any deviation from the specified input and output length ranges results in .Sy undefined behaviour . Make sure your inputs are correct. .Sh SECURITY CONSIDERATIONS If either of the long-term secret keys leaks, it may compromise .Em all past messages . This can be avoided by using protocols that provide forward secrecy, such as the X3DH key agreement protocol. .Pp 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, .D1 BLAKE2b(shared_secret || your_pk || their_pk) using .Xr crypto_blake2b 3monocypher . .Sh 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).