X25519(3)

πŸ“¦ version 4.1.0, in opensuse-leap 16.0

πŸ’– X25519(3) β€” bestie's guide to Curve25519 ✨

Okay so this is the X25519(3) manual page, and honestly? It's giving crypto baddie energy. Buckle up, we're doing elliptic curves πŸŽ€


🌸 NAME 🌸

X25519, X25519_keypair, ED25519_keypair, ED25519_sign, ED25519_verify β€” Elliptic Curve Diffie-Hellman and signature primitives based on Curve25519 πŸ¦‹

Like, the whole squad. Diffie-Hellman and signatures, all riding on Curve25519. Periodt.


✨ SYNOPSIS ✨

#include <openssl/curve25519.h>

int
X25519(uint8_t out_shared_key[X25519_KEY_LENGTH],
    const uint8_t private_key[X25519_KEY_LENGTH],
    const uint8_t peer_public_value[X25519_KEY_LENGTH]);

void
X25519_keypair(uint8_t out_public_value[X25519_KEY_LENGTH],
    uint8_t out_private_key[X25519_KEY_LENGTH]);

void
ED25519_keypair(uint8_t out_public_key[ED25519_PUBLIC_KEY_LENGTH],
    uint8_t out_private_key[ED25519_PRIVATE_KEY_LENGTH]);

int
ED25519_sign(uint8_t *out_sig, const uint8_t *message,
    size_t message_len,
    const uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH],
    const uint8_t private_key_seed[ED25519_PRIVATE_KEY_LENGTH]);

int
ED25519_verify(const uint8_t *message, size_t message_len,
    const uint8_t signature[ED25519_SIGNATURE_LENGTH],
    const uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH]);

Slay, look at all those uint8_t arrays bestie πŸ’…


πŸ‘‘ DESCRIPTION πŸ‘‘

🧚 Curve25519, the main character

Curve25519 is an elliptic curve over a prime field specified in RFC 7748 section 4.1. The prime field is defined by the prime number 2^255 - 19. Yes, that's literally the name. Iconic behavior, no notes πŸ”₯

πŸ’œ X25519 β€” the Diffie-Hellman one

X25519 is the Diffie-Hellman primitive built from Curve25519 as described in RFC 7748 section 5. And if you want the intended use case, that's section 6.1, which describes using it in an Elliptic Curve Diffie-Hellman (ECDH) protocol. iykyk πŸ’«

X25519() writes a shared key to out_shared_key that is calculated from the given private_key and the peer_public_value by scalar multiplication.

⚠️ Big don't, bestie: do not use the shared key directly. Instead, use a key derivation function and also include the two public values as inputs. Like, that's the whole vibe check. Don't skip it 😭

πŸ¦‹ X25519_keypair β€” fresh keys, fresh me

X25519_keypair() sets out_public_value and out_private_key to a freshly generated public/private key pair. Here's the tea β˜•:

  1. First, the out_private_key is generated with arc4random_buf(3).
  2. Then, the opposite of the masking described in RFC 7748 section 5 is applied to it β€” to make sure that the generated private key is never correctly masked.
  3. The purpose? To cause incorrect implementations on the peer side to consistently fail πŸ’€
  4. Correct implementations will decode the key correctly even when it is not correctly masked. So like, the good ones are fine. Chill.
  5. Finally, the out_public_value is calculated from the out_private_key by multiplying it with the Montgomery base point uint8_t u[32] = {9}.

The size of a public and private key is X25519_KEY_LENGTH = 32 bytes each. Cute and compact πŸŽ€

🌈 Ed25519 β€” now with signatures

Ed25519 is a signature scheme using a twisted Edwards curve that is birationally equivalent to Curve25519. Twisted! Edwards! Birationally equivalent! She's got range πŸ’…

ED25519_keypair() sets out_public_key and out_private_key to a freshly generated public/private key pair. First, the out_private_key is generated with arc4random_buf(3). Then, the out_public_key is calculated from the private key. Simple as that ✨

ED25519_sign() signs the message of message_len bytes using the public_key and the private_key and writes the signature to out_sig.

ED25519_verify() checks that signing the message of message_len bytes using the public_key would indeed result in the given signature. It's giving "prove it" energy πŸ™ˆ

The sizes of a public and private keys are ED25519_PUBLIC_KEY_LENGTH and ED25519_PRIVATE_KEY_LENGTH, which are both 32 bytes, and the size of a signature is ED25519_SIGNATURE_LENGTH = 64 bytes. Locked in πŸ”’


πŸ’€ RETURN VALUES πŸ’€

  • X25519() and ED25519_sign() return 1 on success or 0 on error.
  • X25519() can fail if the input is a point of small order. So like, watch out for tiny points πŸ‘€
  • ED25519_sign() always succeeds in LibreSSL, but the API reserves the return value 0 for memory allocation failure. So if you see a 0, it's an OOM moment, not a signing moment 😭
  • ED25519_verify() returns 1 if the signature is valid or 0 otherwise. Valid or bust πŸ’–

🫢 SEE ALSO 🫢

Also, the academic besties πŸ“š:

  • Daniel J. Bernstein, A state-of-the-art Diffie-Hellman function: How do I use Curve25519 in my own software?, <https://cr.yp.to/ecdh.html>.
  • Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, and Bo-Yin Yang, β€œHigh-Speed High-Security Signatures”, Cryptographic Hardware and Embedded Systems β€” CHES 2011, Springer, Lecture Notes in Computer Science, vol 6917, <https://doi.org/10.1007/978-3-642-23951-9_9>, Nara, Japan, September 29, 2011.

The way they cited a whole squad of authors 😭 we stan collaboration 🌟


⭐ STANDARDS ⭐

  • RFC 7748: Elliptic Curves for Security
  • RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)

Certified, standardized, and honestly? Flawless πŸ’…


and that's the whole page bestie, go forth and curve your 25519s πŸ¦‹πŸ’–

this cutie dropped from Debian, dated December 15, 2022 β€” so you know it's the real X25519(3) deal, mwah πŸŽ€

πŸ“¦ where this page comes from