From 1f7657f8da219605c7e2727b52b564f25d5d47a5 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Tue, 24 Sep 2024 17:41:14 -0400 Subject: [PATCH] WIP:^Cdd TLS13-KDF Signed-off-by: Simo Sorce --- src/kdf.c | 376 +++++++++++++++++++++++++++++++++++++++++------------- src/kdf.h | 1 + 2 files changed, 290 insertions(+), 87 deletions(-) diff --git a/src/kdf.c b/src/kdf.c index 609d924d..5c5987c6 100644 --- a/src/kdf.c +++ b/src/kdf.c @@ -2,6 +2,7 @@ SPDX-License-Identifier: Apache-2.0 */ #include "provider.h" +#include "platform/endian.h" #include #include @@ -12,9 +13,23 @@ struct p11prov_kdf_ctx { CK_MECHANISM_TYPE mechtype; - CK_HKDF_PARAMS params; + int mode; + CK_MECHANISM_TYPE hash_mech; + CK_ULONG salt_type; + uint8_t *salt; + size_t saltlen; + uint8_t *info; + size_t infolen; + uint8_t *prefix; + uint8_t *label; + uint8_t *data; + size_t prefixlen; + size_t labellen; + size_t datalen; P11PROV_SESSION *session; + + bool is_tls13_kdf; }; typedef struct p11prov_kdf_ctx P11PROV_KDF_CTX; @@ -76,8 +91,11 @@ static void p11prov_hkdf_reset(void *ctx) hkdfctx->session = NULL; } - OPENSSL_clear_free(hkdfctx->params.pSalt, hkdfctx->params.ulSaltLen); - OPENSSL_clear_free(hkdfctx->params.pInfo, hkdfctx->params.ulInfoLen); + OPENSSL_clear_free(hkdfctx->salt, hkdfctx->saltlen); + OPENSSL_clear_free(hkdfctx->info, hkdfctx->infolen); + OPENSSL_clear_free(hkdfctx->prefix, hkdfctx->prefixlen); + OPENSSL_clear_free(hkdfctx->label, hkdfctx->labellen); + OPENSSL_clear_free(hkdfctx->data, hkdfctx->datalen); /* zero all */ memset(hkdfctx, 0, sizeof(*hkdfctx)); @@ -87,10 +105,39 @@ static void p11prov_hkdf_reset(void *ctx) hkdfctx->mechtype = CKM_HKDF_DERIVE; } -static int p11prov_hkdf_derive(void *ctx, unsigned char *key, size_t keylen, - const OSSL_PARAM params[]) +static int inner_extract_key_value(P11PROV_CTX *ctx, + P11PROV_SESSION *session, + CK_OBJECT_HANDLE dkey_handle, + unsigned char *key, size_t keylen) +{ + CK_ULONG key_size; + struct fetch_attrs attrs[1]; + int num = 0; + CK_RV ret; + + P11PROV_debug("HKDF derived hey handle: %lu", dkey_handle); + FA_SET_BUF_VAL(attrs, num, CKA_VALUE, key, keylen, true); + ret = p11prov_fetch_attributes(ctx, session, dkey_handle, attrs, num); + if (ret != CKR_OK) { + P11PROV_raise(ctx, ret, "Failed to retrieve derived key"); + return RET_OSSL_ERR; + } + FA_GET_LEN(attrs, 0, key_size); + if (key_size != keylen) { + ret = CKR_GENERAL_ERROR; + P11PROV_raise(ctx, ret, + "Expected derived key of len %zu, but got %lu", keylen, + key_size); + return RET_OSSL_ERR; + } + + return RET_OSSL_OK; +} + +static int inner_derive_key(P11PROV_CTX *ctx, P11PROV_OBJ *key, + P11PROV_SESSION **session, CK_MECHANISM *mechanism, + size_t keylen, CK_OBJECT_HANDLE *dkey_handle) { - P11PROV_KDF_CTX *hkdfctx = (P11PROV_KDF_CTX *)ctx; CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; CK_KEY_TYPE key_type = CKK_GENERIC_SECRET; CK_BBOOL val_true = CK_TRUE; @@ -103,17 +150,57 @@ static int p11prov_hkdf_derive(void *ctx, unsigned char *key, size_t keylen, { CKA_EXTRACTABLE, &val_true, sizeof(val_true) }, { CKA_VALUE_LEN, &key_size, sizeof(key_size) }, }; - CK_MECHANISM mechanism; CK_OBJECT_HANDLE pkey_handle; - CK_OBJECT_HANDLE dkey_handle; CK_SLOT_ID slotid; - struct fetch_attrs attrs[1]; - int num = 0; - CK_RV ret; + + pkey_handle = p11prov_obj_get_handle(key); + if (pkey_handle == CK_INVALID_HANDLE) { + P11PROV_raise(ctx, CKR_KEY_HANDLE_INVALID, "Invalid key handle"); + return RET_OSSL_ERR; + } + + slotid = p11prov_obj_get_slotid(key); + if (slotid == CK_UNAVAILABLE_INFORMATION) { + P11PROV_raise(ctx, CKR_SLOT_ID_INVALID, "Invalid key slotid"); + return RET_OSSL_ERR; + } + + return p11prov_derive_key(ctx, slotid, mechanism, pkey_handle, + key_template, 5, session, dkey_handle); +} + +static int p11prov_hkdf_derive(void *ctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + P11PROV_KDF_CTX *hkdfctx = (P11PROV_KDF_CTX *)ctx; + CK_HKDF_PARAMS ck_params = { + .bExtract = (hkdfctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND || hkdfctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_ONLY) ? CK_TRUE : CK_FALSE, + .bExpand = (hkdfctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND || hkdfctx->mode == EVP_KDF_HKDF_MODE_EXPAND_ONLY) ? CK_TRUE : CK_FALSE, + .prfHashMechanism = hkdfctx->hash_mech, + .ulSaltType = hkdfctx->salt_type, + .pSalt = hkdfctx->salt, + .ulSaltLen = hkdfctx->saltlen, + .pInfo = hkdfctx->info, + .ulInfoLen = hkdfctx->infolen, + }; + CK_MECHANISM mechanism = { + .mechanism = hkdfctx->mechtype, + .pParameter = &ck_params, + .ulParameterLen = sizeof(ck_params), + }; + + CK_OBJECT_HANDLE dkey_handle; + int ret; P11PROV_debug("hkdf derive (ctx:%p, key:%p[%zu], params:%p)", ctx, key, keylen, params); + ret = p11prov_hkdf_set_ctx_params(ctx, params); + if (ret != RET_OSSL_OK) { + P11PROV_raise(hkdfctx->provctx, ret, "Invalid params"); + return RET_OSSL_ERR; + } + if (hkdfctx->key == NULL || key == NULL) { ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); return RET_OSSL_ERR; @@ -124,53 +211,85 @@ static int p11prov_hkdf_derive(void *ctx, unsigned char *key, size_t keylen, return RET_OSSL_ERR; } - mechanism.mechanism = hkdfctx->mechtype; - mechanism.pParameter = &hkdfctx->params; - mechanism.ulParameterLen = sizeof(hkdfctx->params); - - pkey_handle = p11prov_obj_get_handle(hkdfctx->key); - if (pkey_handle == CK_INVALID_HANDLE) { - P11PROV_raise(hkdfctx->provctx, CKR_KEY_HANDLE_INVALID, - "Provided key has invalid handle"); - return RET_OSSL_ERR; - } - /* no salt ? */ - if (hkdfctx->params.ulSaltType == 0) { - hkdfctx->params.ulSaltType = CKF_HKDF_SALT_NULL; + if (hkdfctx->salt_type == 0) { + ck_params.ulSaltType = CKF_HKDF_SALT_NULL; } - slotid = p11prov_obj_get_slotid(hkdfctx->key); - if (slotid == CK_UNAVAILABLE_INFORMATION) { - P11PROV_raise(hkdfctx->provctx, CKR_SLOT_ID_INVALID, - "Provided key has invalid slot"); - return RET_OSSL_ERR; + ret = inner_derive_key(hkdfctx->provctx, hkdfctx->key, &hkdfctx->session, + &mechanism, keylen, &dkey_handle); + if (ret != RET_OSSL_OK) { + return ret; } - ret = p11prov_derive_key(hkdfctx->provctx, slotid, &mechanism, pkey_handle, - key_template, 5, &hkdfctx->session, &dkey_handle); - if (ret != CKR_OK) { - return RET_OSSL_ERR; + return inner_extract_key_value(hkdfctx->provctx, hkdfctx->session, + dkey_handle, key, keylen); +} + +static CK_RV tls13_expand_label(P11PROV_KDF_CTX *hkdfctx, + uint8_t *prefix, size_t prefixlen, + uint8_t *label, size_t labellen, + uint8_t *data, size_t datalen, + size_t keylen, CK_OBJECT_HANDLE *dkey_handle) +{ + CK_HKDF_PARAMS params = { + .bExtract = CK_FALSE, + .bExpand = CK_TRUE, + .prfHashMechanism = hkdfctx->hash_mech, + }; + CK_MECHANISM mechanism = { + .mechanism = hkdfctx->mechtype, + .pParameter = ¶ms, + .ulParameterLen = sizeof(params), + }; + uint8_t info[2+1+255+1+255]; + size_t infolen, i; + uint16_t keysize; + CK_RV ret; + + P11PROV_debug( + "tls13 expand label (prefix:%p[%zu], label:%p[%zu], data:%p[%zu])", + prefix, prefixlen, label, labellen, data, datalen); + + if (prefix == NULL || prefixlen == 0 || label == NULL || labellen == 0 || + (prefixlen + labellen > 255) || (datalen > 0 && data == NULL) || + (datalen == 0 && data != NULL) || (datalen > 255) || (keylen > 65535)) { + return CKR_ARGUMENTS_BAD; } - P11PROV_debug("HKDF derived hey handle: %lu", dkey_handle); - FA_SET_BUF_VAL(attrs, num, CKA_VALUE, key, keylen, true); - ret = p11prov_fetch_attributes(hkdfctx->provctx, hkdfctx->session, - dkey_handle, attrs, num); - if (ret != CKR_OK) { - P11PROV_raise(hkdfctx->provctx, ret, "Failed to retrieve derived key"); - return RET_OSSL_ERR; + /* No salt info for Expand only */ + + /* ref: RFC 8446 - 7.1 Key Schedule */ + infolen = 2 + 1 + prefixlen + labellen + 1 + datalen; + i = 0; + keysize = htobe16(keylen); + memcpy(&info[i], &keysize, sizeof(uint16_t)); + i += sizeof(uint16_t); + info[i] = (uint8_t)(prefixlen + labellen); + i += 1; + memcpy(&info[i], prefix, prefixlen); + i += prefixlen; + memcpy(&info[i], label, labellen); + i += labellen; + info[i] = (uint8_t)(datalen); + i += 1; + if (datalen > 0) { + memcpy(&info[i], data, datalen); + i += datalen; } - FA_GET_LEN(attrs, 0, key_size); - if (key_size != keylen) { - ret = CKR_GENERAL_ERROR; - P11PROV_raise(hkdfctx->provctx, ret, - "Expected derived key of len %zu, but got %lu", keylen, - key_size); - return RET_OSSL_ERR; + if (infolen != i) { + OPENSSL_cleanse(&info, i); + return CKR_HOST_MEMORY; } - return RET_OSSL_OK; + params.pInfo = info; + params.ulInfoLen = infolen; + + ret = inner_derive_key(hkdfctx->provctx, hkdfctx->key, &hkdfctx->session, + &mechanism, keylen, dkey_handle); + + OPENSSL_cleanse(&info, infolen); + return ret; } static int p11prov_hkdf_set_ctx_params(void *ctx, const OSSL_PARAM params[]) @@ -185,6 +304,8 @@ static int p11prov_hkdf_set_ctx_params(void *ctx, const OSSL_PARAM params[]) return RET_OSSL_OK; } + /* params common to HKDF and TLS13_KDF first */ + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DIGEST); if (p) { const char *digest = NULL; @@ -196,54 +317,45 @@ static int p11prov_hkdf_set_ctx_params(void *ctx, const OSSL_PARAM params[]) } rv = p11prov_digest_get_by_name(digest, - &hkdfctx->params.prfHashMechanism); + &hkdfctx->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", hkdfctx->params.prfHashMechanism); + P11PROV_debug("set digest to %lu", hkdfctx->hash_mech); } p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE); if (p) { - int mode; if (p->data_type == OSSL_PARAM_UTF8_STRING) { if (OPENSSL_strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) { - mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND; + hkdfctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND; } else if (OPENSSL_strcasecmp(p->data, "EXTRACT_ONLY") == 0) { - mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + hkdfctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; } else if (OPENSSL_strcasecmp(p->data, "EXPAND_ONLY") == 0) { - mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + hkdfctx->mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; } else { - mode = 1; + hkdfctx->mode = 1; } } else { - ret = OSSL_PARAM_get_int(p, &mode); + ret = OSSL_PARAM_get_int(p, &hkdfctx->mode); if (ret != RET_OSSL_OK) { return ret; } } - switch (mode) { + switch (hkdfctx->mode) { case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND: - hkdfctx->params.bExtract = CK_TRUE; - hkdfctx->params.bExpand = CK_TRUE; break; case EVP_KDF_HKDF_MODE_EXTRACT_ONLY: - hkdfctx->params.bExtract = CK_TRUE; - hkdfctx->params.bExpand = CK_FALSE; break; case EVP_KDF_HKDF_MODE_EXPAND_ONLY: - hkdfctx->params.bExtract = CK_FALSE; - hkdfctx->params.bExpand = CK_TRUE; break; default: ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); return RET_OSSL_ERR; } - P11PROV_debug("set mode to extract:%d expand:%d", - (int)hkdfctx->params.bExtract, - (int)hkdfctx->params.bExpand); + P11PROV_debug("set mode to mode:%d", hkdfctx->mode); } p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY); @@ -270,6 +382,7 @@ static int p11prov_hkdf_set_ctx_params(void *ctx, const OSSL_PARAM params[]) return RET_OSSL_ERR; } + p11prov_obj_free(hkdfctx->key); hkdfctx->key = p11prov_create_secret_key( hkdfctx->provctx, hkdfctx->session, true, secret, secret_len); OPENSSL_clear_free(secret, secret_len); @@ -280,23 +393,75 @@ static int p11prov_hkdf_set_ctx_params(void *ctx, const OSSL_PARAM params[]) p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT); if (p) { - void *ptr = NULL; - size_t len; - OPENSSL_cleanse(hkdfctx->params.pSalt, hkdfctx->params.ulSaltLen); - hkdfctx->params.pSalt = NULL; - ret = OSSL_PARAM_get_octet_string(p, &ptr, 0, &len); + OPENSSL_clear_free(hkdfctx->salt, hkdfctx->saltlen); + hkdfctx->salt = NULL; + ret = OSSL_PARAM_get_octet_string(p, (void **)&hkdfctx->salt, 0, + &hkdfctx->saltlen); if (ret != RET_OSSL_OK) { return ret; } - hkdfctx->params.ulSaltType = CKF_HKDF_SALT_DATA; - hkdfctx->params.pSalt = ptr; - hkdfctx->params.ulSaltLen = len; - P11PROV_debug("set salt (len:%lu)", hkdfctx->params.ulSaltLen); + hkdfctx->salt_type = CKF_HKDF_SALT_DATA; + P11PROV_debug("set salt (len:%lu)", hkdfctx->saltlen); } + if (hkdfctx->is_tls13_kdf) { + + if (hkdfctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return RET_OSSL_ERR; + } + + OPENSSL_clear_free(hkdfctx->info, hkdfctx->infolen); + hkdfctx->info = NULL; + hkdfctx->infolen = 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PREFIX); + if (p) { + OPENSSL_clear_free(hkdfctx->prefix, hkdfctx->prefixlen); + hkdfctx->prefix = NULL; + hkdfctx->prefixlen = 0; + ret = OSSL_PARAM_get_octet_string(p, (void **)&hkdfctx->prefix, 0, + &hkdfctx->prefixlen); + if (ret != RET_OSSL_OK) { + return ret; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_LABEL); + if (p) { + OPENSSL_clear_free(hkdfctx->label, hkdfctx->labellen); + hkdfctx->label = NULL; + hkdfctx->labellen = 0; + ret = OSSL_PARAM_get_octet_string(p, (void **)&hkdfctx->label, 0, + &hkdfctx->labellen); + if (ret != RET_OSSL_OK) { + return ret; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DATA); + if (p) { + OPENSSL_clear_free(hkdfctx->data, hkdfctx->datalen); + hkdfctx->data = NULL; + hkdfctx->datalen = 0; + ret = OSSL_PARAM_get_octet_string(p, (void **)&hkdfctx->data, 0, + &hkdfctx->datalen); + if (ret != RET_OSSL_OK) { + return ret; + } + } + + return RET_OSSL_OK; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_INFO); + if (p) { + OPENSSL_clear_free(hkdfctx->info, hkdfctx->infolen); + hkdfctx->info = NULL; + hkdfctx->infolen = 0; + } /* can be multiple parameters, which will be all concatenated */ - for (p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_INFO); p != NULL; - p = OSSL_PARAM_locate_const(p + 1, OSSL_KDF_PARAM_INFO)) { + for (; p; p = OSSL_PARAM_locate_const(p + 1, OSSL_KDF_PARAM_INFO)) { uint8_t *ptr; size_t len; @@ -304,16 +469,18 @@ static int p11prov_hkdf_set_ctx_params(void *ctx, const OSSL_PARAM params[]) return RET_OSSL_ERR; } - len = hkdfctx->params.ulInfoLen + p->data_size; - ptr = OPENSSL_realloc(hkdfctx->params.pInfo, len); + len = hkdfctx->infolen + p->data_size; + ptr = OPENSSL_realloc(hkdfctx->info, len); if (ptr == NULL) { - OPENSSL_cleanse(hkdfctx->params.pInfo, hkdfctx->params.ulInfoLen); + OPENSSL_clear_free(hkdfctx->info, hkdfctx->infolen); + hkdfctx->info = NULL; + hkdfctx->infolen = 0; return RET_OSSL_ERR; } - memcpy(ptr + hkdfctx->params.ulInfoLen, p->data, p->data_size); - hkdfctx->params.pInfo = ptr; - hkdfctx->params.ulInfoLen = len; - P11PROV_debug("set info (len:%lu)", hkdfctx->params.ulInfoLen); + memcpy(ptr + hkdfctx->infolen, p->data, p->data_size); + hkdfctx->info = ptr; + hkdfctx->infolen = len; + P11PROV_debug("set info (len:%lu)", hkdfctx->infolen); } return RET_OSSL_OK; @@ -348,13 +515,12 @@ static int p11prov_hkdf_get_ctx_params(void *ctx, OSSL_PARAM *params) p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE); if (p) { size_t ret_size = 0; - if (hkdfctx->params.bExpand != CK_FALSE) { + if (hkdfctx->mode != EVP_KDF_HKDF_MODE_EXTRACT_ONLY) { ret_size = SIZE_MAX; } else { CK_RV rv; - rv = p11prov_digest_get_digest_size( - hkdfctx->params.prfHashMechanism, &ret_size); + rv = p11prov_digest_get_digest_size(hkdfctx->hash_mech, &ret_size); if (rv != CKR_OK) { ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST); return RET_OSSL_ERR; @@ -390,3 +556,39 @@ const OSSL_DISPATCH p11prov_hkdf_kdf_functions[] = { DISPATCH_HKDF_ELEM(hkdf, GETTABLE_CTX_PARAMS, gettable_ctx_params), { 0, NULL }, }; + +static void *p11prov_tls13_kdf_newctx(void *provctx) +{ + P11PROV_KDF_CTX *ctx = (P11PROV_KDF_CTX *)p11prov_hkdf_newctx(provctx); + ctx->is_tls13_kdf = true; + return ctx; +} + +static const OSSL_PARAM *p11prov_tls13_kdf_settable_ctx_params(void *ctx, void *prov) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0), + OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_DATA, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +const OSSL_DISPATCH p11prov_tls13_kdf_functions[] = { + DISPATCH_HKDF_ELEM(tls13_kdf, NEWCTX, newctx), + DISPATCH_HKDF_ELEM(hkdf, FREECTX, freectx), + DISPATCH_HKDF_ELEM(hkdf, RESET, reset), + DISPATCH_HKDF_ELEM(hkdf, DERIVE, derive), + DISPATCH_HKDF_ELEM(hkdf, SET_CTX_PARAMS, set_ctx_params), + DISPATCH_HKDF_ELEM(tls13_kdf, SETTABLE_CTX_PARAMS, settable_ctx_params), + DISPATCH_HKDF_ELEM(hkdf, GET_CTX_PARAMS, get_ctx_params), + DISPATCH_HKDF_ELEM(hkdf, GETTABLE_CTX_PARAMS, gettable_ctx_params), + { 0, NULL }, +}; diff --git a/src/kdf.h b/src/kdf.h index cc938780..dfd3cb20 100644 --- a/src/kdf.h +++ b/src/kdf.h @@ -12,5 +12,6 @@ } extern const void *p11prov_hkdf_static_ctx; extern const OSSL_DISPATCH p11prov_hkdf_kdf_functions[]; +extern const OSSL_DISPATCH p11prov_tls13_kdf_functions[]; #endif /* _KDF_H */