1/*
2 * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9{-
10use OpenSSL::paramnames qw(produce_param_decoder);
11-}
12
13#include <stdlib.h>
14#include <string.h>
15#include <openssl/crypto.h>
16#include <openssl/err.h>
17#include <openssl/kdf.h>
18#include <openssl/proverr.h>
19#include <openssl/core_names.h>
20#include "internal/common.h"
21#include "prov/providercommon.h"
22#include "prov/implementations.h"
23#include "prov/hmac_drbg.h"
24#include "prov/provider_ctx.h"
25
26static OSSL_FUNC_kdf_newctx_fn hmac_drbg_kdf_new;
27static OSSL_FUNC_kdf_dupctx_fn hmac_drbg_kdf_dup;
28static OSSL_FUNC_kdf_freectx_fn hmac_drbg_kdf_free;
29static OSSL_FUNC_kdf_reset_fn hmac_drbg_kdf_reset;
30static OSSL_FUNC_kdf_derive_fn hmac_drbg_kdf_derive;
31static OSSL_FUNC_kdf_settable_ctx_params_fn hmac_drbg_kdf_settable_ctx_params;
32static OSSL_FUNC_kdf_set_ctx_params_fn hmac_drbg_kdf_set_ctx_params;
33static OSSL_FUNC_kdf_gettable_ctx_params_fn hmac_drbg_kdf_gettable_ctx_params;
34static OSSL_FUNC_kdf_get_ctx_params_fn hmac_drbg_kdf_get_ctx_params;
35
36typedef struct {
37    PROV_DRBG_HMAC base;
38    void *provctx;
39    unsigned char *entropy, *nonce;
40    size_t entropylen, noncelen;
41    int init;
42} KDF_HMAC_DRBG;
43
44static void *hmac_drbg_kdf_new(void *provctx)
45{
46    KDF_HMAC_DRBG *ctx;
47
48    if (!ossl_prov_is_running())
49        return NULL;
50
51    ctx = OPENSSL_zalloc(sizeof(*ctx));
52    if (ctx == NULL) {
53        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
54        return NULL;
55    }
56    ctx->provctx = provctx;
57    return ctx;
58}
59
60static void hmac_drbg_kdf_reset(void *vctx)
61{
62    KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx;
63    PROV_DRBG_HMAC *drbg = &ctx->base;
64    void *provctx = ctx->provctx;
65
66    EVP_MAC_CTX_free(drbg->ctx);
67    ossl_prov_digest_reset(&drbg->digest);
68    OPENSSL_clear_free(ctx->entropy, ctx->entropylen);
69    OPENSSL_clear_free(ctx->nonce, ctx->noncelen);
70    OPENSSL_cleanse(ctx, sizeof(*ctx));
71    ctx->provctx = provctx;
72}
73
74static void hmac_drbg_kdf_free(void *vctx)
75{
76    KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx;
77
78    if (ctx != NULL) {
79        hmac_drbg_kdf_reset(ctx);
80        OPENSSL_free(ctx);
81    }
82}
83
84static int ossl_drbg_hmac_dup(PROV_DRBG_HMAC *dst, const PROV_DRBG_HMAC *src) {
85    if (src->ctx != NULL) {
86        dst->ctx = EVP_MAC_CTX_dup(src->ctx);
87        if (dst->ctx == NULL)
88            return 0;
89    }
90    if (!ossl_prov_digest_copy(&dst->digest, &src->digest))
91        return 0;
92    memcpy(dst->K, src->K, sizeof(dst->K));
93    memcpy(dst->V, src->V, sizeof(dst->V));
94    dst->blocklen = src->blocklen;
95    return 1;
96}
97
98static void *hmac_drbg_kdf_dup(void *vctx)
99{
100    const KDF_HMAC_DRBG *src = (const KDF_HMAC_DRBG *)vctx;
101    KDF_HMAC_DRBG *dst;
102
103    dst = hmac_drbg_kdf_new(src->provctx);
104    if (dst != NULL) {
105        if (!ossl_drbg_hmac_dup(&dst->base, &src->base)
106                || !ossl_prov_memdup(src->entropy, src->entropylen,
107                                     &dst->entropy , &dst->entropylen)
108                || !ossl_prov_memdup(src->nonce, src->noncelen,
109                                     &dst->nonce, &dst->noncelen))
110            goto err;
111        dst->init = src->init;
112    }
113    return dst;
114
115 err:
116    hmac_drbg_kdf_free(dst);
117    return NULL;
118}
119
120static int hmac_drbg_kdf_derive(void *vctx, unsigned char *out, size_t outlen,
121                                const OSSL_PARAM params[])
122{
123    KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx;
124    PROV_DRBG_HMAC *drbg = &ctx->base;
125
126    if (!ossl_prov_is_running()
127            || !hmac_drbg_kdf_set_ctx_params(vctx, params))
128        return 0;
129    if (!ctx->init) {
130        if (ctx->entropy == NULL
131                || ctx->entropylen == 0
132                || ctx->nonce == NULL
133                || ctx->noncelen == 0
134                || !ossl_drbg_hmac_init(drbg, ctx->entropy, ctx->entropylen,
135                                        ctx->nonce, ctx->noncelen, NULL, 0))
136            return 0;
137        ctx->init = 1;
138    }
139
140    return ossl_drbg_hmac_generate(drbg, out, outlen, NULL, 0);
141}
142
143{- produce_param_decoder('hmac_drbg_kdf_get_ctx_params',
144                         (['KDF_PARAM_MAC',    'mac',    'utf8_string'],
145                          ['KDF_PARAM_DIGEST', 'digest', 'utf8_string'],
146                         )); -}
147
148static int hmac_drbg_kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
149{
150    KDF_HMAC_DRBG *hmac = (KDF_HMAC_DRBG *)vctx;
151    PROV_DRBG_HMAC *drbg = &hmac->base;
152    const char *name;
153    const EVP_MD *md;
154    struct hmac_drbg_kdf_get_ctx_params_st p;
155
156    if (hmac == NULL || !hmac_drbg_kdf_get_ctx_params_decoder(params, &p))
157        return 0;
158
159    if (p.mac != NULL) {
160        if (drbg->ctx == NULL)
161            return 0;
162        name = EVP_MAC_get0_name(EVP_MAC_CTX_get0_mac(drbg->ctx));
163        if (!OSSL_PARAM_set_utf8_string(p.mac, name))
164            return 0;
165    }
166
167    if (p.digest != NULL) {
168        md = ossl_prov_digest_md(&drbg->digest);
169        if (md == NULL
170                || !OSSL_PARAM_set_utf8_string(p.digest, EVP_MD_get0_name(md)))
171            return 0;
172    }
173    return 1;
174}
175
176static const OSSL_PARAM *hmac_drbg_kdf_gettable_ctx_params(
177    ossl_unused void *vctx, ossl_unused void *p_ctx)
178{
179    return hmac_drbg_kdf_get_ctx_params_list;
180}
181
182{- produce_param_decoder('hmac_drbg_kdf_set_ctx_params',
183                         (['KDF_PARAM_PROPERTIES',       'propq',  'utf8_string'],
184                          ['ALG_PARAM_ENGINE',           'engine', 'utf8_string', 'hidden'],
185                          ['KDF_PARAM_DIGEST',           'digest', 'utf8_string'],
186                          ['KDF_PARAM_HMACDRBG_ENTROPY', 'ent',    'octet_string'],
187                          ['KDF_PARAM_HMACDRBG_NONCE',   'nonce',  'octet_string'],
188                         )); -}
189
190static int hmac_drbg_kdf_set_ctx_params(void *vctx,
191                                        const OSSL_PARAM params[])
192{
193    KDF_HMAC_DRBG *hmac = (KDF_HMAC_DRBG *)vctx;
194    PROV_DRBG_HMAC *drbg;
195    OSSL_LIB_CTX *libctx;
196    const EVP_MD *md;
197    struct hmac_drbg_kdf_set_ctx_params_st p;
198    void *ptr = NULL;
199    size_t size = 0;
200    int md_size;
201
202    if (hmac == NULL || !hmac_drbg_kdf_set_ctx_params_decoder(params, &p))
203        return 0;
204
205    drbg = &hmac->base;
206    libctx = PROV_LIBCTX_OF(hmac->provctx);
207
208    if (p.ent != NULL) {
209        if (!OSSL_PARAM_get_octet_string(p.ent, &ptr, 0, &size))
210            return 0;
211        OPENSSL_free(hmac->entropy);
212        hmac->entropy = ptr;
213        hmac->entropylen = size;
214        hmac->init = 0;
215        ptr = NULL;
216    }
217
218    if (p.nonce != NULL) {
219        if (!OSSL_PARAM_get_octet_string(p.nonce, &ptr, 0, &size))
220            return 0;
221        OPENSSL_free(hmac->nonce);
222        hmac->nonce = ptr;
223        hmac->noncelen = size;
224        hmac->init = 0;
225    }
226
227    if (p.digest != NULL) {
228        if (!ossl_prov_digest_load(&drbg->digest, p.digest,
229                                   p.propq, p.engine, libctx))
230            return 0;
231
232        /* Confirm digest is allowed. Allow all digests that are not XOF */
233        md = ossl_prov_digest_md(&drbg->digest);
234        if (md != NULL) {
235            if (EVP_MD_xof(md)) {
236                ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
237                return 0;
238            }
239            md_size = EVP_MD_get_size(md);
240            if (md_size <= 0)
241                return 0;
242            drbg->blocklen = (size_t)md_size;
243        }
244        if (!ossl_prov_macctx_load(&drbg->ctx, NULL, NULL, p.digest, p.propq,
245                                   p.engine, "HMAC", NULL, NULL, libctx))
246            return 0;
247    }
248    return 1;
249}
250
251static const OSSL_PARAM *hmac_drbg_kdf_settable_ctx_params(
252    ossl_unused void *vctx, ossl_unused void *p_ctx)
253{
254    return hmac_drbg_kdf_set_ctx_params_list;
255}
256
257const OSSL_DISPATCH ossl_kdf_hmac_drbg_functions[] = {
258    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))hmac_drbg_kdf_new },
259    { OSSL_FUNC_KDF_FREECTX, (void(*)(void))hmac_drbg_kdf_free },
260    { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))hmac_drbg_kdf_dup },
261    { OSSL_FUNC_KDF_RESET, (void(*)(void))hmac_drbg_kdf_reset },
262    { OSSL_FUNC_KDF_DERIVE, (void(*)(void))hmac_drbg_kdf_derive },
263    { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
264      (void(*)(void))hmac_drbg_kdf_settable_ctx_params },
265    { OSSL_FUNC_KDF_SET_CTX_PARAMS,
266      (void(*)(void))hmac_drbg_kdf_set_ctx_params },
267    { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
268      (void(*)(void))hmac_drbg_kdf_gettable_ctx_params },
269    { OSSL_FUNC_KDF_GET_CTX_PARAMS,
270      (void(*)(void))hmac_drbg_kdf_get_ctx_params },
271    OSSL_DISPATCH_END
272};
273