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 { 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 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 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 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 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 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 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 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_NULL_OUTPUT_BUFFER, 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 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