xref: /freebsd/crypto/openssl/providers/implementations/kem/ml_kem_kem.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1 /*
2  * Copyright 2024-2025 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 
10 #include <string.h>
11 #include <openssl/crypto.h>
12 #include <openssl/evp.h>
13 #include <openssl/core_dispatch.h>
14 #include <openssl/core_names.h>
15 #include <openssl/params.h>
16 #include <openssl/err.h>
17 #include <openssl/proverr.h>
18 #include "crypto/ml_kem.h"
19 #include "prov/provider_ctx.h"
20 #include "prov/implementations.h"
21 #include "prov/securitycheck.h"
22 #include "prov/providercommon.h"
23 
24 static OSSL_FUNC_kem_newctx_fn ml_kem_newctx;
25 static OSSL_FUNC_kem_freectx_fn ml_kem_freectx;
26 static OSSL_FUNC_kem_encapsulate_init_fn ml_kem_encapsulate_init;
27 static OSSL_FUNC_kem_encapsulate_fn ml_kem_encapsulate;
28 static OSSL_FUNC_kem_decapsulate_init_fn ml_kem_decapsulate_init;
29 static OSSL_FUNC_kem_decapsulate_fn ml_kem_decapsulate;
30 static OSSL_FUNC_kem_set_ctx_params_fn ml_kem_set_ctx_params;
31 static OSSL_FUNC_kem_settable_ctx_params_fn ml_kem_settable_ctx_params;
32 
33 typedef struct {
34     ML_KEM_KEY *key;
35     uint8_t entropy_buf[ML_KEM_RANDOM_BYTES];
36     uint8_t *entropy;
37     int op;
38 } PROV_ML_KEM_CTX;
39 
ml_kem_newctx(void * provctx)40 static void *ml_kem_newctx(void *provctx)
41 {
42     PROV_ML_KEM_CTX *ctx;
43 
44     if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
45         return NULL;
46 
47     ctx->key = NULL;
48     ctx->entropy = NULL;
49     ctx->op = 0;
50     return ctx;
51 }
52 
ml_kem_freectx(void * vctx)53 static void ml_kem_freectx(void *vctx)
54 {
55     PROV_ML_KEM_CTX *ctx = vctx;
56 
57     if (ctx->entropy != NULL)
58         OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
59     OPENSSL_free(ctx);
60 }
61 
ml_kem_init(void * vctx,int op,void * key,const OSSL_PARAM params[])62 static int ml_kem_init(void *vctx, int op, void *key,
63                        const OSSL_PARAM params[])
64 {
65     PROV_ML_KEM_CTX *ctx = vctx;
66 
67     if (!ossl_prov_is_running())
68         return 0;
69     ctx->key = key;
70     ctx->op = op;
71     return ml_kem_set_ctx_params(vctx, params);
72 }
73 
ml_kem_encapsulate_init(void * vctx,void * vkey,const OSSL_PARAM params[])74 static int ml_kem_encapsulate_init(void *vctx, void *vkey,
75                                    const OSSL_PARAM params[])
76 {
77     ML_KEM_KEY *key = vkey;
78 
79     if (!ossl_ml_kem_have_pubkey(key)) {
80         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
81         return 0;
82     }
83     return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
84 }
85 
ml_kem_decapsulate_init(void * vctx,void * vkey,const OSSL_PARAM params[])86 static int ml_kem_decapsulate_init(void *vctx, void *vkey,
87                                    const OSSL_PARAM params[])
88 {
89     ML_KEM_KEY *key = vkey;
90 
91     if (!ossl_ml_kem_have_prvkey(key)) {
92         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
93         return 0;
94     }
95     return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
96 }
97 
ml_kem_set_ctx_params(void * vctx,const OSSL_PARAM params[])98 static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
99 {
100     PROV_ML_KEM_CTX *ctx = vctx;
101     const OSSL_PARAM *p;
102 
103     if (ctx == NULL)
104         return 0;
105 
106     if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) {
107         /* Decapsulation is deterministic */
108         OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
109         ctx->entropy = NULL;
110     }
111 
112     if (ossl_param_is_empty(params))
113         return 1;
114 
115     /* Encapsulation ephemeral input key material "ikmE" */
116     if (ctx->op == EVP_PKEY_OP_ENCAPSULATE
117         && (p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME)) != NULL) {
118         size_t len = ML_KEM_RANDOM_BYTES;
119 
120         ctx->entropy = ctx->entropy_buf;
121         if (OSSL_PARAM_get_octet_string(p, (void **)&ctx->entropy,
122                                         len, &len)
123             && len == ML_KEM_RANDOM_BYTES)
124             return 1;
125 
126         /* Possibly, but much less likely wrong type */
127         ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
128         ctx->entropy = NULL;
129         return 0;
130     }
131 
132     return 1;
133 }
134 
ml_kem_settable_ctx_params(ossl_unused void * vctx,ossl_unused void * provctx)135 static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,
136                                                     ossl_unused void *provctx)
137 {
138     static const OSSL_PARAM params[] = {
139         OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
140         OSSL_PARAM_END
141     };
142 
143     return params;
144 }
145 
ml_kem_encapsulate(void * vctx,unsigned char * ctext,size_t * clen,unsigned char * shsec,size_t * slen)146 static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
147                               unsigned char *shsec, size_t *slen)
148 {
149     PROV_ML_KEM_CTX *ctx = vctx;
150     ML_KEM_KEY *key = ctx->key;
151     const ML_KEM_VINFO *v;
152     size_t encap_clen;
153     size_t encap_slen;
154     int ret = 0;
155 
156     if (!ossl_ml_kem_have_pubkey(key)) {
157         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
158         goto end;
159     }
160     v = ossl_ml_kem_key_vinfo(key);
161     encap_clen = v->ctext_bytes;
162     encap_slen = ML_KEM_SHARED_SECRET_BYTES;
163 
164     if (ctext == NULL) {
165         if (clen == NULL && slen == NULL)
166             return 0;
167         if (clen != NULL)
168             *clen = encap_clen;
169         if (slen != NULL)
170             *slen = encap_slen;
171         return 1;
172     }
173     if (shsec == NULL) {
174         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
175                        "NULL shared-secret buffer");
176         goto end;
177     }
178 
179     if (clen == NULL) {
180         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
181                        "null ciphertext input/output length pointer");
182         goto end;
183     } else if (*clen < encap_clen) {
184         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
185                        "ciphertext buffer too small");
186         goto end;
187     } else {
188         *clen = encap_clen;
189     }
190 
191     if (slen == NULL) {
192         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
193                        "null shared secret input/output length pointer");
194         goto end;
195     } else if (*slen < encap_slen) {
196         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
197                        "shared-secret buffer too small");
198         goto end;
199     } else {
200         *slen = encap_slen;
201     }
202 
203     if (ctx->entropy != NULL)
204         ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,
205                                      ctx->entropy, ML_KEM_RANDOM_BYTES, key);
206     else
207         ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);
208 
209  end:
210     /*
211      * One shot entropy, each encapsulate call must either provide a new
212      * "ikmE", or else will use a random value.  If a caller sets an explicit
213      * ikmE once for testing, and later performs multiple encapsulations
214      * without again calling encapsulate_init(), these should not share the
215      * original entropy.
216      */
217     if (ctx->entropy != NULL) {
218         OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
219         ctx->entropy = NULL;
220     }
221     return ret;
222 }
223 
ml_kem_decapsulate(void * vctx,uint8_t * shsec,size_t * slen,const uint8_t * ctext,size_t clen)224 static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
225                               const uint8_t *ctext, size_t clen)
226 {
227     PROV_ML_KEM_CTX *ctx = vctx;
228     ML_KEM_KEY *key = ctx->key;
229     size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;
230 
231     if (!ossl_ml_kem_have_prvkey(key)) {
232         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
233         return 0;
234     }
235 
236     if (shsec == NULL) {
237         if (slen == NULL)
238             return 0;
239         *slen = ML_KEM_SHARED_SECRET_BYTES;
240         return 1;
241     }
242 
243     /* For now tolerate newly-deprecated NULL length pointers. */
244     if (slen == NULL) {
245         slen = &decap_slen;
246     } else if (*slen < decap_slen) {
247         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
248                        "shared-secret buffer too small");
249         return 0;
250     } else {
251         *slen = decap_slen;
252     }
253 
254     /* ML-KEM decap handles incorrect ciphertext lengths internally */
255     return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);
256 }
257 
258 const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = {
259     { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) ml_kem_newctx },
260     { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) ml_kem_encapsulate_init },
261     { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) ml_kem_encapsulate },
262     { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) ml_kem_decapsulate_init },
263     { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) ml_kem_decapsulate },
264     { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) ml_kem_freectx },
265     { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) ml_kem_set_ctx_params },
266     { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) ml_kem_settable_ctx_params },
267     OSSL_DISPATCH_END
268 };
269