EVP_KDF-IKEV2KDF(7ssl)

πŸ“¦ version 4.1.0, in nixos 26.05, nixpkgs unstable

πŸ’… EVP_KDF-IKEV2KDF(7ssl) β€” OpenSSL's IKEv2 KDF, bestie πŸ’…

okay so this is the OpenSSL manual page for EVP_KDF-IKEV2KDF(7ssl), and it's giving key-derivation main character energy ✨ no cap.

πŸ’– NAME πŸ’–

EVP_KDF-IKEV2KDF β€” the IKEV2KDF EVP_KDF implementation, periodt πŸ’…

✨ DESCRIPTION ✨

Support for computing the IKEV2KDF KDF through the EVP_KDF API. slay.

The EVP_KDF-IKEV2KDF algorithm implements the IKEv2 key derivation function. It is defined in RFC 7296, sections 2.13, 2.14, 2.17, 2.18 and is used by the Internet Key Exchange version 2 (IKEv2) protocol to derive various keys from the shared secret established during the Diffie-Hellman (DH) exchange. RFC 4753 specifies additional ECDH groups for use with IKEv2. The key derivation requires several inputs including the hash function, the shared secret (g^ir), nonces from both parties, the Security Parameter Index (SPI) values from both parties, and new shared secret (new g^ir). SA refers to the Security Association established between the two parties. iykyk πŸ¦‹

πŸͺͺ Identity πŸͺͺ

"IKEV2KDF" is the name for this implementation; it can be used with the EVP_KDF_fetch() function. iconic ✨

πŸŽ€ Supported parameters πŸŽ€

The supported parameters are:

  • "properties" (OSSL_KDF_PARAM_PROPERTIES) <UTF8 string> and "digest" (OSSL_KDF_PARAM_DIGEST) <UTF8 string> β€” these parameters work as described in "PARAMETERS" in EVP_KDF(3). The digest parameter must be set to one of "SHA1", "SHA224", "SHA256", "SHA384", or "SHA512" for the IKEV2KDF implementation. If a value is already set, the contents are replaced.
  • "secret" (OSSL_KDF_PARAM_SECRET) <octet string> β€” this parameter sets the shared secret (g^ir) used for generating the SEEDKEY, or the new shared secret (new g^ir) used for deriving DKM(Child_DH) or REKEY. The shared secret length is in the range of 8 to 1024 bytes as specified in the NIST ACVP draft (https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ikev2.html). If a value is already set, the contents are replaced. obsessed with the precision here πŸŽ€
  • "ni" (OSSL_KDF_PARAM_IKEV2_NI) <octet string> β€” this parameter sets the initiator's nonce (Ni) value for the KDF. The nonce length is in the range of 8 to 256 bytes as specified in NIST ACVP draft (https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ikev2.html). If a value is already set, the contents are replaced.
  • "nr" (OSSL_KDF_PARAM_IKEV2_NR) <octet string> β€” this parameter sets the responder's nonce (Nr) value for the KDF. The nonce length is in the range of 8 to 256 bytes as specified in NIST ACVP draft (https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ikev2.html). If a value is already set, the contents are replaced.
  • "spii" (OSSL_KDF_PARAM_IKEV2_SPII) <octet string> β€” this parameter sets the initiator's Security Parameter Index (SPIi) value for the KDF. If a value is already set, the contents are replaced.
  • "spir" (OSSL_KDF_PARAM_IKEV2_SPIR) <octet string> β€” this parameter sets the responder's Security Parameter Index (SPIr) value for the KDF. If a value is already set, the contents are replaced.
  • "seedkey" (OSSL_KDF_PARAM_SEEDKEY) <octet string> β€” this parameter sets the SEEDKEY used for deriving DKM(Initial SA). If a value is already set, the contents are replaced.
  • "key" (OSSL_KDF_PARAM_KEY) <octet string> β€” this parameter sets the SK_d used for deriving DKM(Child_SA) or REKEY. If a value is already set, the contents are replaced.
  • "mode" (OSSL_KDF_PARAM_MODE) <int> β€” this parameter sets the IKEv2 KDF mode. The IKEV2KDF implementation supports three different modes of operation:
    • EVP_KDF_IKEV2_MODE_GEN β€” it generates the SKEYSEED used in deriving key material. It is the default mode if the "mode" parameter is not set. It takes the shared secret (g^ir) and nonces (Ni and Nr) as input and generates the SKEYSEED.
    • EVP_KDF_IKEV2_MODE_DKM β€” it operates in three stages, based on the input parameter sets.
      • It first performs initial key derivation: it derives the initial set of keys (SK_d, SK_ai, SK_ar, SK_ei, SK_er, SK_pi, SK_pr) using SKEYSEED generated in "EVP_KDF_IKEV2_MODE_GEN", the nonces (from both sides), and security parameter index (SPI) values (from both sides).
      • Then, it derives keying material for the Child_SA, using the SK_d as input along with new nonces.
      • Finally, it derives keying material for the Child_DH, using the SK_d as input along with new nonces and new shared secret (new g^ir).
    • EVP_KDF_IKEV2_MODE_REKEY β€” it derives a new SKEYSEED when rekeying the IKE SA, using the existing SK_d as input along with new nonces and new shared secret (new g^ir).
  • "fips-indicator" (OSSL_KDF_PARAM_FIPS_APPROVED_INDICATOR) <integer> β€” the OpenSSL FIPS provider returns 1 if the operation is FIPS approved, or 0 otherwise. See "FIPS indicators" in fips_module(7) for more information. vibe check: approved or not approved, bestie πŸ’™

πŸ’Œ NOTES πŸ’Œ

A context for IKEV2KDF can be obtained by calling:

EVP_KDF *kdf = EVP_KDF_fetch(NULL, "IKEV2KDF", NULL);
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);

The output length for IKEV2KDF can be specified by the caller based on the specific key being derived (e.g., SK_d, SK_ai, SK_ar, SK_ei, SK_er, SK_pi, SK_pr). Different keys may require different lengths depending on the cryptographic algorithms being used. literally so flexible 🫢

✨ EXAMPLES ✨

Okay bestie, this is the fun part β€” actual code you can copy-paste-slay into your project πŸŽ€ Every example below is doing a real IKEv2 thing, so like, pay attention, iykyk.

πŸ’– Example of SEEDKEY generation

This one derives a 32-byte SEEDKEY using SHA-256, with the appropriate shared secret and nonces. It's giving "first step of the whole vibe" energy:

EVP_KDF *kdf = NULL;
EVP_KDF_CTX *kctx = NULL;
unsigned char secret[32] = "0123456789abcdef0123456789abcdef"; /* g^ir */
unsigned char ni[8] = "01234567";
unsigned char nr[8] = "89abcdef";
int mode = EVP_KDF_IKEV2_MODE_GEN;
unsigned char out[32];
size_t outlen = sizeof(out);
OSSL_PARAM params[6], *p = params;

kdf = EVP_KDF_fetch(NULL, "IKEV2KDF", NULL);
kctx = EVP_KDF_CTX_new(kdf);
if (!kctx) {
    /* Error handling */
}

*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
    SN_sha256, strlen(SN_sha256));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET,
    secret, sizeof(secret));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NI,
    ni, sizeof(ni));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NR,
    nr, sizeof(nr));
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
*p = OSSL_PARAM_construct_end();
if (EVP_KDF_derive(kctx, out, outlen, params) <= 0) {
    /* Error */
    ;
}
EVP_KDF_CTX_free(kctx);
EVP_KDF_free(kdf);

🌸 Example of deriving key material DKM(Initial SA)

Now we're deriving key material for the Initial SA, using skeyseed as the seed πŸ¦‹ Notice the param array is params[8] here because there's more going on β€” we got SPIi and SPIr in the mix:

EVP_KDF *kdf = NULL;
EVP_KDF_CTX *kctx = NULL;
unsigned char ni[8] = "01234567";
unsigned char nr[8] = "89abcdef";
unsigned char spii[8] = "01234567";
unsigned char spir[8] = "89abcdef";
unsigned char skeyseed[32] = "0123456789abcdef0123456789abcdef";
int mode = EVP_KDF_IKEV2_MODE_DKM;
unsigned char out[224];
OSSL_PARAM params[8], *p = params;

kdf = EVP_KDF_fetch(NULL, "IKEV2KDF", NULL);
kctx = EVP_KDF_CTX_new(kdf);
if (!kctx) {
    /* Error handling */
}

*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
    SN_sha256, strlen(SN_sha256));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED,
    skeyseed, sizeof(skeyseed));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NI,
    ni, sizeof(ni));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NR,
    nr, sizeof(nr));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_SPII,
    spii, sizeof(spii));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_SPIR,
    spir, sizeof(spir));
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
*p = OSSL_PARAM_construct_end();
if (EVP_KDF_derive(kctx, out, sizeof(out), params) <= 0) {
    /* Error handling */
    ;
}
EVP_KDF_CTX_free(kctx);
EVP_KDF_free(kdf);

πŸŽ€ Example of generating DKM(Child_SA)

Child SA time!! πŸ‘‘ This one's leaner β€” params[6], no SPIs, just sk_d doing its main character thing:

EVP_KDF *kdf = NULL;
EVP_KDF_CTX *kctx = NULL;
unsigned char ni[8] = "01234567";
unsigned char nr[8] = "89abcdef";
unsigned char sk_d[32] = "0123456789abcdef0123456789abcdef";
int mode = EVP_KDF_IKEV2_MODE_DKM;
unsigned char out[224];
OSSL_PARAM params[6], *p = params;

kdf = EVP_KDF_fetch(NULL, "IKEV2KDF", NULL);
kctx = EVP_KDF_CTX_new(kdf);
if (!kctx) {
    /* Error handling */
}

*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
    SN_sha256, strlen(SN_sha256));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
    sk_d, sizeof(sk_d));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NI,
    ni, sizeof(ni));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NR,
    nr, sizeof(nr));
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
*p = OSSL_PARAM_construct_end();
if (EVP_KDF_derive(kctx, out, sizeof(out), params) <= 0) {
    /* Error handling */
    ;
}
EVP_KDF_CTX_free(kctx);
EVP_KDF_free(kdf);

πŸ¦‹ Example of generating DKM(Child_DH)

Same EVP_KDF_IKEV2_MODE_DKM mode, but now we're pulling in a new_secret (a fresh g^ir) alongside sk_d β€” so params[7] πŸ”₯:

EVP_KDF *kdf = NULL;
EVP_KDF_CTX *kctx = NULL;
unsigned char ni[8] = "01234567";
unsigned char nr[8] = "89abcdef";
unsigned char sk_d[32] = "0123456789abcdef0123456789abcdef";
unsigned char new_secret[32] = "0123456789abcdef0123456789abcdef"; /* new g^ir */
int mode = EVP_KDF_IKEV2_MODE_DKM;
unsigned char out[224];
OSSL_PARAM params[7], *p = params;

kdf = EVP_KDF_fetch(NULL, "IKEV2KDF", NULL);
kctx = EVP_KDF_CTX_new(kdf);
if (!kctx) {
    /* Error handling */
}

*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
    SN_sha256, strlen(SN_sha256));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
    sk_d, sizeof(sk_d));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET,
    new_secret, sizeof(new_secret));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NI,
    ni, sizeof(ni));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NR,
    nr, sizeof(nr));
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
*p = OSSL_PARAM_construct_end();
if (EVP_KDF_derive(kctx, out, sizeof(out), params) <= 0) {
    /* Error */
    ;
}
EVP_KDF_CTX_free(kctx);
EVP_KDF_free(kdf);

🌈 Example of rekeying the IKE SA

Rekeying, bestie!! ✨ Switch that mode to EVP_KDF_IKEV2_MODE_REKEY and your output is a tidy 32 bytes again. Very "new keys, who dis" of you:

EVP_KDF *kdf = NULL;
EVP_KDF_CTX *kctx = NULL;
unsigned char ni[8] = "01234567";
unsigned char nr[8] = "89abcdef";
unsigned char sk_d[32] = "0123456789abcdef0123456789abcdef";
unsigned char new_secret[32] = "0123456789abcdef0123456789abcdef"; /* new g^ir */
int mode = EVP_KDF_IKEV2_MODE_REKEY;
unsigned char out[32];
OSSL_PARAM params[7], *p = params;

kdf = EVP_KDF_fetch(NULL, "IKEV2KDF", NULL);
kctx = EVP_KDF_CTX_new(kdf);
if (!kctx) {
    /* Error handling */
}

*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
    SN_sha256, strlen(SN_sha256));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
    sk_d, sizeof(sk_d));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET,
    new_secret, sizeof(new_secret));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NI,
    ni, sizeof(ni));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_IKEV2KDF_NR,
    nr, sizeof(nr));
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
*p = OSSL_PARAM_construct_end();
if (EVP_KDF_derive(kctx, out, sizeof(out), params) <= 0) {
    /* Error handling */
    ;
}
EVP_KDF_CTX_free(kctx);
EVP_KDF_free(kdf);

πŸ’œ CONFORMING TO

It's conforming to RFC 7296 and SP800-135 β€” certified good behavior, no cap 🫢

⭐ SEE ALSO

Go read these too, they're literally the supporting cast and they ate:

🧚 HISTORY

This functionality was added in OpenSSL 4.1. New drop, fresh out the oven πŸ’…

Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.


That's the whole page, bestie β€” go forth and derive 🌸

Okay and like, the very last thing: this gorgeous piece of documentation is from version 4.1.0, dated 2026-10-23, and it goes by EVP_KDF-IKEV2KDF(7ssl) πŸ’– mwah, take care of her πŸŽ€

πŸ“¦ where this page comes from