/src/openssl/providers/implementations/kem/ml_kem_kem.c
Line  | Count  | Source (jump to first uncovered line)  | 
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  |  |  | 
40  |  | static void *ml_kem_newctx(void *provctx)  | 
41  | 0  | { | 
42  | 0  |     PROV_ML_KEM_CTX *ctx;  | 
43  |  | 
  | 
44  | 0  |     if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)  | 
45  | 0  |         return NULL;  | 
46  |  |  | 
47  | 0  |     ctx->key = NULL;  | 
48  | 0  |     ctx->entropy = NULL;  | 
49  | 0  |     ctx->op = 0;  | 
50  | 0  |     return ctx;  | 
51  | 0  | }  | 
52  |  |  | 
53  |  | static void ml_kem_freectx(void *vctx)  | 
54  | 0  | { | 
55  | 0  |     PROV_ML_KEM_CTX *ctx = vctx;  | 
56  |  | 
  | 
57  | 0  |     if (ctx->entropy != NULL)  | 
58  | 0  |         OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);  | 
59  | 0  |     OPENSSL_free(ctx);  | 
60  | 0  | }  | 
61  |  |  | 
62  |  | static int ml_kem_init(void *vctx, int op, void *key,  | 
63  |  |                        const OSSL_PARAM params[])  | 
64  | 0  | { | 
65  | 0  |     PROV_ML_KEM_CTX *ctx = vctx;  | 
66  |  | 
  | 
67  | 0  |     if (!ossl_prov_is_running())  | 
68  | 0  |         return 0;  | 
69  | 0  |     ctx->key = key;  | 
70  | 0  |     ctx->op = op;  | 
71  | 0  |     return ml_kem_set_ctx_params(vctx, params);  | 
72  | 0  | }  | 
73  |  |  | 
74  |  | static int ml_kem_encapsulate_init(void *vctx, void *vkey,  | 
75  |  |                                    const OSSL_PARAM params[])  | 
76  | 0  | { | 
77  | 0  |     ML_KEM_KEY *key = vkey;  | 
78  |  | 
  | 
79  | 0  |     if (!ossl_ml_kem_have_pubkey(key)) { | 
80  | 0  |         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);  | 
81  | 0  |         return 0;  | 
82  | 0  |     }  | 
83  | 0  |     return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);  | 
84  | 0  | }  | 
85  |  |  | 
86  |  | static int ml_kem_decapsulate_init(void *vctx, void *vkey,  | 
87  |  |                                    const OSSL_PARAM params[])  | 
88  | 0  | { | 
89  | 0  |     ML_KEM_KEY *key = vkey;  | 
90  |  | 
  | 
91  | 0  |     if (!ossl_ml_kem_have_prvkey(key)) { | 
92  | 0  |         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);  | 
93  | 0  |         return 0;  | 
94  | 0  |     }  | 
95  | 0  |     return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);  | 
96  | 0  | }  | 
97  |  |  | 
98  |  | static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])  | 
99  | 0  | { | 
100  | 0  |     PROV_ML_KEM_CTX *ctx = vctx;  | 
101  | 0  |     const OSSL_PARAM *p;  | 
102  |  | 
  | 
103  | 0  |     if (ctx == NULL)  | 
104  | 0  |         return 0;  | 
105  |  |  | 
106  | 0  |     if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) { | 
107  |  |         /* Decapsulation is deterministic */  | 
108  | 0  |         OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);  | 
109  | 0  |         ctx->entropy = NULL;  | 
110  | 0  |     }  | 
111  |  | 
  | 
112  | 0  |     if (ossl_param_is_empty(params))  | 
113  | 0  |         return 1;  | 
114  |  |  | 
115  |  |     /* Encapsulation ephemeral input key material "ikmE" */  | 
116  | 0  |     if (ctx->op == EVP_PKEY_OP_ENCAPSULATE  | 
117  | 0  |         && (p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME)) != NULL) { | 
118  | 0  |         size_t len = ML_KEM_RANDOM_BYTES;  | 
119  |  | 
  | 
120  | 0  |         ctx->entropy = ctx->entropy_buf;  | 
121  | 0  |         if (OSSL_PARAM_get_octet_string(p, (void **)&ctx->entropy,  | 
122  | 0  |                                         len, &len)  | 
123  | 0  |             && len == ML_KEM_RANDOM_BYTES)  | 
124  | 0  |             return 1;  | 
125  |  |  | 
126  |  |         /* Possibly, but much less likely wrong type */  | 
127  | 0  |         ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);  | 
128  | 0  |         ctx->entropy = NULL;  | 
129  | 0  |         return 0;  | 
130  | 0  |     }  | 
131  |  |  | 
132  | 0  |     return 1;  | 
133  | 0  | }  | 
134  |  |  | 
135  |  | static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,  | 
136  |  |                                                     ossl_unused void *provctx)  | 
137  | 0  | { | 
138  | 0  |     static const OSSL_PARAM params[] = { | 
139  | 0  |         OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),  | 
140  | 0  |         OSSL_PARAM_END  | 
141  | 0  |     };  | 
142  |  | 
  | 
143  | 0  |     return params;  | 
144  | 0  | }  | 
145  |  |  | 
146  |  | static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,  | 
147  |  |                               unsigned char *shsec, size_t *slen)  | 
148  | 0  | { | 
149  | 0  |     PROV_ML_KEM_CTX *ctx = vctx;  | 
150  | 0  |     ML_KEM_KEY *key = ctx->key;  | 
151  | 0  |     const ML_KEM_VINFO *v;  | 
152  | 0  |     size_t encap_clen;  | 
153  | 0  |     size_t encap_slen;  | 
154  | 0  |     int ret = 0;  | 
155  |  | 
  | 
156  | 0  |     if (!ossl_ml_kem_have_pubkey(key)) { | 
157  | 0  |         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);  | 
158  | 0  |         goto end;  | 
159  | 0  |     }  | 
160  | 0  |     v = ossl_ml_kem_key_vinfo(key);  | 
161  | 0  |     encap_clen = v->ctext_bytes;  | 
162  | 0  |     encap_slen = ML_KEM_SHARED_SECRET_BYTES;  | 
163  |  | 
  | 
164  | 0  |     if (ctext == NULL) { | 
165  | 0  |         if (clen == NULL && slen == NULL)  | 
166  | 0  |             return 0;  | 
167  | 0  |         if (clen != NULL)  | 
168  | 0  |             *clen = encap_clen;  | 
169  | 0  |         if (slen != NULL)  | 
170  | 0  |             *slen = encap_slen;  | 
171  | 0  |         return 1;  | 
172  | 0  |     }  | 
173  | 0  |     if (shsec == NULL) { | 
174  | 0  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,  | 
175  | 0  |                        "NULL shared-secret buffer");  | 
176  | 0  |         goto end;  | 
177  | 0  |     }  | 
178  |  |  | 
179  | 0  |     if (clen == NULL) { | 
180  | 0  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,  | 
181  | 0  |                        "null ciphertext input/output length pointer");  | 
182  | 0  |         goto end;  | 
183  | 0  |     } else if (*clen < encap_clen) { | 
184  | 0  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,  | 
185  | 0  |                        "ciphertext buffer too small");  | 
186  | 0  |         goto end;  | 
187  | 0  |     } else { | 
188  | 0  |         *clen = encap_clen;  | 
189  | 0  |     }  | 
190  |  |  | 
191  | 0  |     if (slen == NULL) { | 
192  | 0  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,  | 
193  | 0  |                        "null shared secret input/output length pointer");  | 
194  | 0  |         goto end;  | 
195  | 0  |     } else if (*slen < encap_slen) { | 
196  | 0  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,  | 
197  | 0  |                        "shared-secret buffer too small");  | 
198  | 0  |         goto end;  | 
199  | 0  |     } else { | 
200  | 0  |         *slen = encap_slen;  | 
201  | 0  |     }  | 
202  |  |  | 
203  | 0  |     if (ctx->entropy != NULL)  | 
204  | 0  |         ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,  | 
205  | 0  |                                      ctx->entropy, ML_KEM_RANDOM_BYTES, key);  | 
206  | 0  |     else  | 
207  | 0  |         ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);  | 
208  |  | 
  | 
209  | 0  |  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  | 0  |     if (ctx->entropy != NULL) { | 
218  | 0  |         OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);  | 
219  | 0  |         ctx->entropy = NULL;  | 
220  | 0  |     }  | 
221  | 0  |     return ret;  | 
222  | 0  | }  | 
223  |  |  | 
224  |  | static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,  | 
225  |  |                               const uint8_t *ctext, size_t clen)  | 
226  | 0  | { | 
227  | 0  |     PROV_ML_KEM_CTX *ctx = vctx;  | 
228  | 0  |     ML_KEM_KEY *key = ctx->key;  | 
229  | 0  |     size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;  | 
230  |  | 
  | 
231  | 0  |     if (!ossl_ml_kem_have_prvkey(key)) { | 
232  | 0  |         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);  | 
233  | 0  |         return 0;  | 
234  | 0  |     }  | 
235  |  |  | 
236  | 0  |     if (shsec == NULL) { | 
237  | 0  |         if (slen == NULL)  | 
238  | 0  |             return 0;  | 
239  | 0  |         *slen = ML_KEM_SHARED_SECRET_BYTES;  | 
240  | 0  |         return 1;  | 
241  | 0  |     }  | 
242  |  |  | 
243  |  |     /* For now tolerate newly-deprecated NULL length pointers. */  | 
244  | 0  |     if (slen == NULL) { | 
245  | 0  |         slen = &decap_slen;  | 
246  | 0  |     } else if (*slen < decap_slen) { | 
247  | 0  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,  | 
248  | 0  |                        "shared-secret buffer too small");  | 
249  | 0  |         return 0;  | 
250  | 0  |     } else { | 
251  | 0  |         *slen = decap_slen;  | 
252  | 0  |     }  | 
253  |  |  | 
254  |  |     /* ML-KEM decap handles incorrect ciphertext lengths internally */  | 
255  | 0  |     return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);  | 
256  | 0  | }  | 
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  |  | };  |