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