+
+/*
+ * TLS uses slight variations of the above and for FIPS validation purposes,
+ * they need to be present here.
+ * Refer to RFC 8446 section 7 for specific details.
+ */
+
+/*
+ * Given a |secret|; a |label| of length |labellen|; and |data| of length
+ * |datalen| (e.g. typically a hash of the handshake messages), derive a new
+ * secret |outlen| bytes long and store it in the location pointed to be |out|.
+ * The |data| value may be zero length. Returns 1 on success and 0 on failure.
+ */
+static int prov_tls13_hkdf_expand(const EVP_MD *md,
+ const unsigned char *key, size_t keylen,
+ const unsigned char *prefix, size_t prefixlen,
+ const unsigned char *label, size_t labellen,
+ const unsigned char *data, size_t datalen,
+ unsigned char *out, size_t outlen)
+{
+ size_t hkdflabellen;
+ unsigned char hkdflabel[HKDF_MAXBUF];
+ WPACKET pkt;
+
+ /*
+ * 2 bytes for length of derived secret + 1 byte for length of combined
+ * prefix and label + bytes for the label itself + 1 byte length of hash
+ * + bytes for the hash itself. We've got the maximum the KDF can handle
+ * which should always be sufficient.
+ */
+ if (!WPACKET_init_static_len(&pkt, hkdflabel, sizeof(hkdflabel), 0)
+ || !WPACKET_put_bytes_u16(&pkt, outlen)
+ || !WPACKET_start_sub_packet_u8(&pkt)
+ || !WPACKET_memcpy(&pkt, prefix, prefixlen)
+ || !WPACKET_memcpy(&pkt, label, labellen)
+ || !WPACKET_close(&pkt)
+ || !WPACKET_sub_memcpy_u8(&pkt, data, (data == NULL) ? 0 : datalen)
+ || !WPACKET_get_total_written(&pkt, &hkdflabellen)
+ || !WPACKET_finish(&pkt)) {
+ WPACKET_cleanup(&pkt);
+ return 0;
+ }
+
+ return HKDF_Expand(md, key, keylen, hkdflabel, hkdflabellen,
+ out, outlen);
+}
+
+static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx,
+ const EVP_MD *md,
+ const unsigned char *prevsecret,
+ size_t prevsecretlen,
+ const unsigned char *insecret,
+ size_t insecretlen,
+ const unsigned char *prefix,
+ size_t prefixlen,
+ const unsigned char *label,
+ size_t labellen,
+ unsigned char *out, size_t outlen)
+{
+ size_t mdlen;
+ int ret;
+ unsigned char preextractsec[EVP_MAX_MD_SIZE];
+ /* Always filled with zeros */
+ static const unsigned char default_zeros[EVP_MAX_MD_SIZE];
+
+ ret = EVP_MD_get_size(md);
+ /* Ensure cast to size_t is safe */
+ if (ret <= 0)
+ return 0;
+ mdlen = (size_t)ret;
+
+ if (insecret == NULL) {
+ insecret = default_zeros;
+ insecretlen = mdlen;
+ }
+ if (prevsecret == NULL) {
+ prevsecret = default_zeros;
+ prevsecretlen = mdlen;
+ } else {
+ EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+ unsigned char hash[EVP_MAX_MD_SIZE];
+
+ /* The pre-extract derive step uses a hash of no messages */
+ if (mctx == NULL
+ || EVP_DigestInit_ex(mctx, md, NULL) <= 0
+ || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) {
+ EVP_MD_CTX_free(mctx);
+ return 0;
+ }
+ EVP_MD_CTX_free(mctx);
+
+ /* Generate the pre-extract secret */
+ if (!prov_tls13_hkdf_expand(md, prevsecret, mdlen,
+ prefix, prefixlen, label, labellen,
+ hash, mdlen, preextractsec, mdlen))
+ return 0;
+ prevsecret = preextractsec;
+ prevsecretlen = mdlen;
+ }
+
+ ret = HKDF_Extract(libctx, md, prevsecret, prevsecretlen,
+ insecret, insecretlen, out, outlen);
+
+ if (prevsecret == preextractsec)
+ OPENSSL_cleanse(preextractsec, mdlen);
+ return ret;
+}
+
+static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_tls1_3_set_ctx_params(ctx, params))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+
+ switch (ctx->mode) {
+ default:
+ return 0;
+
+ case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
+ return prov_tls13_hkdf_generate_secret(PROV_LIBCTX_OF(ctx->provctx),
+ md,
+ ctx->salt, ctx->salt_len,
+ ctx->key, ctx->key_len,
+ ctx->prefix, ctx->prefix_len,
+ ctx->label, ctx->label_len,
+ key, keylen);
+
+ case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
+ return prov_tls13_hkdf_expand(md, ctx->key, ctx->key_len,
+ ctx->prefix, ctx->prefix_len,
+ ctx->label, ctx->label_len,
+ ctx->data, ctx->data_len,
+ key, keylen);
+ }
+}
+
+static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_HKDF *ctx = vctx;
+
+ if (params == NULL)
+ return 1;
+
+ if (!hkdf_common_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PREFIX)) != NULL) {
+ OPENSSL_free(ctx->prefix);
+ ctx->prefix = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->prefix, 0,
+ &ctx->prefix_len))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_LABEL)) != NULL) {
+ OPENSSL_free(ctx->label);
+ ctx->label = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->label, 0,
+ &ctx->label_len))
+ return 0;
+ }
+
+ OPENSSL_clear_free(ctx->data, ctx->data_len);
+ ctx->data = NULL;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DATA)) != NULL
+ && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->data, 0,
+ &ctx->data_len))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ HKDF_COMMON_SETTABLES,
+ 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 known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_3_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_3_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_hkdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};