1
0

mlx_kem.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. #include <openssl/core_dispatch.h>
  10. #include <openssl/core_names.h>
  11. #include <openssl/crypto.h>
  12. #include <openssl/err.h>
  13. #include <openssl/evp.h>
  14. #include <openssl/params.h>
  15. #include <openssl/proverr.h>
  16. #include <openssl/rand.h>
  17. #include "prov/implementations.h"
  18. #include "prov/mlx_kem.h"
  19. #include "prov/provider_ctx.h"
  20. #include "prov/providercommon.h"
  21. static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx;
  22. static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx;
  23. static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init;
  24. static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate;
  25. static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init;
  26. static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate;
  27. static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params;
  28. static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params;
  29. typedef struct {
  30. OSSL_LIB_CTX *libctx;
  31. MLX_KEY *key;
  32. int op;
  33. } PROV_MLX_KEM_CTX;
  34. static void *mlx_kem_newctx(void *provctx)
  35. {
  36. PROV_MLX_KEM_CTX *ctx;
  37. if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
  38. return NULL;
  39. ctx->libctx = PROV_LIBCTX_OF(provctx);
  40. ctx->key = NULL;
  41. ctx->op = 0;
  42. return ctx;
  43. }
  44. static void mlx_kem_freectx(void *vctx)
  45. {
  46. OPENSSL_free(vctx);
  47. }
  48. static int mlx_kem_init(void *vctx, int op, void *key,
  49. ossl_unused const OSSL_PARAM params[])
  50. {
  51. PROV_MLX_KEM_CTX *ctx = vctx;
  52. if (!ossl_prov_is_running())
  53. return 0;
  54. ctx->key = key;
  55. ctx->op = op;
  56. return 1;
  57. }
  58. static int
  59. mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
  60. {
  61. MLX_KEY *key = vkey;
  62. if (!mlx_kem_have_pubkey(key)) {
  63. ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
  64. return 0;
  65. }
  66. return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
  67. }
  68. static int
  69. mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
  70. {
  71. MLX_KEY *key = vkey;
  72. if (!mlx_kem_have_prvkey(key)) {
  73. ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
  74. return 0;
  75. }
  76. return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
  77. }
  78. static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx,
  79. ossl_unused void *provctx)
  80. {
  81. static const OSSL_PARAM params[] = { OSSL_PARAM_END };
  82. return params;
  83. }
  84. static int
  85. mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
  86. {
  87. return 1;
  88. }
  89. static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
  90. unsigned char *shsec, size_t *slen)
  91. {
  92. MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key;
  93. EVP_PKEY_CTX *ctx = NULL;
  94. EVP_PKEY *xkey = NULL;
  95. size_t encap_clen;
  96. size_t encap_slen;
  97. uint8_t *cbuf;
  98. uint8_t *sbuf;
  99. int ml_kem_slot = key->xinfo->ml_kem_slot;
  100. int ret = 0;
  101. if (!mlx_kem_have_pubkey(key)) {
  102. ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
  103. goto end;
  104. }
  105. encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
  106. encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
  107. if (ctext == NULL) {
  108. if (clen == NULL && slen == NULL)
  109. return 0;
  110. if (clen != NULL)
  111. *clen = encap_clen;
  112. if (slen != NULL)
  113. *slen = encap_slen;
  114. return 1;
  115. }
  116. if (shsec == NULL) {
  117. ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
  118. "null shared-secret output buffer");
  119. return 0;
  120. }
  121. if (clen == NULL) {
  122. ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
  123. "null ciphertext input/output length pointer");
  124. return 0;
  125. } else if (*clen < encap_clen) {
  126. ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
  127. "ciphertext buffer too small");
  128. return 0;
  129. } else {
  130. *clen = encap_clen;
  131. }
  132. if (slen == NULL) {
  133. ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
  134. "null shared secret input/output length pointer");
  135. return 0;
  136. } else if (*slen < encap_slen) {
  137. ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
  138. "shared-secret buffer too small");
  139. return 0;
  140. } else {
  141. *slen = encap_slen;
  142. }
  143. /* ML-KEM encapsulation */
  144. encap_clen = key->minfo->ctext_bytes;
  145. encap_slen = ML_KEM_SHARED_SECRET_BYTES;
  146. cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
  147. sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
  148. ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
  149. if (ctx == NULL
  150. || EVP_PKEY_encapsulate_init(ctx, NULL) <= 0
  151. || EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0)
  152. goto end;
  153. if (encap_clen != key->minfo->ctext_bytes) {
  154. ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
  155. "unexpected %s ciphertext output size: %lu",
  156. key->minfo->algorithm_name, (unsigned long) encap_clen);
  157. goto end;
  158. }
  159. if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) {
  160. ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
  161. "unexpected %s shared secret output size: %lu",
  162. key->minfo->algorithm_name, (unsigned long) encap_slen);
  163. goto end;
  164. }
  165. EVP_PKEY_CTX_free(ctx);
  166. /*-
  167. * ECDHE encapsulation
  168. *
  169. * Generate own ephemeral private key and add its public key to ctext.
  170. *
  171. * Note, we could support a settable parameter that sets an extant ECDH
  172. * keypair as the keys to use in encap, making it possible to reuse the
  173. * same (TLS client) ECDHE keypair for both the classical EC keyshare and a
  174. * corresponding ECDHE + ML-KEM keypair. But the TLS layer would then need
  175. * know that this is a hybrid, and that it can partly reuse the same keys
  176. * as another group for which a keyshare will be sent. Deferred until we
  177. * support generating multiple keyshares, there's a workable keyshare
  178. * prediction specification, and the optimisation is justified.
  179. */
  180. cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
  181. encap_clen = key->xinfo->pubkey_bytes;
  182. ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
  183. if (ctx == NULL
  184. || EVP_PKEY_keygen_init(ctx) <= 0
  185. || EVP_PKEY_keygen(ctx, &xkey) <= 0
  186. || EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
  187. cbuf, encap_clen, &encap_clen) <= 0)
  188. goto end;
  189. if (encap_clen != key->xinfo->pubkey_bytes) {
  190. ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
  191. "unexpected %s public key output size: %lu",
  192. key->xinfo->algorithm_name, (unsigned long) encap_clen);
  193. goto end;
  194. }
  195. EVP_PKEY_CTX_free(ctx);
  196. /* Derive the ECDH shared secret */
  197. encap_slen = key->xinfo->shsec_bytes;
  198. sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
  199. ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq);
  200. if (ctx == NULL
  201. || EVP_PKEY_derive_init(ctx) <= 0
  202. || EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0
  203. || EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0)
  204. goto end;
  205. if (encap_slen != key->xinfo->shsec_bytes) {
  206. ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
  207. "unexpected %s shared secret output size: %lu",
  208. key->xinfo->algorithm_name, (unsigned long) encap_slen);
  209. goto end;
  210. }
  211. ret = 1;
  212. end:
  213. EVP_PKEY_free(xkey);
  214. EVP_PKEY_CTX_free(ctx);
  215. return ret;
  216. }
  217. static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
  218. const uint8_t *ctext, size_t clen)
  219. {
  220. MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key;
  221. EVP_PKEY_CTX *ctx = NULL;
  222. EVP_PKEY *xkey = NULL;
  223. const uint8_t *cbuf;
  224. uint8_t *sbuf;
  225. size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
  226. size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
  227. int ml_kem_slot = key->xinfo->ml_kem_slot;
  228. int ret = 0;
  229. if (!mlx_kem_have_prvkey(key)) {
  230. ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
  231. return 0;
  232. }
  233. if (shsec == NULL) {
  234. if (slen == NULL)
  235. return 0;
  236. *slen = decap_slen;
  237. return 1;
  238. }
  239. /* For now tolerate newly-deprecated NULL length pointers. */
  240. if (slen == NULL) {
  241. slen = &decap_slen;
  242. } else if (*slen < decap_slen) {
  243. ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
  244. "shared-secret buffer too small");
  245. return 0;
  246. } else {
  247. *slen = decap_slen;
  248. }
  249. if (clen != decap_clen) {
  250. ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE,
  251. "wrong decapsulation input ciphertext size: %lu",
  252. (unsigned long) clen);
  253. return 0;
  254. }
  255. /* ML-KEM decapsulation */
  256. decap_clen = key->minfo->ctext_bytes;
  257. decap_slen = ML_KEM_SHARED_SECRET_BYTES;
  258. cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
  259. sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
  260. ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
  261. if (ctx == NULL
  262. || EVP_PKEY_decapsulate_init(ctx, NULL) <= 0
  263. || EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)
  264. goto end;
  265. if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) {
  266. ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
  267. "unexpected %s shared secret output size: %lu",
  268. key->minfo->algorithm_name, (unsigned long) decap_slen);
  269. goto end;
  270. }
  271. EVP_PKEY_CTX_free(ctx);
  272. /* ECDH decapsulation */
  273. decap_clen = key->xinfo->pubkey_bytes;
  274. decap_slen = key->xinfo->shsec_bytes;
  275. cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
  276. sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
  277. ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
  278. if (ctx == NULL
  279. || (xkey = EVP_PKEY_new()) == NULL
  280. || EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0
  281. || EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0
  282. || EVP_PKEY_derive_init(ctx) <= 0
  283. || EVP_PKEY_derive_set_peer(ctx, xkey) <= 0
  284. || EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0)
  285. goto end;
  286. if (decap_slen != key->xinfo->shsec_bytes) {
  287. ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
  288. "unexpected %s shared secret output size: %lu",
  289. key->xinfo->algorithm_name, (unsigned long) decap_slen);
  290. goto end;
  291. }
  292. ret = 1;
  293. end:
  294. EVP_PKEY_CTX_free(ctx);
  295. EVP_PKEY_free(xkey);
  296. return ret;
  297. }
  298. const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = {
  299. { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) mlx_kem_newctx },
  300. { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) mlx_kem_encapsulate_init },
  301. { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) mlx_kem_encapsulate },
  302. { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) mlx_kem_decapsulate_init },
  303. { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) mlx_kem_decapsulate },
  304. { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) mlx_kem_freectx },
  305. { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) mlx_kem_set_ctx_params },
  306. { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) mlx_kem_settable_ctx_params },
  307. OSSL_DISPATCH_END
  308. };