the RFC5705 based key material generation to the current custom
OpenVPN PRF. This feature requires OpenSSL or mbed TLS 2.18+.
+Compatibility with OpenSSL in FIPS mode
+ OpenVPN will now work with OpenSSL in FIPS mode. Note, no effort
+ has been made to check or implement all the
+ requirements/recommendation of FIPS 140-2. This just allows OpenVPN
+ to be run on a system that be configured OpenSSL in FIPS mode.
+
Deprecated features
-------------------
``inetd`` has been removed
*/
const char *translate_cipher_name_to_openvpn(const char *cipher_name);
+
+/**
+ * Calculates the TLS 1.0-1.1 PRF function. For the exact specification of the
+ * function definition see the TLS RFCs like RFC 4346.
+ *
+ * @param seed seed to use
+ * @param seed_len length of the seed
+ * @param secret secret to use
+ * @param secret_len length of the secret
+ * @param output output destination
+ * @param output_len length of output/number of bytes to generate
+ *
+ * @return true if successful, false on any error
+ */
+bool ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret,
+ int secret_len, uint8_t *output, int output_len);
+
#endif /* CRYPTO_BACKEND_H_ */
#include <mbedtls/pem.h>
#include <mbedtls/entropy.h>
+#include <mbedtls/ssl.h>
/*
return diff;
}
+/* mbedtls-2.18.0 or newer */
+#ifdef HAVE_MBEDTLS_SSL_TLS_PRF
+bool
+ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret,
+ int secret_len, uint8_t *output, int output_len)
+{
+ return mbed_ok(mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1, secret,
+ secret_len, "", seed, seed_len, output,
+ output_len));
+}
+#else /* ifdef HAVE_MBEDTLS_SSL_TLS_PRF */
+/*
+ * Generate the hash required by for the \c tls1_PRF function.
+ *
+ * @param md_kt Message digest to use
+ * @param sec Secret to base the hash on
+ * @param sec_len Length of the secret
+ * @param seed Seed to hash
+ * @param seed_len Length of the seed
+ * @param out Output buffer
+ * @param olen Length of the output buffer
+ */
+static void
+tls1_P_hash(const md_kt_t *md_kt, const uint8_t *sec, int sec_len,
+ const uint8_t *seed, int seed_len, uint8_t *out, int olen)
+{
+ struct gc_arena gc = gc_new();
+ uint8_t A1[MAX_HMAC_KEY_LENGTH];
+
+#ifdef ENABLE_DEBUG
+ /* used by the D_SHOW_KEY_SOURCE, guarded with ENABLE_DEBUG to avoid unused
+ * variables warnings if compiled with --enable-small */
+ const int olen_orig = olen;
+ const uint8_t *out_orig = out;
+#endif
+
+ hmac_ctx_t *ctx = hmac_ctx_new();
+ hmac_ctx_t *ctx_tmp = hmac_ctx_new();
+
+ dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex(sec, sec_len, 0, &gc));
+ dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex(seed, seed_len, 0, &gc));
+
+ int chunk = md_kt_size(md_kt);
+ unsigned int A1_len = md_kt_size(md_kt);
+
+ hmac_ctx_init(ctx, sec, sec_len, md_kt);
+ hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt);
+
+ hmac_ctx_update(ctx,seed,seed_len);
+ hmac_ctx_final(ctx, A1);
+
+ for (;; )
+ {
+ hmac_ctx_reset(ctx);
+ hmac_ctx_reset(ctx_tmp);
+ hmac_ctx_update(ctx, A1, A1_len);
+ hmac_ctx_update(ctx_tmp, A1, A1_len);
+ hmac_ctx_update(ctx, seed, seed_len);
+
+ if (olen > chunk)
+ {
+ hmac_ctx_final(ctx, out);
+ out += chunk;
+ olen -= chunk;
+ hmac_ctx_final(ctx_tmp, A1); /* calc the next A1 value */
+ }
+ else /* last one */
+ {
+ hmac_ctx_final(ctx, A1);
+ memcpy(out, A1, olen);
+ break;
+ }
+ }
+ hmac_ctx_cleanup(ctx);
+ hmac_ctx_free(ctx);
+ hmac_ctx_cleanup(ctx_tmp);
+ hmac_ctx_free(ctx_tmp);
+ secure_memzero(A1, sizeof(A1));
+
+ dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex(out_orig, olen_orig, 0, &gc));
+ gc_free(&gc);
+}
+
+/*
+ * Use the TLS PRF function for generating data channel keys.
+ * This code is based on the OpenSSL library.
+ *
+ * TLS generates keys as such:
+ *
+ * master_secret[48] = PRF(pre_master_secret[48], "master secret",
+ * ClientHello.random[32] + ServerHello.random[32])
+ *
+ * key_block[] = PRF(SecurityParameters.master_secret[48],
+ * "key expansion",
+ * SecurityParameters.server_random[32] +
+ * SecurityParameters.client_random[32]);
+ *
+ * Notes:
+ *
+ * (1) key_block contains a full set of 4 keys.
+ * (2) The pre-master secret is generated by the client.
+ */
+bool
+ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec,
+ int slen, uint8_t *out1, int olen)
+{
+ struct gc_arena gc = gc_new();
+ const md_kt_t *md5 = md_kt_get("MD5");
+ const md_kt_t *sha1 = md_kt_get("SHA1");
+
+ uint8_t *out2 = (uint8_t *)gc_malloc(olen, false, &gc);
+
+ int len = slen/2;
+ const uint8_t *S1 = sec;
+ const uint8_t *S2 = &(sec[len]);
+ len += (slen&1); /* add for odd, make longer */
+
+ tls1_P_hash(md5, S1, len, label, label_len, out1, olen);
+ tls1_P_hash(sha1, S2, len, label, label_len, out2, olen);
+
+ for (int i = 0; i<olen; i++)
+ {
+ out1[i] ^= out2[i];
+ }
+
+ secure_memzero(out2, olen);
+
+ dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc));
+
+ gc_free(&gc);
+ return true;
+}
+#endif /* ifdef HAVE_MBEDTLS_SSL_TLS_PRF */
#endif /* ENABLE_CRYPTO_MBEDTLS */
#include <openssl/rand.h>
#include <openssl/ssl.h>
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#include <openssl/kdf.h>
+#endif
+
/*
* Check for key size creepage.
*/
#endif /* if HAVE_OPENSSL_ENGINE */
}
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+bool
+ssl_tls1_PRF(const uint8_t *seed, int seed_len, const uint8_t *secret,
+ int secret_len, uint8_t *output, int output_len)
+{
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_TLS1_PRF, NULL);
+ if (!EVP_PKEY_derive_init(pctx))
+ {
+ return false;
+ }
+
+ if (!EVP_PKEY_CTX_set_tls1_prf_md(pctx, EVP_md5_sha1()))
+ {
+ return false;
+ }
+
+ if (!EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, secret, secret_len))
+ {
+ return false;
+ }
+
+ if (!EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed, seed_len))
+ {
+ return false;
+ }
+
+ size_t out_len = output_len;
+ if (!EVP_PKEY_derive(pctx, output, &out_len))
+ {
+ return false;
+ }
+ if (out_len != output_len)
+ {
+ return false;
+ }
+ return true;
+}
+#else /* if OPENSSL_VERSION_NUMBER >= 0x10100000L */
+/*
+ * Generate the hash required by for the \c tls1_PRF function.
+ *
+ * We cannot use our normal hmac_* function as they do not work
+ * in a FIPS environment (no MD5 allowed, which we need). Instead
+ * we need to directly use the EVP_MD_* API with the special
+ * EVP_MD_CTX_FLAG_NON_FIPS_ALLOW flag.
+ *
+ * The function below is adapted from OpenSSL 1.0.2t
+ *
+ * @param md_kt Message digest to use
+ * @param sec Secret to base the hash on
+ * @param sec_len Length of the secret
+ * @param seed Seed to hash
+ * @param seed_len Length of the seed
+ * @param out Output buffer
+ * @param olen Length of the output buffer
+ */
+static
+bool
+tls1_P_hash(const EVP_MD *md, const unsigned char *sec,
+ int sec_len, const void *seed, int seed_len,
+ unsigned char *out, int olen)
+{
+ int chunk;
+ size_t j;
+ EVP_MD_CTX ctx, ctx_tmp, ctx_init;
+ EVP_PKEY *mac_key;
+ unsigned char A1[EVP_MAX_MD_SIZE];
+ size_t A1_len;
+ int ret = false;
+
+ chunk = EVP_MD_size(md);
+ OPENSSL_assert(chunk >= 0);
+
+ EVP_MD_CTX_init(&ctx);
+ EVP_MD_CTX_init(&ctx_tmp);
+ EVP_MD_CTX_init(&ctx_init);
+ EVP_MD_CTX_set_flags(&ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, sec, sec_len);
+ if (!mac_key)
+ {
+ goto err;
+ }
+ if (!EVP_DigestSignInit(&ctx_init, NULL, md, NULL, mac_key))
+ {
+ goto err;
+ }
+ if (!EVP_MD_CTX_copy_ex(&ctx, &ctx_init))
+ {
+ goto err;
+ }
+ if (!EVP_DigestSignUpdate(&ctx, seed, seed_len))
+ {
+ goto err;
+ }
+ if (!EVP_DigestSignFinal(&ctx, A1, &A1_len))
+ {
+ goto err;
+ }
+
+ for (;; )
+ {
+ /* Reinit mac contexts */
+ if (!EVP_MD_CTX_copy_ex(&ctx, &ctx_init))
+ {
+ goto err;
+ }
+ if (!EVP_DigestSignUpdate(&ctx, A1, A1_len))
+ {
+ goto err;
+ }
+ if (olen > chunk && !EVP_MD_CTX_copy_ex(&ctx_tmp, &ctx))
+ {
+ goto err;
+ }
+ if (!EVP_DigestSignUpdate(&ctx, seed, seed_len))
+ {
+ goto err;
+ }
+
+ if (olen > chunk)
+ {
+ if (!EVP_DigestSignFinal(&ctx, out, &j))
+ {
+ goto err;
+ }
+ out += j;
+ olen -= j;
+ /* calc the next A1 value */
+ if (!EVP_DigestSignFinal(&ctx_tmp, A1, &A1_len))
+ {
+ goto err;
+ }
+ }
+ else
+ {
+ /* last one */
+ if (!EVP_DigestSignFinal(&ctx, A1, &A1_len))
+ {
+ goto err;
+ }
+ memcpy(out, A1, olen);
+ break;
+ }
+ }
+ ret = true;
+err:
+ EVP_PKEY_free(mac_key);
+ EVP_MD_CTX_cleanup(&ctx);
+ EVP_MD_CTX_cleanup(&ctx_tmp);
+ EVP_MD_CTX_cleanup(&ctx_init);
+ OPENSSL_cleanse(A1, sizeof(A1));
+ return ret;
+}
+
+
+/*
+ * Use the TLS PRF function for generating data channel keys.
+ * This code is based on the OpenSSL library.
+ *
+ * TLS generates keys as such:
+ *
+ * master_secret[48] = PRF(pre_master_secret[48], "master secret",
+ * ClientHello.random[32] + ServerHello.random[32])
+ *
+ * key_block[] = PRF(SecurityParameters.master_secret[48],
+ * "key expansion",
+ * SecurityParameters.server_random[32] +
+ * SecurityParameters.client_random[32]);
+ *
+ * Notes:
+ *
+ * (1) key_block contains a full set of 4 keys.
+ * (2) The pre-master secret is generated by the client.
+ */
+bool
+ssl_tls1_PRF(const uint8_t *label, int label_len, const uint8_t *sec,
+ int slen, uint8_t *out1, int olen)
+{
+ bool ret = true;
+ struct gc_arena gc = gc_new();
+ /* For some reason our md_kt_get("MD5") fails otherwise in the unit test */
+ const md_kt_t *md5 = EVP_md5();
+ const md_kt_t *sha1 = EVP_sha1();
+
+ uint8_t *out2 = (uint8_t *)gc_malloc(olen, false, &gc);
+
+ int len = slen/2;
+ const uint8_t *S1 = sec;
+ const uint8_t *S2 = &(sec[len]);
+ len += (slen&1); /* add for odd, make longer */
+
+ if (!tls1_P_hash(md5, S1, len, label, label_len, out1, olen))
+ {
+ ret = false;
+ goto done;
+ }
+
+ if (!tls1_P_hash(sha1, S2, len, label, label_len, out2, olen))
+ {
+ ret = false;
+ goto done;
+ }
+
+ for (int i = 0; i < olen; i++)
+ {
+ out1[i] ^= out2[i];
+ }
+
+ secure_memzero(out2, olen);
+
+ dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc));
+done:
+ gc_free(&gc);
+ return ret;
+}
+#endif /* if OPENSSL_VERSION_NUMBER >= 0x10100000L */
#endif /* ENABLE_CRYPTO_OPENSSL */
key_source_print(&k->server, "Server");
}
-/*
- * Generate the hash required by for the \c tls1_PRF function.
- *
- * @param md_kt Message digest to use
- * @param sec Secret to base the hash on
- * @param sec_len Length of the secret
- * @param seed Seed to hash
- * @param seed_len Length of the seed
- * @param out Output buffer
- * @param olen Length of the output buffer
- */
-static void
-tls1_P_hash(const md_kt_t *md_kt,
- const uint8_t *sec,
- int sec_len,
- const uint8_t *seed,
- int seed_len,
- uint8_t *out,
- int olen)
-{
- struct gc_arena gc = gc_new();
- uint8_t A1[MAX_HMAC_KEY_LENGTH];
-
-#ifdef ENABLE_DEBUG
- const int olen_orig = olen;
- const uint8_t *out_orig = out;
-#endif
-
- hmac_ctx_t *ctx = hmac_ctx_new();
- hmac_ctx_t *ctx_tmp = hmac_ctx_new();
-
- dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex(sec, sec_len, 0, &gc));
- dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex(seed, seed_len, 0, &gc));
-
- int chunk = md_kt_size(md_kt);
- unsigned int A1_len = md_kt_size(md_kt);
-
- hmac_ctx_init(ctx, sec, sec_len, md_kt);
- hmac_ctx_init(ctx_tmp, sec, sec_len, md_kt);
-
- hmac_ctx_update(ctx,seed,seed_len);
- hmac_ctx_final(ctx, A1);
-
- for (;; )
- {
- hmac_ctx_reset(ctx);
- hmac_ctx_reset(ctx_tmp);
- hmac_ctx_update(ctx,A1,A1_len);
- hmac_ctx_update(ctx_tmp,A1,A1_len);
- hmac_ctx_update(ctx,seed,seed_len);
-
- if (olen > chunk)
- {
- hmac_ctx_final(ctx, out);
- out += chunk;
- olen -= chunk;
- hmac_ctx_final(ctx_tmp, A1); /* calc the next A1 value */
- }
- else /* last one */
- {
- hmac_ctx_final(ctx, A1);
- memcpy(out,A1,olen);
- break;
- }
- }
- hmac_ctx_cleanup(ctx);
- hmac_ctx_free(ctx);
- hmac_ctx_cleanup(ctx_tmp);
- hmac_ctx_free(ctx_tmp);
- secure_memzero(A1, sizeof(A1));
-
- dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex(out_orig, olen_orig, 0, &gc));
- gc_free(&gc);
-}
-
-/*
- * Use the TLS PRF function for generating data channel keys.
- * This code is based on the OpenSSL library.
- *
- * TLS generates keys as such:
- *
- * master_secret[48] = PRF(pre_master_secret[48], "master secret",
- * ClientHello.random[32] + ServerHello.random[32])
- *
- * key_block[] = PRF(SecurityParameters.master_secret[48],
- * "key expansion",
- * SecurityParameters.server_random[32] +
- * SecurityParameters.client_random[32]);
- *
- * Notes:
- *
- * (1) key_block contains a full set of 4 keys.
- * (2) The pre-master secret is generated by the client.
- */
-static void
-tls1_PRF(const uint8_t *label,
- int label_len,
- const uint8_t *sec,
- int slen,
- uint8_t *out1,
- int olen)
-{
- struct gc_arena gc = gc_new();
- const md_kt_t *md5 = md_kt_get("MD5");
- const md_kt_t *sha1 = md_kt_get("SHA1");
-
- uint8_t *out2 = (uint8_t *) gc_malloc(olen, false, &gc);
-
- int len = slen/2;
- const uint8_t *S1 = sec;
- const uint8_t *S2 = &(sec[len]);
- len += (slen&1); /* add for odd, make longer */
-
- tls1_P_hash(md5,S1,len,label,label_len,out1,olen);
- tls1_P_hash(sha1,S2,len,label,label_len,out2,olen);
-
- for (int i = 0; i<olen; i++)
- {
- out1[i] ^= out2[i];
- }
-
- secure_memzero(out2, olen);
-
- dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex(out1, olen, 0, &gc));
-
- gc_free(&gc);
-}
-
-static void
+static bool
openvpn_PRF(const uint8_t *secret,
int secret_len,
const char *label,
uint8_t *output,
int output_len)
{
+ bool ret = true;
/* concatenate seed components */
struct buffer seed = alloc_buf(strlen(label)
}
/* compute PRF */
- tls1_PRF(BPTR(&seed), BLEN(&seed), secret, secret_len, output, output_len);
+ if (!ssl_tls1_PRF(BPTR(&seed), BLEN(&seed), secret, secret_len, output, output_len))
+ {
+ ret = false;
+ }
buf_clear(&seed);
free_buf(&seed);
VALGRIND_MAKE_READABLE((void *)output, output_len);
+ return ret;
}
static void
return true;
}
-static struct key2
-generate_key_expansion_openvpn_prf(const struct tls_session *session)
+static bool
+generate_key_expansion_openvpn_prf(const struct tls_session *session, struct key2 *key2)
{
uint8_t master[48] = { 0 };
key_source2_print(key_src);
/* compute master secret */
- openvpn_PRF(key_src->client.pre_master,
- sizeof(key_src->client.pre_master),
- KEY_EXPANSION_ID " master secret",
- key_src->client.random1,
- sizeof(key_src->client.random1),
- key_src->server.random1,
- sizeof(key_src->server.random1),
- NULL,
- NULL,
- master,
- sizeof(master));
+ if (!openvpn_PRF(key_src->client.pre_master,
+ sizeof(key_src->client.pre_master),
+ KEY_EXPANSION_ID " master secret",
+ key_src->client.random1,
+ sizeof(key_src->client.random1),
+ key_src->server.random1,
+ sizeof(key_src->server.random1),
+ NULL,
+ NULL,
+ master,
+ sizeof(master)))
+ {
+ return false;
+ }
- struct key2 key2;
/* compute key expansion */
- openvpn_PRF(master,
- sizeof(master),
- KEY_EXPANSION_ID " key expansion",
- key_src->client.random2,
- sizeof(key_src->client.random2),
- key_src->server.random2,
- sizeof(key_src->server.random2),
- client_sid,
- server_sid,
- (uint8_t *)key2.keys,
- sizeof(key2.keys));
+ if (!openvpn_PRF(master,
+ sizeof(master),
+ KEY_EXPANSION_ID " key expansion",
+ key_src->client.random2,
+ sizeof(key_src->client.random2),
+ key_src->server.random2,
+ sizeof(key_src->server.random2),
+ client_sid,
+ server_sid,
+ (uint8_t *)key2->keys,
+ sizeof(key2->keys)))
+ {
+ return false;
+ }
secure_memzero(&master, sizeof(master));
+
+
/*
* fixup_key only correctly sets DES parity bits if the cipher is a
* DES variant.
*/
for (int i = 0; i < 2; ++i)
{
- fixup_key(&key2.keys[i], &session->opt->key_type);
+ fixup_key(&key2->keys[i], &session->opt->key_type);
}
- key2.n = 2;
+ key2->n = 2;
- return key2;
+ return true;
}
/*
}
else
{
- key2 = generate_key_expansion_openvpn_prf(session);
+ if (!generate_key_expansion_openvpn_prf(session, &key2))
+ {
+ msg(D_TLS_ERRORS, "TLS Error: PRF calcuation failed");
+ goto exit;
+ }
}
key2_print(&key2, &session->opt->key_type,
#include <cmocka.h>
#include "crypto.h"
+#include "ssl_backend.h"
#include "mock_msg.h"
test_cipher_names("id-aes256-GCM", "AES-256-GCM");
}
+
+static uint8_t good_prf[32] = {0xd9, 0x8c, 0x85, 0x18, 0xc8, 0x5e, 0x94, 0x69,
+ 0x27, 0x91, 0x6a, 0xcf, 0xc2, 0xd5, 0x92, 0xfb,
+ 0xb1, 0x56, 0x7e, 0x4b, 0x4b, 0x14, 0x59, 0xe6,
+ 0xa9, 0x04, 0xac, 0x2d, 0xda, 0xb7, 0x2d, 0x67};
+static void
+crypto_test_tls_prf(void **state)
+{
+ const char *seedstr = "Quis aute iure reprehenderit in voluptate "
+ "velit esse cillum dolore";
+ const unsigned char *seed = (const unsigned char *)seedstr;
+ const size_t seed_len = strlen(seedstr);
+
+
+
+
+ const char* ipsumlorem = "Lorem ipsum dolor sit amet, consectetur "
+ "adipisici elit, sed eiusmod tempor incidunt ut "
+ "labore et dolore magna aliqua.";
+
+ const unsigned char *secret = (const unsigned char *) ipsumlorem;
+ size_t secret_len = strlen((const char *)secret);
+
+
+ uint8_t out[32];
+ ssl_tls1_PRF(seed, seed_len, secret, secret_len, out, sizeof(out));
+
+ assert_memory_equal(good_prf, out, sizeof(out));
+}
+
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(crypto_pem_encode_decode_loopback),
cmocka_unit_test(crypto_translate_cipher_names),
+ cmocka_unit_test(crypto_test_tls_prf)
};
#if defined(ENABLE_CRYPTO_OPENSSL)