EVP_PKEY_DERIVE(3)
π 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_DHEVP_PKEY_ECEVP_PKEY_HKDFEVP_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
keyisNULL, then the maximum size of the output buffer gets written to thekeylenparameter. that's your "how much room do I need" probe. π΅οΈ - if
keyis notNULL, then before the call, thekeylenparameter 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 tokeylen. 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:
ctxis not properly initialized- it does not contain an
EVP_PKEYthat can be retrieved withEVP_PKEY_CTX_get0_pkey(3) - the
EVP_PKEY_id(3)of both keys mismatch EVP_PKEY_cmp_parameters(3)reports mismatching key parameters
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
- alpine 3.24: libressl-doc 4.3.1-r0 Β· custom
- arch rolling: libressl 4.4.0-1 Β· ISC AND custom:OpenSSL
- Copyright (c) 2023 Ingo Schwarze <schwarze@openbsd.org>
- Copyright (c) 2006, 2009, 2013, 2018 The OpenSSL Project
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms