Difference between revisions of "EVP Key Derivation"

From OpenSSLWiki
Jump to navigationJump to search
m (Add KDF example; stop redirect to Key Agreement page.)
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Key derivation is the process of deriving one or more secret keys from a secret value such as a password or a passphrase. Several key derivation algoirthms have been standardized, and they are usually referred to a Key Derivation Functions (KDFs). KDFs include PBKDF2 from RFC 2898, HKDF form RFC 5869 and Scrypt from RFC 7914. OpenSSL provides PBKDF2, Scrypt, HKDF, ANSI X9.42 and ANSI X9.63 by way of <tt>EVP_KDF</tt>.
+
Key derivation is the process of deriving one or more secret keys from a secret value such as a password or a passphrase. Several key derivation algoirthms have been standardized, and they are usually referred to a Key Derivation Functions (KDFs). KDFs include PBKDF2 from RFC 2898, HKDF form RFC 5869 and Scrypt from RFC 7914.
 +
 
 +
OpenSSL 1.0.2 and above provides PBKDF2 by way of <tt>PKCS5_PBKDF2_HMAC</tt> and <tt>PKCS5_PBKDF2_HMAC_SHA1</tt>.
 +
 
 +
OpenSSL 1.1.0 and above additionally provides HKDF and TLS1 PRF KDF by way of <tt>EVP_PKEY_derive</tt> and Scrypt by way of <tt>EVP_PBE_scrypt</tt>
 +
 
 +
OpenSSL 1.1.1 and above additionally provides Scrypt by way of <tt>EVP_PKEY_derive</tt>.
 +
 
 +
OpenSSL 3.0 additionally provides Single Step KDF, SSH KDF, PBKDF2, Scrypt, HKDF, ANSI X9.42 KDF, ANSI X9.63 KDF and TLS1 PRF KDF by way of <tt>EVP_KDF</tt>.
 +
 
 +
From OpenSSL 3.0 the recommended way of performing key derivation is to use the EVP_KDF functions. If compatibility with OpenSSL 1.1.1 is required then a limited set of KDFs can be used via EVP_PKEY_derive.
  
Not all key derivation functions are available in all versions of OpenSSL. OpenSSL 1.0.2 and below provide WWW, XXX, YYY and ZZZ. OpenSSL 1.1.0 and above provide WWW, XXX, YYY and ZZZ. TODO: fill in the pieces here and talk about changes.
 
  
 
== HKDF key derivation ==
 
== HKDF key derivation ==
  
The following program derives a key using HKDF from RFC 5869. HKDF was designed by Krawczyk and Eronen, and it is state of the art in expand-then-extract key derivation algorithms. It is usually a good choice when you need a KDF. The program below was taken from the OpenSSL man pages.
+
The following example derives a key and initialization vector using HKDF from RFC 5869 and SHA-256. HKDF was designed by Krawczyk and Eronen, and it is state of the art in expand-then-extract key derivation algorithms. It is usually a good choice when you need a KDF. The program below was taken from the OpenSSL man pages.
  
 
HKDF takes three parameter:
 
HKDF takes three parameter:
  
<tt>secret</tt> - private information to use during derivation, like a password or passphrase. The parameter is set using <tt>EVP_KDF_CTRL_SET_KEY</tt>.
+
* <tt>secret</tt> - private information to use during derivation, like a password or passphrase. The parameter is set using <tt>EVP_KDF_CTRL_SET_KEY</tt>.
  
<tt>salt</tt> - possibly public information to use during derivation. <tt>salt</tt> is optional. The parameter is set using <tt>EVP_KDF_CTRL_SET_SALT</tt>.
+
* <tt>salt</tt> - possibly public information to use during derivation. <tt>salt</tt> is optional. The parameter is set using <tt>EVP_KDF_CTRL_SET_SALT</tt>.
  
<tt>info</tt> - additional, possibly public information to use during derivation. <tt>info</tt> is optional. The parameter is set using <tt>EVP_KDF_CTRL_ADD_HKDF_INFO</tt>.
+
* <tt>info</tt> - additional, possibly public information to use during derivation. <tt>info</tt> is optional. The parameter is set using <tt>EVP_KDF_CTRL_ADD_HKDF_INFO</tt>.
  
 
<pre>#include <openssl/evp.h>
 
<pre>#include <openssl/evp.h>
 
#include <openssl/kdf.h>
 
#include <openssl/kdf.h>
 +
#include <openssl/params.h>
  
 
#include <stdio.h>
 
#include <stdio.h>
Line 23: Line 33:
 
int main(int argc, char* argv[])
 
int main(int argc, char* argv[])
 
{
 
{
 +
    EVP_KDF *kdf;
 
     EVP_KDF_CTX *kctx = NULL;
 
     EVP_KDF_CTX *kctx = NULL;
     unsigned char out[32];
+
     unsigned char derived[32];
 
+
     OSSL_PARAM params[5], *p = params;
     kctx = EVP_KDF_CTX_new_id(EVP_KDF_HKDF);
 
  
     if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, EVP_sha256()) <= 0) {
+
    /* Find and allocate a context for the HKDF algorithm */
         error("EVP_KDF_CTRL_SET_MD");
+
     if ((kdf = EVP_KDF_fetch(NULL, "hkdf", NULL)) == NULL) {
 +
         error("EVP_KDF_fetch");
 
     }
 
     }
     if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT, "salt", (size_t)4) <= 0) {
+
     kctx = EVP_KDF_CTX_new(kdf);
         error("EVP_KDF_CTRL_SET_SALT");
+
    EVP_KDF_free(kdf);    /* The kctx keeps a reference so this is safe */
 +
    if (kctx == NULL) {
 +
         error("EVP_KDF_CTX_new");
 
     }
 
     }
     if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, "secret", (size_t)6) <= 0) {
+
 
         error("EVP_KDF_CTRL_SET_KEY");
+
    /* Build up the parameters for the derivation */
 +
     *p++ = OSSL_PARAM_construct_utf8_string("digest", "sha256", (size_t)7);
 +
    *p++ = OSSL_PARAM_construct_octet_string("salt", "salt", (size_t)4);
 +
    *p++ = OSSL_PARAM_construct_octet_string("key", "secret", (size_t)6);
 +
    *p++ = OSSL_PARAM_construct_octet_string("info", "label", (size_t)5);
 +
    *p = OSSL_PARAM_construct_end();
 +
    if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
 +
         error("EVP_KDF_CTX_set_params");
 
     }
 
     }
    if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_ADD_HKDF_INFO, "label", (size_t)5) <= 0) {
+
 
        error("EVP_KDF_CTRL_ADD_HKDF_INFO");
+
     /* Do the derivation */
     }
+
     if (EVP_KDF_derive(kctx, derived, sizeof(derived), NULL) <= 0) {
     if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
 
 
         error("EVP_KDF_derive");
 
         error("EVP_KDF_derive");
 
     }
 
     }
   
+
 
 
     /* Use the 32 bytes as a Key and IV */
 
     /* Use the 32 bytes as a Key and IV */
     const unsigned char *key = out+0;
+
     const unsigned char *key = derived+0;
     const unsigned char  *iv = out+16;
+
     const unsigned char  *iv = derived+16;
   
+
 
 
     printf("Key: ");
 
     printf("Key: ");
 
     for (size_t i=0; i<16; ++i)
 
     for (size_t i=0; i<16; ++i)

Latest revision as of 06:42, 14 June 2022

Key derivation is the process of deriving one or more secret keys from a secret value such as a password or a passphrase. Several key derivation algoirthms have been standardized, and they are usually referred to a Key Derivation Functions (KDFs). KDFs include PBKDF2 from RFC 2898, HKDF form RFC 5869 and Scrypt from RFC 7914.

OpenSSL 1.0.2 and above provides PBKDF2 by way of PKCS5_PBKDF2_HMAC and PKCS5_PBKDF2_HMAC_SHA1.

OpenSSL 1.1.0 and above additionally provides HKDF and TLS1 PRF KDF by way of EVP_PKEY_derive and Scrypt by way of EVP_PBE_scrypt

OpenSSL 1.1.1 and above additionally provides Scrypt by way of EVP_PKEY_derive.

OpenSSL 3.0 additionally provides Single Step KDF, SSH KDF, PBKDF2, Scrypt, HKDF, ANSI X9.42 KDF, ANSI X9.63 KDF and TLS1 PRF KDF by way of EVP_KDF.

From OpenSSL 3.0 the recommended way of performing key derivation is to use the EVP_KDF functions. If compatibility with OpenSSL 1.1.1 is required then a limited set of KDFs can be used via EVP_PKEY_derive.


HKDF key derivation[edit]

The following example derives a key and initialization vector using HKDF from RFC 5869 and SHA-256. HKDF was designed by Krawczyk and Eronen, and it is state of the art in expand-then-extract key derivation algorithms. It is usually a good choice when you need a KDF. The program below was taken from the OpenSSL man pages.

HKDF takes three parameter:

  • secret - private information to use during derivation, like a password or passphrase. The parameter is set using EVP_KDF_CTRL_SET_KEY.
  • salt - possibly public information to use during derivation. salt is optional. The parameter is set using EVP_KDF_CTRL_SET_SALT.
  • info - additional, possibly public information to use during derivation. info is optional. The parameter is set using EVP_KDF_CTRL_ADD_HKDF_INFO.
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/params.h>

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    EVP_KDF *kdf;
    EVP_KDF_CTX *kctx = NULL;
    unsigned char derived[32];
    OSSL_PARAM params[5], *p = params;

    /* Find and allocate a context for the HKDF algorithm */
    if ((kdf = EVP_KDF_fetch(NULL, "hkdf", NULL)) == NULL) {
        error("EVP_KDF_fetch");
    }
    kctx = EVP_KDF_CTX_new(kdf);
    EVP_KDF_free(kdf);    /* The kctx keeps a reference so this is safe */
    if (kctx == NULL) {
        error("EVP_KDF_CTX_new");
    }

    /* Build up the parameters for the derivation */
    *p++ = OSSL_PARAM_construct_utf8_string("digest", "sha256", (size_t)7);
    *p++ = OSSL_PARAM_construct_octet_string("salt", "salt", (size_t)4);
    *p++ = OSSL_PARAM_construct_octet_string("key", "secret", (size_t)6);
    *p++ = OSSL_PARAM_construct_octet_string("info", "label", (size_t)5);
    *p = OSSL_PARAM_construct_end();
    if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
        error("EVP_KDF_CTX_set_params");
    }

    /* Do the derivation */
    if (EVP_KDF_derive(kctx, derived, sizeof(derived), NULL) <= 0) {
        error("EVP_KDF_derive");
    }

    /* Use the 32 bytes as a Key and IV */
    const unsigned char *key = derived+0;
    const unsigned char  *iv = derived+16;
  
    printf("Key: ");
    for (size_t i=0; i<16; ++i)
        printf("%02x ", key[i]);
    printf("\n");

    printf("IV:  ");
    for (size_t i=0; i<16; ++i)
        printf("%02x ", iv[i]);
    printf("\n");

    EVP_KDF_CTX_free(kctx);

    return 0;
}

You can compile the program using C99 with the following command.

openssl$ gcc -std=c99 test.c -o test.exe -l:libcrypto.a -pthread -ldl
openssl$

Running the program results in the following output.

$ ./test.exe
Key: 2a c4 36 9f 52 59 96 f8 de 13 73 1f 56 22 4f 34
IV:  df 0e 4c 96 ca a9 3b fd ec cf 23 7b 50 39 c8 db