EVP_PKEY_DERIVE(3)

πŸ“¦ version 4.4.0, in alpine 3.24, arch rolling

πŸ’– EVP_PKEY_DERIVE(3) β€” the "let's share a secret" page πŸ’–

okay bestie so this is from the Library Functions Manual (yes, the EVP_PKEY_DERIVE(3) one), and it's literally all about two keys agreeing on a shared secret without ever telling anyone else what it is. iconic behaviour, honestly. ✨


🌸 NAME 🌸

EVP_PKEY_derive_init, EVP_PKEY_derive_set_peer, EVP_PKEY_CTX_get0_peerkey, EVP_PKEY_derive β€” derive a shared secret using a public key algorithm 🫢


πŸŽ€ SYNOPSIS πŸŽ€

library β€œlibcrypto” #include <openssl/evp.h>

int
EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx);

int
EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peerkey);

EVP_PKEY *
EVP_PKEY_CTX_get0_peerkey(EVP_PKEY_CTX *ctx);

int
EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);

like, four whole functions, all up in libcrypto, all doing the derivation thing πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

So EVP_PKEY_derive_init() is the opening move: it sets up the public key algorithm context ctx for shared secret derivation, using the EVP_PKEY object that's already sitting inside ctx. no cap, the library ships with built-in support for keys whose EVP_PKEY_base_id(3) is any of these:

  • EVP_PKEY_DH
  • EVP_PKEY_EC
  • EVP_PKEY_HKDF
  • EVP_PKEY_X25519

once you've called EVP_PKEY_derive_init(), you can (optionally!) do algorithm-specific control operations to set whatever parameters the operation needs. totally your call, bestie. 🌈

Next up: EVP_PKEY_derive_set_peer() configures ctx to use peerkey, which is normally a public key. ⚠️ heads up though β€” ctx already needs to be initialized with EVP_PKEY_derive_init(), EVP_PKEY_encrypt_init(3), or EVP_PKEY_decrypt_init(3) before this works. And on success? the reference count of peerkey gets incremented by one. so yeah, the caller needs to call EVP_PKEY_free(3) on peerkey once they're done with it β€” even if ctx is still using it. like, yes, both things are true at once, don't @ me. 😭

Then EVP_PKEY_derive() actually derives the shared secret using ctx. here's the vibe check on how to call it:

  • if key is NULL, then the maximum size of the output buffer gets written to the keylen parameter. that's your "how much room do I need" probe. πŸ•΅οΈ
  • if key is not NULL, then before the call, the keylen parameter should contain the length of the key buffer. πŸ“
  • if the call succeeds, the shared secret is written to key, and the amount of data actually written goes to keylen. slay. ✨

and the fun part: EVP_PKEY_derive() can be called more than once on the same context, if you're doing several operations with the same parameters. efficient queen behaviour πŸ‘‘


πŸ”₯ RETURN VALUES πŸ”₯

EVP_PKEY_derive_init(), EVP_PKEY_derive_set_peer(), and EVP_PKEY_derive() return 1 for success, and 0 or a negative value for failure. Specifically, a return value of -2 means the operation is not supported by the public key algorithm. rude, but okay. πŸ’€

For EVP_PKEY_derive_set_peer(), a return value of -1 can for example happen if:

EVP_PKEY_derive() fails with a return value of -1 for example if ctx has not been successfully initialized with EVP_PKEY_derive_init(). πŸ™ˆ

And EVP_PKEY_CTX_get0_peerkey() returns an internal pointer to the peerkey used by ctx, without incrementing its reference count. so don't go freeing that one thinking you own it, bestie, fr fr. 🫢


⭐ EXAMPLES ⭐

derive a shared secret (for example with DH or EC keys) β€” here's the whole thing, verbatim, go off:

#include <openssl/evp.h>
#include <openssl/rsa.h>

EVP_PKEY_CTX *ctx;
unsigned char *skey;
size_t skeylen;
EVP_PKEY *pkey, *peerkey;

/* Assumes that pkey and peerkey have already been set up. */
ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx)
        /* Error occurred */
if (EVP_PKEY_derive_init(ctx) <= 0)
        /* Error */
if (EVP_PKEY_derive_set_peer(ctx, peerkey) <= 0)
        /* Error */

/* Determine buffer length */
if (EVP_PKEY_derive(ctx, NULL, &skeylen) <= 0)
        /* Error */

skey = malloc(skeylen);

if (!skey)
        /* malloc failure */

if (EVP_PKEY_derive(ctx, skey, &skeylen) <= 0)
        /* Error */

/* Shared secret is skey bytes written to buffer skey */

see how it calls EVP_PKEY_derive() twice β€” once with NULL to size the buffer, once for real? that's the move. 🎯


πŸ’œ SEE ALSO πŸ’œ

EVP_PKEY_CTX_new(3), EVP_PKEY_decrypt(3), EVP_PKEY_encrypt(3), EVP_PKEY_sign(3), EVP_PKEY_verify(3), EVP_PKEY_verify_recover(3), X25519(3) β€” go read those too, iykyk πŸ“š


πŸ‘‘ HISTORY πŸ‘‘

EVP_PKEY_derive_init(), EVP_PKEY_derive_set_peer(), EVP_PKEY_CTX_get0_peerkey(), and EVP_PKEY_derive() first appeared in OpenSSL 1.0.0 and have been available since OpenBSD 4.9. vintage. πŸ•°οΈ


and that's the whole page, bestie β€” no notes, mwah πŸ’‹

signing off from the Debian edition, dated June 8, 2025 β€” this was EVP_PKEY_DERIVE(3), and she ate. πŸ“βœ¨

πŸ“¦ where this page comes from