]>
Commit | Line | Data |
---|---|---|
78c44b05 | 1 | /* |
da1c088f | 2 | * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. |
78c44b05 | 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 | /* | |
11 | * The following implementation is part of RFC 9180 related to DHKEM using | |
12 | * ECX keys (i.e. X25519 and X448) | |
13 | * References to Sections in the comments below refer to RFC 9180. | |
14 | */ | |
15 | ||
16 | #include "internal/deprecated.h" | |
17 | ||
18 | #include <string.h> | |
19 | #include <openssl/crypto.h> | |
20 | #include <openssl/evp.h> | |
21 | #include <openssl/core_dispatch.h> | |
22 | #include <openssl/core_names.h> | |
23 | #include <openssl/params.h> | |
24 | #include <openssl/kdf.h> | |
25 | #include <openssl/err.h> | |
26 | #include <openssl/sha.h> | |
27 | #include <openssl/rand.h> | |
28 | #include <openssl/proverr.h> | |
29 | #include "prov/provider_ctx.h" | |
30 | #include "prov/implementations.h" | |
31 | #include "prov/securitycheck.h" | |
32 | #include "prov/providercommon.h" | |
33 | #include "prov/ecx.h" | |
34 | #include "crypto/ecx.h" | |
ad062480 SF |
35 | #include <openssl/hpke.h> |
36 | #include "internal/hpke_util.h" | |
78c44b05 | 37 | #include "eckem.h" |
38 | ||
39 | #define MAX_ECX_KEYLEN X448_KEYLEN | |
40 | ||
41 | /* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */ | |
42 | #define KEMID_X25519_HKDF_SHA256 0x20 | |
43 | #define KEMID_X448_HKDF_SHA512 0x21 | |
44 | ||
ad062480 SF |
45 | /* ASCII: "KEM", in hex for EBCDIC compatibility */ |
46 | static const char LABEL_KEM[] = "\x4b\x45\x4d"; | |
47 | ||
78c44b05 | 48 | typedef struct { |
49 | ECX_KEY *recipient_key; | |
50 | ECX_KEY *sender_authkey; | |
51 | OSSL_LIB_CTX *libctx; | |
52 | char *propq; | |
53 | unsigned int mode; | |
54 | unsigned int op; | |
78c44b05 | 55 | unsigned char *ikm; |
56 | size_t ikmlen; | |
57 | const char *kdfname; | |
ad062480 | 58 | const OSSL_HPKE_KEM_INFO *info; |
78c44b05 | 59 | } PROV_ECX_CTX; |
60 | ||
61 | static OSSL_FUNC_kem_newctx_fn ecxkem_newctx; | |
62 | static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init; | |
63 | static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate; | |
64 | static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init; | |
65 | static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate; | |
66 | static OSSL_FUNC_kem_freectx_fn ecxkem_freectx; | |
67 | static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params; | |
68 | static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init; | |
69 | static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init; | |
70 | ||
71 | /* | |
72 | * Set KEM values as specified in Section 7.1 "Table 2 KEM IDs" | |
73 | * There is only one set of values for X25519 and X448. | |
74 | * Additional values could be set via set_params if required. | |
75 | */ | |
ad062480 | 76 | static const OSSL_HPKE_KEM_INFO *get_kem_info(ECX_KEY *ecx) |
78c44b05 | 77 | { |
ad062480 SF |
78 | const char *name = NULL; |
79 | ||
80 | if (ecx->type == ECX_KEY_TYPE_X25519) | |
81 | name = SN_X25519; | |
82 | else | |
83 | name = SN_X448; | |
84 | return ossl_HPKE_KEM_INFO_find_curve(name); | |
78c44b05 | 85 | } |
86 | ||
87 | /* | |
88 | * Set the recipient key, and free any existing key. | |
89 | * ecx can be NULL. The ecx key may have only a private or public component. | |
90 | */ | |
91 | static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx) | |
92 | { | |
93 | ossl_ecx_key_free(ctx->recipient_key); | |
94 | ctx->recipient_key = NULL; | |
95 | if (ecx != NULL) { | |
ad062480 SF |
96 | ctx->info = get_kem_info(ecx); |
97 | if (ctx->info == NULL) | |
98 | return -2; | |
78c44b05 | 99 | ctx->kdfname = "HKDF"; |
100 | if (!ossl_ecx_key_up_ref(ecx)) | |
101 | return 0; | |
102 | ctx->recipient_key = ecx; | |
103 | } | |
104 | return 1; | |
105 | } | |
106 | ||
107 | /* | |
108 | * Set the senders auth key, and free any existing auth key. | |
109 | * ecx can be NULL. | |
110 | */ | |
111 | static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx) | |
112 | { | |
113 | ossl_ecx_key_free(ctx->sender_authkey); | |
114 | ctx->sender_authkey = NULL; | |
115 | ||
116 | if (ecx != NULL) { | |
117 | if (!ossl_ecx_key_up_ref(ecx)) | |
118 | return 0; | |
119 | ctx->sender_authkey = ecx; | |
120 | } | |
121 | return 1; | |
122 | } | |
123 | ||
124 | /* | |
125 | * Serialize a public key from byte array's for the encoded public keys. | |
126 | * ctx is used to access the key type. | |
127 | * Returns: The created ECX_KEY or NULL on error. | |
128 | */ | |
129 | static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx, | |
130 | const unsigned char *pubbuf, size_t pubbuflen) | |
131 | { | |
132 | ECX_KEY *ecx = NULL; | |
133 | OSSL_PARAM params[2], *p = params; | |
134 | ||
135 | *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, | |
136 | (char *)pubbuf, pubbuflen); | |
137 | *p = OSSL_PARAM_construct_end(); | |
138 | ||
139 | ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq); | |
140 | if (ecx == NULL) | |
141 | return NULL; | |
142 | if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) { | |
143 | ossl_ecx_key_free(ecx); | |
144 | ecx = NULL; | |
145 | } | |
146 | return ecx; | |
147 | } | |
148 | ||
149 | static unsigned char *ecx_pubkey(ECX_KEY *ecx) | |
150 | { | |
151 | if (ecx == NULL || !ecx->haspubkey) { | |
152 | ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); | |
153 | return 0; | |
154 | } | |
155 | return ecx->pubkey; | |
156 | } | |
157 | ||
158 | static void *ecxkem_newctx(void *provctx) | |
159 | { | |
160 | PROV_ECX_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX)); | |
161 | ||
162 | if (ctx == NULL) | |
163 | return NULL; | |
164 | ctx->libctx = PROV_LIBCTX_OF(provctx); | |
165 | ||
166 | return ctx; | |
167 | } | |
168 | ||
169 | static void ecxkem_freectx(void *vectx) | |
170 | { | |
171 | PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx; | |
172 | ||
173 | OPENSSL_clear_free(ctx->ikm, ctx->ikmlen); | |
174 | recipient_key_set(ctx, NULL); | |
175 | sender_authkey_set(ctx, NULL); | |
176 | OPENSSL_free(ctx); | |
177 | } | |
178 | ||
179 | static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2) | |
180 | { | |
181 | return (key1->type == key2->type && key1->keylen == key2->keylen); | |
182 | } | |
183 | ||
184 | static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey) | |
185 | { | |
186 | if (ecx->privkey == NULL) | |
187 | return (requires_privatekey == 0); | |
188 | return 1; | |
189 | } | |
190 | ||
191 | static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth, | |
192 | ossl_unused const OSSL_PARAM params[]) | |
193 | { | |
194 | int rv; | |
195 | PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx; | |
196 | ECX_KEY *ecx = vecx; | |
197 | ECX_KEY *auth = vauth; | |
198 | ||
199 | if (!ossl_prov_is_running()) | |
200 | return 0; | |
201 | ||
202 | if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE)) | |
203 | return 0; | |
204 | rv = recipient_key_set(ctx, ecx); | |
205 | if (rv <= 0) | |
206 | return rv; | |
207 | ||
208 | if (auth != NULL) { | |
209 | if (!ecx_match_params(auth, ctx->recipient_key) | |
210 | || !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE) | |
211 | || !sender_authkey_set(ctx, auth)) | |
212 | return 0; | |
213 | } | |
214 | ||
215 | ctx->op = operation; | |
216 | return ecxkem_set_ctx_params(vecxctx, params); | |
217 | } | |
218 | ||
219 | static int ecxkem_encapsulate_init(void *vecxctx, void *vecx, | |
220 | const OSSL_PARAM params[]) | |
221 | { | |
222 | return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params); | |
223 | } | |
224 | ||
225 | static int ecxkem_decapsulate_init(void *vecxctx, void *vecx, | |
226 | const OSSL_PARAM params[]) | |
227 | { | |
228 | return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params); | |
229 | } | |
230 | ||
231 | static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv, | |
232 | const OSSL_PARAM params[]) | |
233 | { | |
234 | return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params); | |
235 | } | |
236 | ||
237 | static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub, | |
238 | const OSSL_PARAM params[]) | |
239 | { | |
240 | return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params); | |
241 | } | |
242 | ||
243 | static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[]) | |
244 | { | |
245 | PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx; | |
246 | const OSSL_PARAM *p; | |
247 | int mode; | |
248 | ||
249 | if (ctx == NULL) | |
250 | return 0; | |
251 | if (params == NULL) | |
252 | return 1; | |
253 | ||
254 | p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME); | |
255 | if (p != NULL) { | |
256 | void *tmp = NULL; | |
257 | size_t tmplen = 0; | |
258 | ||
259 | if (p->data != NULL && p->data_size != 0) { | |
260 | if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen)) | |
261 | return 0; | |
262 | } | |
263 | OPENSSL_clear_free(ctx->ikm, ctx->ikmlen); | |
264 | ctx->ikm = tmp; | |
265 | ctx->ikmlen = tmplen; | |
266 | } | |
267 | p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION); | |
268 | if (p != NULL) { | |
269 | if (p->data_type != OSSL_PARAM_UTF8_STRING) | |
270 | return 0; | |
271 | mode = ossl_eckem_modename2id(p->data); | |
272 | if (mode == KEM_MODE_UNDEFINED) | |
273 | return 0; | |
274 | ctx->mode = mode; | |
275 | } | |
276 | return 1; | |
277 | } | |
278 | ||
279 | static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = { | |
280 | OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0), | |
281 | OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0), | |
282 | OSSL_PARAM_END | |
283 | }; | |
284 | ||
285 | static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx, | |
286 | ossl_unused void *provctx) | |
287 | { | |
288 | return known_settable_ecxkem_ctx_params; | |
289 | } | |
290 | ||
291 | /* | |
292 | * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand | |
293 | */ | |
294 | static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx, | |
295 | unsigned char *okm, size_t okmlen, | |
296 | uint16_t kemid, | |
297 | const unsigned char *dhkm, size_t dhkmlen, | |
298 | const unsigned char *kemctx, | |
299 | size_t kemctxlen) | |
300 | { | |
ad062480 | 301 | uint8_t suiteid[2]; |
78c44b05 | 302 | uint8_t prk[EVP_MAX_MD_SIZE]; |
303 | size_t prklen = okmlen; /* Nh */ | |
304 | int ret; | |
305 | ||
306 | if (prklen > sizeof(prk)) | |
307 | return 0; | |
308 | ||
ad062480 SF |
309 | suiteid[0] = (kemid >> 8) &0xff; |
310 | suiteid[1] = kemid & 0xff; | |
78c44b05 | 311 | |
312 | ret = ossl_hpke_labeled_extract(kctx, prk, prklen, | |
ad062480 | 313 | NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid), |
78c44b05 | 314 | OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen) |
315 | && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen, | |
ad062480 | 316 | LABEL_KEM, suiteid, sizeof(suiteid), |
78c44b05 | 317 | OSSL_DHKEM_LABEL_SHARED_SECRET, |
318 | kemctx, kemctxlen); | |
319 | OPENSSL_cleanse(prk, prklen); | |
320 | return ret; | |
321 | } | |
322 | ||
323 | /* | |
324 | * See Section 7.1.3 DeriveKeyPair. | |
325 | * | |
326 | * This function is used by ecx keygen. | |
327 | * (For this reason it does not use any of the state stored in PROV_ECX_CTX). | |
328 | * | |
329 | * Params: | |
330 | * ecx An initialized ecx key. | |
331 | * privout The buffer to store the generated private key into (it is assumed | |
332 | * this is of length ecx->keylen). | |
333 | * ikm buffer containing the input key material (seed). This must be non NULL. | |
334 | * ikmlen size of the ikm buffer in bytes | |
335 | * Returns: | |
336 | * 1 if successful or 0 otherwise. | |
337 | */ | |
338 | int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout, | |
339 | const unsigned char *ikm, size_t ikmlen) | |
340 | { | |
341 | int ret = 0; | |
342 | EVP_KDF_CTX *kdfctx = NULL; | |
343 | unsigned char prk[EVP_MAX_MD_SIZE]; | |
ad062480 SF |
344 | uint8_t suiteid[2]; |
345 | const OSSL_HPKE_KEM_INFO *info = get_kem_info(ecx); | |
78c44b05 | 346 | |
347 | /* ikmlen should have a length of at least Nsk */ | |
ad062480 | 348 | if (ikmlen < info->Nsk) { |
78c44b05 | 349 | ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH, |
350 | "ikm length is :%zu, should be at least %zu", | |
ad062480 | 351 | ikmlen, info->Nsk); |
78c44b05 | 352 | goto err; |
353 | } | |
354 | ||
ad062480 | 355 | kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, ecx->libctx, ecx->propq); |
78c44b05 | 356 | if (kdfctx == NULL) |
357 | return 0; | |
358 | ||
ad062480 SF |
359 | suiteid[0] = info->kem_id / 256; |
360 | suiteid[1] = info->kem_id % 256; | |
78c44b05 | 361 | |
ad062480 SF |
362 | if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret, |
363 | NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid), | |
78c44b05 | 364 | OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen)) |
365 | goto err; | |
366 | ||
ad062480 SF |
367 | if (!ossl_hpke_labeled_expand(kdfctx, privout, info->Nsk, prk, info->Nsecret, |
368 | LABEL_KEM, suiteid, sizeof(suiteid), | |
369 | OSSL_DHKEM_LABEL_SK, NULL, 0)) | |
78c44b05 | 370 | goto err; |
371 | ret = 1; | |
372 | err: | |
373 | OPENSSL_cleanse(prk, sizeof(prk)); | |
374 | EVP_KDF_CTX_free(kdfctx); | |
375 | return ret; | |
376 | } | |
377 | ||
378 | /* | |
379 | * Do a keygen operation without having to use EVP_PKEY. | |
380 | * Params: | |
381 | * ctx Context object | |
382 | * ikm The seed material - if this is NULL, then a random seed is used. | |
383 | * Returns: | |
384 | * The generated ECX key, or NULL on failure. | |
385 | */ | |
386 | static ECX_KEY *derivekey(PROV_ECX_CTX *ctx, | |
387 | const unsigned char *ikm, size_t ikmlen) | |
388 | { | |
389 | int ok = 0; | |
390 | ECX_KEY *key; | |
391 | unsigned char *privkey; | |
392 | unsigned char *seed = (unsigned char *)ikm; | |
393 | size_t seedlen = ikmlen; | |
394 | unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE]; | |
ad062480 | 395 | const OSSL_HPKE_KEM_INFO *info = ctx->info; |
78c44b05 | 396 | |
397 | key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq); | |
398 | if (key == NULL) | |
399 | return NULL; | |
400 | privkey = ossl_ecx_key_allocate_privkey(key); | |
401 | if (privkey == NULL) | |
402 | goto err; | |
403 | ||
404 | /* Generate a random seed if there is no input ikm */ | |
405 | if (seed == NULL || seedlen == 0) { | |
ad062480 | 406 | if (info->Nsk > sizeof(tmpbuf)) |
78c44b05 | 407 | goto err; |
ad062480 | 408 | if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, info->Nsk, 0) <= 0) |
78c44b05 | 409 | goto err; |
410 | seed = tmpbuf; | |
ad062480 | 411 | seedlen = info->Nsk; |
78c44b05 | 412 | } |
413 | if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen)) | |
414 | goto err; | |
415 | if (!ossl_ecx_public_from_private(key)) | |
416 | goto err; | |
417 | key->haspubkey = 1; | |
418 | ok = 1; | |
419 | err: | |
420 | if (!ok) { | |
421 | ossl_ecx_key_free(key); | |
422 | key = NULL; | |
423 | } | |
424 | if (seed != ikm) | |
425 | OPENSSL_cleanse(seed, seedlen); | |
426 | return key; | |
427 | } | |
428 | ||
429 | /* | |
430 | * Do an ecxdh key exchange. | |
431 | * dhkm = DH(sender, peer) | |
432 | * | |
433 | * NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations | |
434 | * to avoid messy conversions back to EVP_PKEY. | |
435 | * | |
436 | * Returns the size of the secret if successful, or 0 otherwise, | |
437 | */ | |
438 | static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer, | |
439 | unsigned char *out, size_t maxout, | |
440 | unsigned int secretsz) | |
441 | { | |
442 | size_t len = 0; | |
443 | ||
444 | /* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */ | |
445 | return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender, | |
446 | sender->keylen, out, &len, maxout); | |
447 | } | |
448 | ||
449 | /* | |
450 | * Derive a secret using ECXDH (code is shared by the encap and decap) | |
451 | * | |
452 | * dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2) | |
453 | * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey) | |
454 | * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx); | |
455 | * | |
456 | * Params: | |
457 | * ctx Object that contains algorithm state and constants. | |
458 | * secret The returned secret (with a length ctx->alg->secretlen bytes). | |
459 | * privkey1 A private key used for ECXDH key derivation. | |
460 | * peerkey1 A public key used for ECXDH key derivation with privkey1 | |
461 | * privkey2 A optional private key used for a second ECXDH key derivation. | |
462 | * It can be NULL. | |
463 | * peerkey2 A optional public key used for a second ECXDH key derivation | |
464 | * with privkey2,. It can be NULL. | |
465 | * sender_pub The senders public key in encoded form. | |
466 | * recipient_pub The recipients public key in encoded form. | |
467 | * Notes: | |
468 | * The second ecdh() is only used for the HPKE auth modes when both privkey2 | |
469 | * and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL). | |
470 | */ | |
471 | static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret, | |
472 | const ECX_KEY *privkey1, const ECX_KEY *peerkey1, | |
473 | const ECX_KEY *privkey2, const ECX_KEY *peerkey2, | |
474 | const unsigned char *sender_pub, | |
475 | const unsigned char *recipient_pub) | |
476 | { | |
477 | int ret = 0; | |
478 | EVP_KDF_CTX *kdfctx = NULL; | |
479 | unsigned char *sender_authpub = NULL; | |
480 | unsigned char dhkm[MAX_ECX_KEYLEN * 2]; | |
481 | unsigned char kemctx[MAX_ECX_KEYLEN * 3]; | |
482 | size_t kemctxlen = 0, dhkmlen = 0; | |
ad062480 | 483 | const OSSL_HPKE_KEM_INFO *info = ctx->info; |
78c44b05 | 484 | int auth = ctx->sender_authkey != NULL; |
ad062480 | 485 | size_t encodedkeylen = info->Npk; |
78c44b05 | 486 | |
487 | if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen)) | |
488 | goto err; | |
489 | dhkmlen = encodedkeylen; | |
490 | ||
491 | /* Concat the optional second ECXDH (used for Auth) */ | |
492 | if (auth) { | |
493 | if (!generate_ecxdhkm(privkey2, peerkey2, | |
494 | dhkm + dhkmlen, sizeof(dhkm) - dhkmlen, | |
495 | encodedkeylen)) | |
496 | goto err; | |
497 | /* Get the public key of the auth sender in encoded form */ | |
498 | sender_authpub = ecx_pubkey(ctx->sender_authkey); | |
499 | if (sender_authpub == NULL) | |
500 | goto err; | |
501 | dhkmlen += encodedkeylen; | |
502 | } | |
503 | kemctxlen = encodedkeylen + dhkmlen; | |
504 | if (kemctxlen > sizeof(kemctx)) | |
505 | goto err; | |
506 | ||
507 | /* kemctx is the concat of both sides encoded public key */ | |
508 | memcpy(kemctx, sender_pub, encodedkeylen); | |
509 | memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen); | |
510 | if (auth) | |
511 | memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen); | |
ad062480 | 512 | kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname, |
78c44b05 | 513 | ctx->libctx, ctx->propq); |
514 | if (kdfctx == NULL) | |
515 | goto err; | |
ad062480 SF |
516 | if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret, |
517 | info->kem_id, dhkm, dhkmlen, | |
78c44b05 | 518 | kemctx, kemctxlen)) |
519 | goto err; | |
520 | ret = 1; | |
521 | err: | |
522 | OPENSSL_cleanse(dhkm, dhkmlen); | |
523 | EVP_KDF_CTX_free(kdfctx); | |
524 | return ret; | |
525 | } | |
526 | ||
527 | /* | |
528 | * Do a DHKEM encapsulate operation. | |
529 | * | |
530 | * See Section 4.1 Encap() and AuthEncap() | |
531 | * | |
532 | * Params: | |
533 | * ctx A context object holding the recipients public key and the | |
534 | * optional senders auth private key. | |
535 | * enc A buffer to return the senders ephemeral public key. | |
536 | * Setting this to NULL allows the enclen and secretlen to return | |
537 | * values, without calculating the secret. | |
538 | * enclen Passes in the max size of the enc buffer and returns the | |
539 | * encoded public key length. | |
540 | * secret A buffer to return the calculated shared secret. | |
541 | * secretlen Passes in the max size of the secret buffer and returns the | |
542 | * secret length. | |
543 | * Returns: 1 on success or 0 otherwise. | |
544 | */ | |
545 | static int dhkem_encap(PROV_ECX_CTX *ctx, | |
546 | unsigned char *enc, size_t *enclen, | |
547 | unsigned char *secret, size_t *secretlen) | |
548 | { | |
549 | int ret = 0; | |
550 | ECX_KEY *sender_ephemkey = NULL; | |
551 | unsigned char *sender_ephempub, *recipient_pub; | |
ad062480 | 552 | const OSSL_HPKE_KEM_INFO *info = ctx->info; |
78c44b05 | 553 | |
554 | if (enc == NULL) { | |
555 | if (enclen == NULL && secretlen == NULL) | |
556 | return 0; | |
557 | if (enclen != NULL) | |
ad062480 | 558 | *enclen = info->Nenc; |
78c44b05 | 559 | if (secretlen != NULL) |
ad062480 | 560 | *secretlen = info->Nsecret; |
78c44b05 | 561 | return 1; |
562 | } | |
563 | ||
ad062480 | 564 | if (*secretlen < info->Nsecret) { |
78c44b05 | 565 | ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small"); |
566 | return 0; | |
567 | } | |
ad062480 | 568 | if (*enclen < info->Nenc) { |
78c44b05 | 569 | ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small"); |
570 | return 0; | |
571 | } | |
572 | ||
573 | /* Create an ephemeral key */ | |
574 | sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen); | |
575 | ||
576 | sender_ephempub = ecx_pubkey(sender_ephemkey); | |
577 | recipient_pub = ecx_pubkey(ctx->recipient_key); | |
578 | if (sender_ephempub == NULL || recipient_pub == NULL) | |
579 | goto err; | |
580 | ||
581 | if (!derive_secret(ctx, secret, | |
582 | sender_ephemkey, ctx->recipient_key, | |
583 | ctx->sender_authkey, ctx->recipient_key, | |
584 | sender_ephempub, recipient_pub)) | |
585 | goto err; | |
586 | ||
587 | /* Return the public part of the ephemeral key */ | |
ad062480 SF |
588 | memcpy(enc, sender_ephempub, info->Nenc); |
589 | *enclen = info->Nenc; | |
590 | *secretlen = info->Nsecret; | |
78c44b05 | 591 | ret = 1; |
592 | err: | |
593 | ossl_ecx_key_free(sender_ephemkey); | |
594 | return ret; | |
595 | } | |
596 | ||
597 | /* | |
598 | * Do a DHKEM decapsulate operation. | |
599 | * See Section 4.1 Decap() and Auth Decap() | |
600 | * | |
601 | * Params: | |
602 | * ctx A context object holding the recipients private key and the | |
603 | * optional senders auth public key. | |
604 | * secret A buffer to return the calculated shared secret. Setting this to | |
605 | * NULL can be used to return the secretlen. | |
606 | * secretlen Passes in the max size of the secret buffer and returns the | |
607 | * secret length. | |
608 | * enc A buffer containing the senders ephemeral public key that was returned | |
609 | * from dhkem_encap(). | |
610 | * enclen The length in bytes of enc. | |
611 | * Returns: 1 If the shared secret is returned or 0 on error. | |
612 | */ | |
613 | static int dhkem_decap(PROV_ECX_CTX *ctx, | |
614 | unsigned char *secret, size_t *secretlen, | |
615 | const unsigned char *enc, size_t enclen) | |
616 | { | |
617 | int ret = 0; | |
618 | ECX_KEY *recipient_privkey = ctx->recipient_key; | |
619 | ECX_KEY *sender_ephempubkey = NULL; | |
ad062480 | 620 | const OSSL_HPKE_KEM_INFO *info = ctx->info; |
78c44b05 | 621 | unsigned char *recipient_pub; |
622 | ||
623 | if (secret == NULL) { | |
ad062480 | 624 | *secretlen = info->Nsecret; |
78c44b05 | 625 | return 1; |
626 | } | |
ad062480 | 627 | if (*secretlen < info->Nsecret) { |
78c44b05 | 628 | ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small"); |
629 | return 0; | |
630 | } | |
ad062480 | 631 | if (enclen != info->Nenc) { |
78c44b05 | 632 | ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key"); |
633 | return 0; | |
634 | } | |
635 | ||
636 | /* Get the public part of the ephemeral key created by encap */ | |
637 | sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen); | |
638 | if (sender_ephempubkey == NULL) | |
639 | goto err; | |
640 | ||
641 | recipient_pub = ecx_pubkey(recipient_privkey); | |
642 | if (recipient_pub == NULL) | |
643 | goto err; | |
644 | ||
645 | if (!derive_secret(ctx, secret, | |
646 | ctx->recipient_key, sender_ephempubkey, | |
647 | ctx->recipient_key, ctx->sender_authkey, | |
648 | enc, recipient_pub)) | |
649 | goto err; | |
650 | ||
ad062480 | 651 | *secretlen = info->Nsecret; |
78c44b05 | 652 | ret = 1; |
653 | err: | |
654 | ossl_ecx_key_free(sender_ephempubkey); | |
655 | return ret; | |
656 | } | |
657 | ||
658 | static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen, | |
659 | unsigned char *secret, size_t *secretlen) | |
660 | { | |
661 | PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx; | |
662 | ||
663 | switch (ctx->mode) { | |
664 | case KEM_MODE_DHKEM: | |
665 | return dhkem_encap(ctx, out, outlen, secret, secretlen); | |
666 | default: | |
667 | ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); | |
668 | return -2; | |
669 | } | |
670 | } | |
671 | ||
672 | static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen, | |
673 | const unsigned char *in, size_t inlen) | |
674 | { | |
675 | PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx; | |
676 | ||
677 | switch (ctx->mode) { | |
678 | case KEM_MODE_DHKEM: | |
679 | return dhkem_decap(vctx, out, outlen, in, inlen); | |
680 | default: | |
681 | ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); | |
682 | return -2; | |
683 | } | |
684 | } | |
685 | ||
686 | const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = { | |
687 | { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx }, | |
688 | { OSSL_FUNC_KEM_ENCAPSULATE_INIT, | |
689 | (void (*)(void))ecxkem_encapsulate_init }, | |
690 | { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate }, | |
691 | { OSSL_FUNC_KEM_DECAPSULATE_INIT, | |
692 | (void (*)(void))ecxkem_decapsulate_init }, | |
693 | { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate }, | |
694 | { OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx }, | |
695 | { OSSL_FUNC_KEM_SET_CTX_PARAMS, | |
696 | (void (*)(void))ecxkem_set_ctx_params }, | |
697 | { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, | |
698 | (void (*)(void))ecxkem_settable_ctx_params }, | |
699 | { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT, | |
700 | (void (*)(void))ecxkem_auth_encapsulate_init }, | |
701 | { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT, | |
702 | (void (*)(void))ecxkem_auth_decapsulate_init }, | |
1e6bd31e | 703 | OSSL_DISPATCH_END |
78c44b05 | 704 | }; |