From be75bae8d6d060363e1a2f47d9e302c88afa6a83 Mon Sep 17 00:00:00 2001 From: Ilie Halip Date: Thu, 15 Jan 2026 11:52:49 +0200 Subject: [PATCH 1/2] Add support for the TLS1-PRF KDF (TLS 1.2) Add an implementation for the TLS1-PRF KDF. When deriving either master secret or key expansion, try to use the generic CKM_TLS_KDF mechanism. If it is not available or unsuccessful, fall back to the dedicated mechanisms for each of the derivation steps. A particual implementation detail is that OpenSSL does not give any information about the selected ciphersuite. When using CKM_TLS12_KEY_AND_MAC_DERIVE, we need to provide the sizes of the mac keys, enc keys and IV, so try to infer these from the key block size passed by OpenSSL. Signed-off-by: Ilie Halip --- src/exchange.c | 148 +++++++++++ src/exchange.h | 10 + src/kdf.c | 588 +++++++++++++++++++++++++++++++++++++++++++ src/kdf.h | 3 + src/kmgmt/keymgmt.h | 2 + src/kmgmt/tls1_prf.c | 63 +++++ src/meson.build | 1 + src/obj/create.c | 5 + src/provider.c | 17 +- src/provider.h | 5 +- 10 files changed, 840 insertions(+), 2 deletions(-) create mode 100644 src/kmgmt/tls1_prf.c diff --git a/src/exchange.c b/src/exchange.c index d2918fb9..6d738f17 100644 --- a/src/exchange.c +++ b/src/exchange.c @@ -1,4 +1,5 @@ /* Copyright (C) 2022 Simo Sorce + Copyright 2026 NXP SPDX-License-Identifier: Apache-2.0 */ #include "provider.h" @@ -815,3 +816,150 @@ const OSSL_DISPATCH p11prov_hkdf_exchange_functions[] = { DISPATCH_EXCHHKDF_ELEM(exch_hkdf, SETTABLE_CTX_PARAMS, settable_ctx_params), { 0, NULL }, }; + +DISPATCH_EXCHTLS1PRF_FN(newctx); +DISPATCH_EXCHTLS1PRF_FN(freectx); +DISPATCH_EXCHTLS1PRF_FN(init); +DISPATCH_EXCHTLS1PRF_FN(derive); +DISPATCH_EXCHTLS1PRF_FN(set_ctx_params); +DISPATCH_EXCHTLS1PRF_FN(settable_ctx_params); + +static void *p11prov_exch_tls1_prf_newctx(void *provctx) +{ + P11PROV_CTX *ctx = (P11PROV_CTX *)provctx; + P11PROV_EXCH_CTX *tls1_prfctx; + EVP_KDF *kdf = NULL; + + P11PROV_debug("tls1_prf exchange newctx"); + + tls1_prfctx = OPENSSL_zalloc(sizeof(P11PROV_EXCH_CTX)); + if (tls1_prfctx == NULL) { + return NULL; + } + + tls1_prfctx->provctx = ctx; + + /* mark with mechanism type */ + tls1_prfctx->mechtype = CKM_TLS12_MASTER_KEY_DERIVE_DH; + + kdf = EVP_KDF_fetch(NULL, "TLS1-PRF", P11PROV_DEFAULT_PROPERTIES); + if (kdf == NULL) { + OPENSSL_free(tls1_prfctx); + return NULL; + } + tls1_prfctx->kdfctx = EVP_KDF_CTX_new(kdf); + EVP_KDF_free(kdf); + + if (tls1_prfctx->kdfctx == NULL) { + OPENSSL_free(tls1_prfctx); + return NULL; + } + + return tls1_prfctx; +} + +static void p11prov_exch_tls1_prf_freectx(void *ctx) +{ + P11PROV_EXCH_CTX *tls1_prfctx = (P11PROV_EXCH_CTX *)ctx; + + P11PROV_debug("tls1_prf exchange freectx"); + + if (tls1_prfctx == NULL) { + return; + } + + EVP_KDF_CTX_free(tls1_prfctx->kdfctx); + p11prov_obj_free(tls1_prfctx->key); + OPENSSL_clear_free(tls1_prfctx, sizeof(P11PROV_EXCH_CTX)); +} + +static int p11prov_exch_tls1_prf_init(void *ctx, void *provkey, + const OSSL_PARAM params[]) +{ + P11PROV_EXCH_CTX *tls1_prfctx = (P11PROV_EXCH_CTX *)ctx; + P11PROV_OBJ *key = (P11PROV_OBJ *)provkey; + CK_RV ret; + + P11PROV_debug("tls1_prf exchange init (ctx:%p key:%p params:%p)", ctx, key, + params); + + if (ctx == NULL || provkey == NULL) { + return RET_OSSL_ERR; + } + + ret = p11prov_ctx_status(tls1_prfctx->provctx); + if (ret != CKR_OK) { + return RET_OSSL_ERR; + } + + if (provkey != &p11prov_tls1_prf_static_ctx) { + p11prov_obj_free(tls1_prfctx->key); + tls1_prfctx->key = p11prov_obj_ref(key); + if (tls1_prfctx->key == NULL) { + P11PROV_raise(tls1_prfctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid object"); + return RET_OSSL_ERR; + } + if (p11prov_obj_get_class(tls1_prfctx->key) != CKO_PRIVATE_KEY) { + P11PROV_raise(tls1_prfctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid key class"); + return RET_OSSL_ERR; + } + } + + return p11prov_exch_tls1_prf_set_ctx_params(ctx, params); +} + +static int p11prov_exch_tls1_prf_derive(void *ctx, unsigned char *secret, + size_t *secretlen, size_t outlen) +{ + P11PROV_EXCH_CTX *tls1_prfctx = (P11PROV_EXCH_CTX *)ctx; + + P11PROV_debug("tls1_prf exchange derive (ctx:%p)", ctx); + + if (secret == NULL) { + *secretlen = EVP_KDF_CTX_get_kdf_size(tls1_prfctx->kdfctx); + return 1; + } + + return EVP_KDF_derive(tls1_prfctx->kdfctx, secret, outlen, NULL); +} + +static int p11prov_exch_tls1_prf_set_ctx_params(void *ctx, + const OSSL_PARAM params[]) +{ + P11PROV_EXCH_CTX *tls1_prfctx = (P11PROV_EXCH_CTX *)ctx; + + P11PROV_debug("tls1_prf exchange set ctx params (ctx:%p, params:%p)", ctx, + params); + + return EVP_KDF_CTX_set_params(tls1_prfctx->kdfctx, params); +} + +static const OSSL_PARAM * +p11prov_exch_tls1_prf_settable_ctx_params(void *ctx, void *provctx) +{ + const OSSL_PARAM *params; + EVP_KDF *kdf; + + kdf = EVP_KDF_fetch(NULL, "TLS1-PRF", P11PROV_DEFAULT_PROPERTIES); + if (kdf == NULL) { + return NULL; + } + + params = EVP_KDF_settable_ctx_params(kdf); + EVP_KDF_free(kdf); + + return params; +} + +const OSSL_DISPATCH p11prov_tls1_prf_exchange_functions[] = { + DISPATCH_EXCHTLS1PRF_ELEM(exch_tls1_prf, NEWCTX, newctx), + DISPATCH_EXCHTLS1PRF_ELEM(exch_tls1_prf, FREECTX, freectx), + DISPATCH_EXCHTLS1PRF_ELEM(exch_tls1_prf, INIT, init), + DISPATCH_EXCHTLS1PRF_ELEM(exch_tls1_prf, DERIVE, derive), + DISPATCH_EXCHTLS1PRF_ELEM(exch_tls1_prf, SET_CTX_PARAMS, set_ctx_params), + DISPATCH_EXCHTLS1PRF_ELEM(exch_tls1_prf, SETTABLE_CTX_PARAMS, + settable_ctx_params), + { 0, NULL }, +}; diff --git a/src/exchange.h b/src/exchange.h index dd4d1901..11e6b446 100644 --- a/src/exchange.h +++ b/src/exchange.h @@ -1,4 +1,5 @@ /* Copyright (C) 2022 Simo Sorce + Copyright 2026 NXP SPDX-License-Identifier: Apache-2.0 */ #ifndef _EXCHANGE_H @@ -22,4 +23,13 @@ extern const OSSL_DISPATCH p11prov_x448_exchange_functions[]; } extern const OSSL_DISPATCH p11prov_hkdf_exchange_functions[]; +/* TLS1-PRF exchange fns */ +#define DISPATCH_EXCHTLS1PRF_FN(name) \ + DECL_DISPATCH_FUNC(keyexch, p11prov_exch_tls1_prf, name) +#define DISPATCH_EXCHTLS1PRF_ELEM(prefix, NAME, name) \ + { \ + OSSL_FUNC_KEYEXCH_##NAME, (void (*)(void))p11prov_##prefix##_##name \ + } +extern const OSSL_DISPATCH p11prov_tls1_prf_exchange_functions[]; + #endif /* _EXCHANGE_H */ diff --git a/src/kdf.c b/src/kdf.c index 0cc54422..cdf170ac 100644 --- a/src/kdf.c +++ b/src/kdf.c @@ -1,10 +1,13 @@ /* Copyright (C) 2022 Simo Sorce + Copyright 2026 NXP SPDX-License-Identifier: Apache-2.0 */ #include "provider.h" #include "platform/endian.h" #include #include +#include +#include struct p11prov_kdf_ctx { P11PROV_CTX *provctx; @@ -31,6 +34,35 @@ struct p11prov_kdf_ctx { }; typedef struct p11prov_kdf_ctx P11PROV_KDF_CTX; +static struct { + CK_ULONG keytype; + CK_MECHANISM_TYPE cipher; + CK_ULONG keylen; + CK_ULONG maclen; + CK_ULONG ivlen; +} tls12_cipher_map[] = { + { .keytype = CKK_AES, + .cipher = CKM_AES_CBC, + .keylen = 32, + .maclen = 48, + .ivlen = 16 }, + { .keytype = CKK_AES, + .cipher = CKM_AES_CBC, + .keylen = 16, + .maclen = 32, + .ivlen = 16 }, + { .keytype = CKK_AES, + .cipher = CKM_AES_GCM, + .keylen = 32, + .maclen = 0, + .ivlen = 4 }, + { .keytype = CKK_AES, + .cipher = CKM_AES_GCM, + .keylen = 16, + .maclen = 0, + .ivlen = 4 }, +}; + DISPATCH_HKDF_FN(newctx); DISPATCH_HKDF_FN(freectx); DISPATCH_HKDF_FN(reset); @@ -995,3 +1027,559 @@ const OSSL_DISPATCH p11prov_tls13_kdf_functions[] = { #endif { 0, NULL }, }; + +static int inner_derive_master_key(P11PROV_CTX *ctx, P11PROV_OBJ *key, + P11PROV_SESSION **session, + CK_MECHANISM *mechanism, size_t keylen, + CK_OBJECT_HANDLE *dkey_handle) +{ + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_KEY_TYPE key_type = CKK_GENERIC_SECRET; + CK_BBOOL val_false = CK_FALSE; + CK_BBOOL val_true = CK_TRUE; + CK_ULONG key_size = keylen; + CK_ATTRIBUTE key_template[] = { + { CKA_CLASS, &class, sizeof(class) }, + { CKA_TOKEN, &val_false, sizeof(val_false) }, + { CKA_VALUE_LEN, &key_size, sizeof(key_size) }, + { CKA_KEY_TYPE, &key_type, sizeof(key_type) }, + { CKA_SIGN, &val_true, sizeof(val_true) }, + { CKA_VERIFY, &val_true, sizeof(val_true) }, + { CKA_DERIVE, &val_true, sizeof(val_true) }, + { CKA_SENSITIVE, &val_false, sizeof(val_false) }, + { CKA_EXTRACTABLE, &val_true, sizeof(val_true) } + }; + + return p11prov_derive_key(key, mechanism, key_template, + sizeof(key_template) / sizeof(CK_ATTRIBUTE), + session, dkey_handle); +} + +static int inner_derive_key_expansion(P11PROV_CTX *ctx, P11PROV_OBJ *key, + P11PROV_SESSION **session, + CK_KEY_TYPE key_type, + CK_MECHANISM *mechanism, size_t keylen, + CK_OBJECT_HANDLE *dkey_handle) +{ + CK_BBOOL val_false = CK_FALSE; + CK_BBOOL val_true = CK_TRUE; + CK_ULONG key_size = keylen; + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_ATTRIBUTE key_template[] = { + { CKA_CLASS, &class, sizeof(class) }, + { CKA_TOKEN, &val_false, sizeof(val_false) }, + { CKA_VALUE_LEN, &key_size, sizeof(key_size) }, + { CKA_KEY_TYPE, &key_type, sizeof(key_type) }, + { CKA_SENSITIVE, &val_false, sizeof(val_false) }, + { CKA_EXTRACTABLE, &val_true, sizeof(val_true) }, + { CKA_ENCRYPT, &val_true, sizeof(val_true) }, + { CKA_DECRYPT, &val_true, sizeof(val_true) }, + }; + + return p11prov_derive_key(key, mechanism, key_template, + sizeof(key_template) / sizeof(CK_ATTRIBUTE), + session, dkey_handle); +} + +static CK_RV inner_tls_sign(P11PROV_KDF_CTX *tls12ctx, CK_MECHANISM *mechanism, + CK_BYTE_PTR data, CK_ULONG datalen, CK_BYTE_PTR sig, + CK_ULONG siglen) +{ + CK_SLOT_ID slotid = CK_UNAVAILABLE_INFORMATION; + CK_RV rv; + + if (tls12ctx->session == NULL) { + rv = p11prov_get_session(tls12ctx->provctx, &slotid, NULL, NULL, + mechanism->mechanism, NULL, NULL, false, false, + &tls12ctx->session); + if (rv != CKR_OK) { + return rv; + } + } + if (tls12ctx->session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + rv = p11prov_SignInit(tls12ctx->provctx, + p11prov_session_handle(tls12ctx->session), mechanism, + p11prov_obj_get_handle(tls12ctx->key)); + if (rv != CKR_OK) { + return rv; + } + + return p11prov_Sign(tls12ctx->provctx, + p11prov_session_handle(tls12ctx->session), data, + datalen, sig, &siglen); +} + +static const OSSL_PARAM *p11prov_tls1_prf_kdf_settable_ctx_params(void *ctx, + void *prov) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int p11prov_tls1_prf_kdf_set_ctx_params(void *ctx, + const OSSL_PARAM params[]) +{ + P11PROV_KDF_CTX *kdfctx = (P11PROV_KDF_CTX *)ctx; + const OSSL_PARAM *p; + int ret; + + P11PROV_debug("tls1_prf set ctx params (ctx=%p, params=%p)", kdfctx, + params); + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DIGEST); + if (p) { + const char *digest = NULL; + CK_RV rv; + + ret = OSSL_PARAM_get_utf8_string_ptr(p, &digest); + if (ret != RET_OSSL_OK) { + return ret; + } + + rv = p11prov_digest_get_by_name(digest, &kdfctx->hash_mech); + if (rv != CKR_OK) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST); + return RET_OSSL_ERR; + } + P11PROV_debug("set digest to %lu", kdfctx->hash_mech); + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET); + if (p) { + const void *secret = NULL; + size_t secret_len; + + ret = OSSL_PARAM_get_octet_string_ptr(p, &secret, &secret_len); + if (ret != RET_OSSL_OK) { + return ret; + } + + /* Create Session and key from key material */ + p11prov_obj_free(kdfctx->key); + ret = inner_pkcs11_key(kdfctx, CKM_TLS12_MASTER_KEY_DERIVE_DH, secret, + secret_len, &kdfctx->key); + if (ret != CKR_OK) { + return RET_OSSL_ERR; + } + P11PROV_debug("set secret (len:%lu)", secret_len); + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SEED); + if (p) { + OPENSSL_clear_free(kdfctx->data, kdfctx->datalen); + kdfctx->data = NULL; + kdfctx->datalen = 0; + } + + /* can be multiple parameters, which will be all concatenated */ + for (; p; p = OSSL_PARAM_locate_const(p + 1, OSSL_KDF_PARAM_SEED)) { + uint8_t *ptr; + size_t len; + + if (p->data_size == 0 || p->data == NULL) { + continue; + } + + len = kdfctx->datalen + p->data_size; + ptr = OPENSSL_realloc(kdfctx->data, len); + if (ptr == NULL) { + OPENSSL_clear_free(kdfctx->data, kdfctx->datalen); + kdfctx->data = NULL; + kdfctx->datalen = 0; + return RET_OSSL_ERR; + } + memcpy(ptr + kdfctx->datalen, p->data, p->data_size); + kdfctx->data = ptr; + kdfctx->datalen = len; + P11PROV_debug("set seed (len:%lu)", kdfctx->datalen); + } + + return RET_OSSL_OK; +} + +#define HAS_TLS12_LABEL(data, datalen, label) \ + (datalen >= TLS_MD_##label##_CONST_SIZE \ + && !CRYPTO_memcmp(data, TLS_MD_##label##_CONST, \ + TLS_MD_##label##_CONST_SIZE)) + +static CK_RV p11prov_tls1_prf_derive_generic(P11PROV_KDF_CTX *tls12ctx, + unsigned char *key, size_t keylen) +{ + CK_RV rv; + + if (!HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, MASTER_SECRET) + && !HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, KEY_EXPANSION)) { + return CKR_DATA_INVALID; + } + + CK_TLS_KDF_PARAMS mechparams; + mechparams.prfMechanism = tls12ctx->hash_mech; + mechparams.pContextData = NULL_PTR; + mechparams.ulContextDataLength = 0; + mechparams.pLabel = tls12ctx->data; + mechparams.ulLabelLength = TLS_MD_MASTER_SECRET_CONST_SIZE; + mechparams.RandomInfo.ulClientRandomLen = SSL3_RANDOM_SIZE; + mechparams.RandomInfo.ulServerRandomLen = SSL3_RANDOM_SIZE; + mechparams.RandomInfo.pClientRandom = + tls12ctx->data + TLS_MD_MASTER_SECRET_CONST_SIZE; + mechparams.RandomInfo.pServerRandom = + tls12ctx->data + TLS_MD_MASTER_SECRET_CONST_SIZE + SSL3_RANDOM_SIZE; + + CK_MECHANISM mech = { .mechanism = CKM_TLS_KDF, + .pParameter = &mechparams, + .ulParameterLen = sizeof(mechparams) }; + + CK_OBJECT_HANDLE dkey_handle = CK_INVALID_HANDLE; + rv = inner_derive_key_expansion(tls12ctx->provctx, tls12ctx->key, + &tls12ctx->session, CKK_GENERIC_SECRET, + &mech, keylen, &dkey_handle); + if (rv != CKR_OK) { + return rv; + } + + return inner_extract_key_value(tls12ctx->provctx, tls12ctx->session, + dkey_handle, key, keylen); +} + +static CK_RV p11prov_tls1_prf_derive_master_secret(P11PROV_KDF_CTX *tls12ctx, + unsigned char *key, + size_t keylen) +{ + CK_RV rv; + + if (keylen != SSL3_MASTER_SECRET_SIZE) { + P11PROV_raise(tls12ctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid length for master secret"); + return CKR_ARGUMENTS_BAD; + } + + if (tls12ctx->datalen + != TLS_MD_MASTER_SECRET_CONST_SIZE + 2 * SSL3_RANDOM_SIZE) { + P11PROV_raise(tls12ctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid seed length for master secret"); + return CKR_ARGUMENTS_BAD; + } + + CK_TLS12_MASTER_KEY_DERIVE_PARAMS mechparams; + mechparams.pVersion = NULL_PTR; + mechparams.prfHashMechanism = tls12ctx->hash_mech; + mechparams.RandomInfo.ulClientRandomLen = SSL3_RANDOM_SIZE; + mechparams.RandomInfo.ulServerRandomLen = SSL3_RANDOM_SIZE; + mechparams.RandomInfo.pClientRandom = + tls12ctx->data + TLS_MD_MASTER_SECRET_CONST_SIZE; + mechparams.RandomInfo.pServerRandom = + tls12ctx->data + TLS_MD_MASTER_SECRET_CONST_SIZE + SSL3_RANDOM_SIZE; + + CK_MECHANISM mech = { .mechanism = CKM_TLS12_MASTER_KEY_DERIVE_DH, + .pParameter = &mechparams, + .ulParameterLen = sizeof(mechparams) }; + + CK_OBJECT_HANDLE dkey_handle = CK_INVALID_HANDLE; + rv = inner_derive_master_key(tls12ctx->provctx, tls12ctx->key, + &tls12ctx->session, &mech, keylen, + &dkey_handle); + if (rv != CKR_OK) { + return rv; + } + + return inner_extract_key_value(tls12ctx->provctx, tls12ctx->session, + dkey_handle, key, keylen); +} + +static CK_RV +p11prov_tls1_prf_derive_ext_master_secret(P11PROV_KDF_CTX *tls12ctx, + unsigned char *key, size_t keylen) +{ + CK_RV rv; + size_t digest_size = 0; + + if (keylen != SSL3_MASTER_SECRET_SIZE) { + P11PROV_raise(tls12ctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid length for extended master secret"); + return CKR_ARGUMENTS_BAD; + } + + rv = p11prov_digest_get_digest_size(tls12ctx->hash_mech, &digest_size); + if (rv != CKR_OK) { + return rv; + } + + if (tls12ctx->datalen + != TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE + digest_size) { + P11PROV_raise(tls12ctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid seed length for extended master secret"); + return CKR_ARGUMENTS_BAD; + } + + CK_TLS12_EXTENDED_MASTER_KEY_DERIVE_PARAMS mechparams; + mechparams.pVersion = NULL_PTR; + mechparams.prfHashMechanism = tls12ctx->hash_mech; + mechparams.pSessionHash = + tls12ctx->data + TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE; + mechparams.ulSessionHashLen = + tls12ctx->datalen - TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE; + + CK_MECHANISM mech = { .mechanism = CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH, + .pParameter = &mechparams, + .ulParameterLen = sizeof(mechparams) }; + + CK_OBJECT_HANDLE dkey_handle = CK_INVALID_HANDLE; + rv = inner_derive_master_key(tls12ctx->provctx, tls12ctx->key, + &tls12ctx->session, &mech, keylen, + &dkey_handle); + if (rv != CKR_OK) { + return rv; + } + + return inner_extract_key_value(tls12ctx->provctx, tls12ctx->session, + dkey_handle, key, keylen); +} + +static CK_RV p11prov_tls1_prf_derive_key_expansion(P11PROV_KDF_CTX *tls12ctx, + unsigned char *key, + size_t keylen) +{ + CK_RV rv; + + if (tls12ctx->datalen + != TLS_MD_KEY_EXPANSION_CONST_SIZE + 2 * SSL3_RANDOM_SIZE) { + P11PROV_raise(tls12ctx->provctx, CKR_ARGUMENTS_BAD, + "Invalid seed length for key expansion"); + return CKR_ARGUMENTS_BAD; + } + + /* + * OpenSSL does not give any hints about which ciphersuite is being used, + * it expects the provider to fill `keylen` bytes of output. + * + * If the generic CKM_TLS_KDF mechanism is not available and we have to + * use CKM_TLS12_KEY_AND_MAC_DERIVE, we need to know more information about + * the selected ciphersuite: the mac key size, the enc key size and the IV + * size. + * + * We can use the size of the requested key block to "guess" the ciphersuite. + */ + CK_ULONG macsz = 0, keysz = 0, ivsz = 0; + for (size_t i = 0; + i < sizeof(tls12_cipher_map) / sizeof(tls12_cipher_map[0]); i++) { + CK_ULONG keyblocklen = tls12_cipher_map[i].maclen + + tls12_cipher_map[i].keylen + + tls12_cipher_map[i].ivlen; + keyblocklen *= 2; + + if (keyblocklen == keylen) { + macsz = tls12_cipher_map[i].maclen; + keysz = tls12_cipher_map[i].keylen; + ivsz = tls12_cipher_map[i].ivlen; + break; + } + } + + /* Couldn't find a corresponding cipher for this length; macsz can be 0 */ + if (!keysz || !ivsz) { + P11PROV_raise(tls12ctx->provctx, CKR_GENERAL_ERROR, + "Cannot determine ciphersuite"); + return CKR_GENERAL_ERROR; + } + + CK_BYTE iv[EVP_MAX_IV_LENGTH * 2] = { 0 }; + CK_SSL3_KEY_MAT_OUT keymaterial = { 0 }; + keymaterial.pIVClient = iv; + keymaterial.pIVServer = iv + ivsz; + + CK_TLS12_KEY_MAT_PARAMS mechparams; + mechparams.bIsExport = CK_FALSE; + mechparams.prfHashMechanism = tls12ctx->hash_mech; + mechparams.pReturnedKeyMaterial = &keymaterial; + mechparams.RandomInfo.ulClientRandomLen = SSL3_RANDOM_SIZE; + mechparams.RandomInfo.ulServerRandomLen = SSL3_RANDOM_SIZE; + mechparams.RandomInfo.pServerRandom = + tls12ctx->data + TLS_MD_KEY_EXPANSION_CONST_SIZE; + mechparams.RandomInfo.pClientRandom = + tls12ctx->data + TLS_MD_KEY_EXPANSION_CONST_SIZE + SSL3_RANDOM_SIZE; + mechparams.ulMacSizeInBits = macsz * 8; + mechparams.ulKeySizeInBits = keysz * 8; + mechparams.ulIVSizeInBits = ivsz * 8; + + CK_MECHANISM mech = { .mechanism = CKM_TLS12_KEY_AND_MAC_DERIVE, + .pParameter = &mechparams, + .ulParameterLen = sizeof(mechparams) }; + + rv = inner_derive_key_expansion(tls12ctx->provctx, tls12ctx->key, + &tls12ctx->session, CKK_AES, &mech, keysz, + NULL_PTR); + if (rv != CKR_OK) { + P11PROV_raise(tls12ctx->provctx, rv, "Key expansion failed"); + return rv; + } + + unsigned char keyval[EVP_MAX_KEY_LENGTH] = { 0 }; + size_t offset = 0; + + if (mechparams.ulMacSizeInBits) { + rv = inner_extract_key_value( + tls12ctx->provctx, tls12ctx->session, + mechparams.pReturnedKeyMaterial->hClientMacSecret, keyval, macsz); + if (rv != CKR_OK) { + return rv; + } + memcpy(key + offset, keyval, macsz); + offset += macsz; + + rv = inner_extract_key_value( + tls12ctx->provctx, tls12ctx->session, + mechparams.pReturnedKeyMaterial->hServerMacSecret, keyval, macsz); + if (rv != CKR_OK) { + return rv; + } + memcpy(key + offset, keyval, macsz); + offset += macsz; + } + + rv = inner_extract_key_value(tls12ctx->provctx, tls12ctx->session, + mechparams.pReturnedKeyMaterial->hClientKey, + keyval, keysz); + if (rv != CKR_OK) { + return rv; + } + memcpy(key + offset, keyval, keysz); + offset += keysz; + + rv = inner_extract_key_value(tls12ctx->provctx, tls12ctx->session, + mechparams.pReturnedKeyMaterial->hServerKey, + keyval, keysz); + if (rv != CKR_OK) { + return rv; + } + memcpy(key + offset, keyval, keysz); + offset += keysz; + + memcpy(key + offset, mechparams.pReturnedKeyMaterial->pIVClient, ivsz); + offset += ivsz; + + memcpy(key + offset, mechparams.pReturnedKeyMaterial->pIVServer, ivsz); + offset += ivsz; + + /* At this point, offset should be equal to keylen */ + if (offset != keylen) { + P11PROV_raise(tls12ctx->provctx, CKR_GENERAL_ERROR, + "Key material size mismatch"); + return CKR_GENERAL_ERROR; + } + + return rv; +} + +static CK_RV p11prov_tls1_prf_derive_finished(P11PROV_KDF_CTX *tls12ctx, + unsigned char *key, size_t keylen, + CK_ULONG flag) +{ + if (flag != 1 && flag != 2) { + P11PROV_raise(tls12ctx->provctx, CKR_GENERAL_ERROR, + "Invalid server or client flag"); + return CKR_GENERAL_ERROR; + } + + CK_TLS_MAC_PARAMS mechparams; + mechparams.prfHashMechanism = tls12ctx->hash_mech; + mechparams.ulMacLength = TLS1_FINISH_MAC_LENGTH; + mechparams.ulServerOrClient = flag; + + CK_MECHANISM mech = { .mechanism = CKM_TLS_MAC, + .pParameter = &mechparams, + .ulParameterLen = sizeof(mechparams) }; + + /* The data passed to CKM_TLS_MAC must not include the "client" or "server" + * label, so we must skip over the first few bytes. TLS_MD_CLIENT_FINISH_CONST_SIZE + * and TLS_MD_SERVER_FINISH_CONST_SIZE have the same value, so use the first one. */ + return inner_tls_sign( + tls12ctx, &mech, tls12ctx->data + TLS_MD_CLIENT_FINISH_CONST_SIZE, + tls12ctx->datalen - TLS_MD_CLIENT_FINISH_CONST_SIZE, key, keylen); +} + +static int p11prov_tls1_prf_kdf_derive(void *ctx, unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + P11PROV_KDF_CTX *tls12ctx = (P11PROV_KDF_CTX *)ctx; + CK_RV rv = CKR_OK; + + P11PROV_debug("tls1_prf derive (ctx:%p, key:%p[%zu], params:%p)", ctx, key, + keylen, params); + + rv = p11prov_tls1_prf_kdf_set_ctx_params(ctx, params); + if (rv != RET_OSSL_OK) { + P11PROV_raise(tls12ctx->provctx, rv, "Invalid params"); + return RET_OSSL_ERR; + } + + if (tls12ctx->key == NULL || key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return RET_OSSL_ERR; + } + + if (keylen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return RET_OSSL_ERR; + } + + if (tls12ctx->datalen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA); + return RET_OSSL_ERR; + } + + /* Try the generic TLS1-PRF derivation mechanism if possible */ + CK_SLOT_ID slotid = p11prov_session_slotid(tls12ctx->session); + if (slotid != CK_UNAVAILABLE_INFORMATION) { + rv = p11prov_check_mechanism(tls12ctx->provctx, slotid, CKM_TLS_KDF); + if (rv == CKR_OK) { + rv = p11prov_tls1_prf_derive_generic(tls12ctx, key, keylen); + if (rv == CKR_OK) { + return RET_OSSL_OK; + } + } + } + + if (HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, MASTER_SECRET)) { + rv = p11prov_tls1_prf_derive_master_secret(tls12ctx, key, keylen); + } else if (HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, + EXTENDED_MASTER_SECRET)) { + rv = p11prov_tls1_prf_derive_ext_master_secret(tls12ctx, key, keylen); + } else if (HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, + KEY_EXPANSION)) { + rv = p11prov_tls1_prf_derive_key_expansion(tls12ctx, key, keylen); + } else if (HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, + SERVER_FINISH)) { + rv = p11prov_tls1_prf_derive_finished(tls12ctx, key, keylen, 1); + } else if (HAS_TLS12_LABEL(tls12ctx->data, tls12ctx->datalen, + CLIENT_FINISH)) { + rv = p11prov_tls1_prf_derive_finished(tls12ctx, key, keylen, 2); + } else { + P11PROV_raise(tls12ctx->provctx, CKR_ARGUMENTS_BAD, + "Unknown seed for TLS1.2 derivation"); + return RET_OSSL_ERR; + } + + if (rv == CKR_OK) { + return RET_OSSL_OK; + } + + return RET_OSSL_ERR; +} + +const OSSL_DISPATCH p11prov_tls1_prf_kdf_functions[] = { + DISPATCH_HKDF_ELEM(hkdf, NEWCTX, newctx), + DISPATCH_HKDF_ELEM(hkdf, FREECTX, freectx), + DISPATCH_HKDF_ELEM(hkdf, RESET, reset), + DISPATCH_HKDF_ELEM(tls1_prf_kdf, DERIVE, derive), + DISPATCH_HKDF_ELEM(tls1_prf_kdf, SET_CTX_PARAMS, set_ctx_params), + DISPATCH_HKDF_ELEM(tls1_prf_kdf, SETTABLE_CTX_PARAMS, settable_ctx_params), + { 0, NULL }, +}; diff --git a/src/kdf.h b/src/kdf.h index dfd3cb20..4dd96a45 100644 --- a/src/kdf.h +++ b/src/kdf.h @@ -1,4 +1,5 @@ /* Copyright (C) 2022 Simo Sorce + Copyright 2026 NXP SPDX-License-Identifier: Apache-2.0 */ #ifndef _KDF_H @@ -13,5 +14,7 @@ extern const void *p11prov_hkdf_static_ctx; extern const OSSL_DISPATCH p11prov_hkdf_kdf_functions[]; extern const OSSL_DISPATCH p11prov_tls13_kdf_functions[]; +extern const void *p11prov_tls1_prf_static_ctx; +extern const OSSL_DISPATCH p11prov_tls1_prf_kdf_functions[]; #endif /* _KDF_H */ diff --git a/src/kmgmt/keymgmt.h b/src/kmgmt/keymgmt.h index b9dfa966..156147e1 100644 --- a/src/kmgmt/keymgmt.h +++ b/src/kmgmt/keymgmt.h @@ -1,4 +1,5 @@ /* Copyright (C) 2022 Simo Sorce + Copyright 2026 NXP SPDX-License-Identifier: Apache-2.0 */ #ifndef _KEYMGMT_H @@ -19,6 +20,7 @@ extern const OSSL_DISPATCH p11prov_rsa_keymgmt_functions[]; extern const OSSL_DISPATCH p11prov_rsapss_keymgmt_functions[]; extern const OSSL_DISPATCH p11prov_ec_keymgmt_functions[]; extern const OSSL_DISPATCH p11prov_hkdf_keymgmt_functions[]; +extern const OSSL_DISPATCH p11prov_tls1_prf_keymgmt_functions[]; extern const OSSL_DISPATCH p11prov_ed25519_keymgmt_functions[]; extern const OSSL_DISPATCH p11prov_ed448_keymgmt_functions[]; extern const OSSL_DISPATCH p11prov_x25519_keymgmt_functions[]; diff --git a/src/kmgmt/tls1_prf.c b/src/kmgmt/tls1_prf.c new file mode 100644 index 00000000..8b4c5a57 --- /dev/null +++ b/src/kmgmt/tls1_prf.c @@ -0,0 +1,63 @@ +/* Copyright 2026 NXP + SPDX-License-Identifier: Apache-2.0 */ + +#include "provider.h" + +DISPATCH_KEYMGMT_FN(tls1_prf, new); +DISPATCH_KEYMGMT_FN(tls1_prf, free); +DISPATCH_KEYMGMT_FN(tls1_prf, query_operation_name); +DISPATCH_KEYMGMT_FN(tls1_prf, has); + +const void *p11prov_tls1_prf_static_ctx = NULL; + +static void *p11prov_tls1_prf_new(void *provctx) +{ + P11PROV_CTX *ctx = (P11PROV_CTX *)provctx; + CK_RV ret; + + P11PROV_debug("tls1_prf keymgmt new"); + + ret = p11prov_ctx_status(ctx); + if (ret != CKR_OK) { + return NULL; + } + + return (void *)&p11prov_tls1_prf_static_ctx; +} + +static void p11prov_tls1_prf_free(void *kdfdata) +{ + P11PROV_debug("tls1_prf keymgmt free %p", kdfdata); + + if (kdfdata != &p11prov_tls1_prf_static_ctx) { + P11PROV_debug("Invalid TLS1-PRF Keymgmt context: %p != %p", kdfdata, + &p11prov_tls1_prf_static_ctx); + } +} + +static const char *p11prov_tls1_prf_query_operation_name(int operation_id) +{ + P11PROV_debug("tls1_prf keymgmt query op name %d", operation_id); + + return P11PROV_NAMES_TLS1_PRF; +} + +static int p11prov_tls1_prf_has(const void *kdfdata, int selection) +{ + P11PROV_debug("tls1_prf keymgmt has"); + + if (kdfdata != &p11prov_tls1_prf_static_ctx) { + P11PROV_debug("Invalid TLS1-PRF Keymgmt context: %p != %p", kdfdata, + &p11prov_tls1_prf_static_ctx); + return RET_OSSL_ERR; + } + return RET_OSSL_OK; +} + +const OSSL_DISPATCH p11prov_tls1_prf_keymgmt_functions[] = { + DISPATCH_KEYMGMT_ELEM(tls1_prf, NEW, new), + DISPATCH_KEYMGMT_ELEM(tls1_prf, FREE, free), + DISPATCH_KEYMGMT_ELEM(tls1_prf, QUERY_OPERATION_NAME, query_operation_name), + DISPATCH_KEYMGMT_ELEM(tls1_prf, HAS, has), + { 0, NULL }, +}; diff --git a/src/meson.build b/src/meson.build index 3237ca05..c815273b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -14,6 +14,7 @@ pkcs11_provider_sources = [ 'kmgmt/mldsa.c', 'kmgmt/mlkem.c', 'kmgmt/rsa.c', + 'kmgmt/tls1_prf.c', 'pk11_uri.c', 'interface.c', 'obj/create.c', diff --git a/src/obj/create.c b/src/obj/create.c index 89471a2a..95229126 100644 --- a/src/obj/create.c +++ b/src/obj/create.c @@ -1,4 +1,5 @@ /* Copyright (C) 2022-2025 Simo Sorce + Copyright 2026 NXP SPDX-License-Identifier: Apache-2.0 */ #include "obj/internal.h" @@ -13,6 +14,7 @@ P11PROV_OBJ *p11prov_create_secret_key(P11PROV_CTX *provctx, CK_SESSION_INFO session_info; CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; CK_KEY_TYPE key_type = CKK_GENERIC_SECRET; + CK_BBOOL val_false = CK_FALSE; CK_BBOOL val_true = CK_TRUE; CK_BBOOL val_token = session_key ? CK_FALSE : CK_TRUE; CK_ATTRIBUTE template[] = { @@ -20,6 +22,9 @@ P11PROV_OBJ *p11prov_create_secret_key(P11PROV_CTX *provctx, { CKA_KEY_TYPE, &key_type, sizeof(key_type) }, { CKA_TOKEN, &val_token, sizeof(val_token) }, { CKA_DERIVE, &val_true, sizeof(val_true) }, + { CKA_SENSITIVE, &val_false, sizeof(val_false) }, + { CKA_EXTRACTABLE, &val_true, sizeof(val_true) }, + { CKA_SIGN, &val_true, sizeof(val_true) }, { CKA_VALUE, (void *)secret, secretlen }, }; CK_ULONG tcount = sizeof(template) / sizeof(CK_ATTRIBUTE); diff --git a/src/provider.c b/src/provider.c index a2517b1c..64bb432d 100644 --- a/src/provider.c +++ b/src/provider.c @@ -1,5 +1,5 @@ /* Copyright (C) 2022 Simo Sorce - Copyright 2025 NXP + Copyright 2025-2026 NXP SPDX-License-Identifier: Apache-2.0 */ #include "provider.h" @@ -867,6 +867,10 @@ static CK_RV alg_set_op(OSSL_ALGORITHM **op, int idx, OSSL_ALGORITHM *alg) CKM_AES_OFB, CKM_AES_CFB8, CKM_AES_CFB128, CKM_AES_CFB1, CKM_AES_GCM #endif +#define TLS12_MECHS \ + CKM_TLS12_MASTER_KEY_DERIVE_DH, CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH, \ + CKM_TLS12_KEY_AND_MAC_DERIVE, CKM_TLS_KDF, CKM_TLS_MAC + static void alg_rm_mechs(CK_ULONG *checklist, CK_ULONG *rmlist, int *clsize, int rmsize) { @@ -919,6 +923,7 @@ static CK_RV operations_init(P11PROV_CTX *ctx) CKM_ECDH1_COFACTOR_DERIVE, CKM_EC_MONTGOMERY_KEY_PAIR_GEN, CKM_HKDF_DERIVE, + TLS12_MECHS, DIGEST_MECHS, CKM_EDDSA, PQC_MECHS, @@ -1160,6 +1165,15 @@ static CK_RV operations_init(P11PROV_CTX *ctx) ADD_ALGO(HKDF, hkdf, exchange, prop); UNCHECK_MECHS(CKM_HKDF_DERIVE); break; + case CKM_TLS12_MASTER_KEY_DERIVE_DH: + case CKM_TLS12_EXTENDED_MASTER_KEY_DERIVE_DH: + case CKM_TLS12_KEY_AND_MAC_DERIVE: + case CKM_TLS_KDF: + case CKM_TLS_MAC: + ADD_ALGO(TLS1_PRF, tls1_prf, kdf, prop); + ADD_ALGO(TLS1_PRF, tls1_prf, exchange, prop); + UNCHECK_MECHS(TLS12_MECHS); + break; case CKM_SHA_1: ADD_ALGO(SHA1, sha1, digest, prop); UNCHECK_MECHS(CKM_SHA_1); @@ -1505,6 +1519,7 @@ static CK_RV static_operations_init(P11PROV_CTX *ctx) ADD_ALGO(RSAPSS, rsapss, keymgmt, prop); ADD_ALGO(EC, ec, keymgmt, prop); ADD_ALGO(HKDF, hkdf, keymgmt, prop); + ADD_ALGO(TLS1_PRF, tls1_prf, keymgmt, prop); ADD_ALGO_EXT(ED25519, keymgmt, prop, p11prov_ed25519_keymgmt_functions); ADD_ALGO_EXT(ED448, keymgmt, prop, p11prov_ed448_keymgmt_functions); ADD_ALGO_EXT(X25519, keymgmt, prop, p11prov_x25519_keymgmt_functions); diff --git a/src/provider.h b/src/provider.h index e020204b..bc2765f1 100644 --- a/src/provider.h +++ b/src/provider.h @@ -1,5 +1,5 @@ /* Copyright (C) 2022 Simo Sorce - Copyright 2025 NXP + Copyright 2025-2026 NXP SPDX-License-Identifier: Apache-2.0 */ #ifndef _PROVIDER_H @@ -157,6 +157,9 @@ #define P11PROV_NAME_TLS13_KDF "TLS13-KDF" #define P11PROV_NAMES_TLS13_KDF P11PROV_NAME_TLS13_KDF #define P11PROV_DESCS_TLS13_KDF "PKCS11 TLS 1.3 HKDF Implementation" +#define P11PROV_NAME_TLS1_PRF "TLS1-PRF" +#define P11PROV_NAMES_TLS1_PRF P11PROV_NAME_TLS1_PRF +#define P11PROV_DESCS_TLS1_PRF "PKCS11 TLS1 1.2 PRF Implementation" #define P11PROV_NAMES_DER "DER" #define P11PROV_DESCS_DER "DER decoder implementation in PKCS11 provider" #define P11PROV_NAMES_URI "pkcs11" From d43e851e20deb68e09ad6279f08f9aff96216770 Mon Sep 17 00:00:00 2001 From: Ilie Halip Date: Thu, 15 Jan 2026 11:59:07 +0200 Subject: [PATCH 2/2] Add tests for TLS1-PRF KDF (TLS 1.2) Run tests for a variety of digests and output lengths that do all the TLS1.2 derivation steps. Signed-off-by: Ilie Halip --- tests/meson.build | 1 + tests/ttls1prf | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100755 tests/ttls1prf diff --git a/tests/meson.build b/tests/meson.build index 2e4a91ac..debc3940 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -155,6 +155,7 @@ tests = { 'fork': {'suites': all_suites}, 'oaepsha2': {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, 'hkdf': {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, + 'tls1prf': {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, 'imported' : {'suites': ['softokn', 'kryoptic', 'kryoptic.nss']}, 'pem_encoder': {'suites': all_suites}, 'rsa': {'suites': all_suites}, diff --git a/tests/ttls1prf b/tests/ttls1prf new file mode 100755 index 00000000..c557e84b --- /dev/null +++ b/tests/ttls1prf @@ -0,0 +1,125 @@ +#!/bin/bash -e +# Copyright 2026 NXP +# SPDX-License-Identifier: Apache-2.0 + +source "${TESTSSRCDIR}/helpers.sh" + +title PARA "TLS1-PRF Derivation" + +PREMASTER_SECRET=aabbccddeeffaabbccddeeffaabbccdd +MASTER_SECRET=aabbccddeeffaabbccddeeffaabbccddeeffaabbccddeeff + +CLIENT_RANDOM=00000000000000000000000000000000 +SERVER_RANDOM=11111111111111111111111111111111 +SESSION_HASH=2222222222222222222222222222222222222222222222222222222222222222 +VERIFY_DATA=33333333333333333333333333333333 + +declare -a DIGESTS=( + SHA256 + SHA-256 + SHA2-256 + SHA384 + SHA-384 + SHA2-384 +) + +declare -a KDF_LENGTHS=( + 192 # AES-CBC-256 + 128 # AES-CBC-128 + 72 # AES-GCM-256 + 40 # AES-GCM-128 +) + +for digest in "${DIGESTS[@]}"; do + # Derive master secret + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 48 + -pkeyopt digest:${digest} + -pkeyopt secret:${PREMASTER_SECRET} + -pkeyopt seed:master\ secret${CLIENT_RANDOM}${SERVER_RANDOM} + -propquery provider=pkcs11 + -out ${TMPPDIR}/tls1-prf-1-out-pkcs11.bin" + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 48 + -pkeyopt digest:${digest} + -pkeyopt secret:${PREMASTER_SECRET} + -pkeyopt seed:master\ secret${CLIENT_RANDOM}${SERVER_RANDOM} + -out ${TMPPDIR}/tls1-prf-1-out.bin" + diff "${TMPPDIR}/tls1-prf-1-out-pkcs11.bin" "${TMPPDIR}/tls1-prf-1-out.bin" + + # Get a substring from SESSION_HASH with an appropriate length + digestlen=0 + if [[ $digest == *256 ]]; then + digestlen=32 + elif [[ $digest == *384 ]]; then + digestlen=48 + fi + echo $digestlen + sessionhash=${SESSION_HASH:0:$digestlen} + + # Derive extended master secret + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 48 + -pkeyopt digest:${digest} + -pkeyopt secret:${PREMASTER_SECRET} + -pkeyopt seed:extended\ master\ secret${sessionhash} + -propquery provider=pkcs11 + -out ${TMPPDIR}/tls1-prf-2-out-pkcs11.bin" + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 48 + -pkeyopt digest:${digest} + -pkeyopt secret:${PREMASTER_SECRET} + -pkeyopt seed:extended\ master\ secret${sessionhash} + -out ${TMPPDIR}/tls1-prf-2-out.bin" + diff "${TMPPDIR}/tls1-prf-2-out-pkcs11.bin" "${TMPPDIR}/tls1-prf-2-out.bin" + + for kdflen in "${KDF_LENGTHS[@]}"; do + # Derive key expansion + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen ${kdflen} + -pkeyopt digest:${digest} + -pkeyopt secret:${MASTER_SECRET} + -pkeyopt seed:key\ expansion${SERVER_RANDOM}${CLIENT_RANDOM} + -propquery provider=pkcs11 + -out ${TMPPDIR}/tls1-prf-3-out-pkcs11.bin" + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen ${kdflen} + -pkeyopt digest:${digest} + -pkeyopt secret:${MASTER_SECRET} + -pkeyopt seed:key\ expansion${SERVER_RANDOM}${CLIENT_RANDOM} + -out ${TMPPDIR}/tls1-prf-3-out.bin" + diff "${TMPPDIR}/tls1-prf-3-out-pkcs11.bin" "${TMPPDIR}/tls1-prf-3-out.bin" + + # Derive client finished + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 12 + -pkeyopt digest:${digest} + -pkeyopt secret:${MASTER_SECRET} + -pkeyopt seed:client\ finished${VERIFY_DATA} + -propquery provider=pkcs11 + -out ${TMPPDIR}/tls1-prf-4-out-pkcs11.bin" + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 12 + -pkeyopt digest:${digest} + -pkeyopt secret:${MASTER_SECRET} + -pkeyopt seed:client\ finished${VERIFY_DATA} + -out ${TMPPDIR}/tls1-prf-4-out.bin" + diff "${TMPPDIR}/tls1-prf-4-out-pkcs11.bin" "${TMPPDIR}/tls1-prf-4-out.bin" + + # Derive server finished + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 12 + -pkeyopt digest:${digest} + -pkeyopt secret:${MASTER_SECRET} + -pkeyopt seed:server\ finished${VERIFY_DATA} + -propquery provider=pkcs11 + -out ${TMPPDIR}/tls1-prf-5-out-pkcs11.bin" + ossl " + pkeyutl -derive -kdf TLS1-PRF -kdflen 12 + -pkeyopt digest:${digest} + -pkeyopt secret:${MASTER_SECRET} + -pkeyopt seed:server\ finished${VERIFY_DATA} + -out ${TMPPDIR}/tls1-prf-5-out.bin" + diff "${TMPPDIR}/tls1-prf-5-out-pkcs11.bin" "${TMPPDIR}/tls1-prf-5-out.bin" + done +done