1 /*
2 * Copyright 2022-2023 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/core_names.h>
12 #include <openssl/kdf.h>
13 #include <openssl/params.h>
14 #include <openssl/err.h>
15 #include <openssl/proverr.h>
16 #include <openssl/hpke.h>
17 #include <openssl/sha.h>
18 #include <openssl/rand.h>
19 #include "crypto/ecx.h"
20 #include "crypto/rand.h"
21 #include "internal/hpke_util.h"
22 #include "internal/packet.h"
23 #include "internal/nelem.h"
24 #include "internal/common.h"
25
26 /*
27 * Delimiter used in OSSL_HPKE_str2suite
28 */
29 #define OSSL_HPKE_STR_DELIMCHAR ','
30
31 /*
32 * table with identifier and synonym strings
33 * right now, there are 4 synonyms for each - a name, a hex string
34 * a hex string with a leading zero and a decimal string - more
35 * could be added but that seems like enough
36 */
37 typedef struct {
38 uint16_t id;
39 char *synonyms[4];
40 } synonymttab_t;
41
42 /* max length of string we'll try map to a suite */
43 #define OSSL_HPKE_MAX_SUITESTR 38
44
45 /* Define HPKE labels from RFC9180 in hex for EBCDIC compatibility */
46 /* ASCII: "HPKE-v1", in hex for EBCDIC compatibility */
47 static const char LABEL_HPKEV1[] = "\x48\x50\x4B\x45\x2D\x76\x31";
48
49 /*
50 * Note that if additions are made to the set of IANA codepoints
51 * and the tables below, corresponding additions should also be
52 * made to the synonymtab tables a little further down so that
53 * OSSL_HPKE_str2suite() continues to function correctly.
54 *
55 * The canonical place to check for IANA registered codepoints
56 * is: https://www.iana.org/assignments/hpke/hpke.xhtml
57 */
58
59 /*
60 * @brief table of KEMs
61 * See RFC9180 Section 7.1 "Table 2 KEM IDs"
62 */
63 static const OSSL_HPKE_KEM_INFO hpke_kem_tab[] = {
64 #ifndef OPENSSL_NO_EC
65 { OSSL_HPKE_KEM_ID_P256, "EC", OSSL_HPKE_KEMSTR_P256,
66 LN_sha256, SHA256_DIGEST_LENGTH, 65, 65, 32, 0xFF },
67 { OSSL_HPKE_KEM_ID_P384, "EC", OSSL_HPKE_KEMSTR_P384,
68 LN_sha384, SHA384_DIGEST_LENGTH, 97, 97, 48, 0xFF },
69 { OSSL_HPKE_KEM_ID_P521, "EC", OSSL_HPKE_KEMSTR_P521,
70 LN_sha512, SHA512_DIGEST_LENGTH, 133, 133, 66, 0x01 },
71 # ifndef OPENSSL_NO_ECX
72 { OSSL_HPKE_KEM_ID_X25519, OSSL_HPKE_KEMSTR_X25519, NULL,
73 LN_sha256, SHA256_DIGEST_LENGTH,
74 X25519_KEYLEN, X25519_KEYLEN, X25519_KEYLEN, 0x00 },
75 { OSSL_HPKE_KEM_ID_X448, OSSL_HPKE_KEMSTR_X448, NULL,
76 LN_sha512, SHA512_DIGEST_LENGTH,
77 X448_KEYLEN, X448_KEYLEN, X448_KEYLEN, 0x00 }
78 # endif
79 #else
80 { OSSL_HPKE_KEM_ID_RESERVED, NULL, NULL, NULL, 0, 0, 0, 0, 0x00 }
81 #endif
82 };
83
84 /*
85 * @brief table of AEADs
86 * See RFC9180 Section 7.2 "Table 3 KDF IDs"
87 */
88 static const OSSL_HPKE_AEAD_INFO hpke_aead_tab[] = {
89 { OSSL_HPKE_AEAD_ID_AES_GCM_128, LN_aes_128_gcm, 16, 16,
90 OSSL_HPKE_MAX_NONCELEN },
91 { OSSL_HPKE_AEAD_ID_AES_GCM_256, LN_aes_256_gcm, 16, 32,
92 OSSL_HPKE_MAX_NONCELEN },
93 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
94 { OSSL_HPKE_AEAD_ID_CHACHA_POLY1305, LN_chacha20_poly1305, 16, 32,
95 OSSL_HPKE_MAX_NONCELEN },
96 #endif
97 { OSSL_HPKE_AEAD_ID_EXPORTONLY, NULL, 0, 0, 0 }
98 };
99
100 /*
101 * @brief table of KDFs
102 * See RFC9180 Section 7.3 "Table 5 AEAD IDs"
103 */
104 static const OSSL_HPKE_KDF_INFO hpke_kdf_tab[] = {
105 { OSSL_HPKE_KDF_ID_HKDF_SHA256, LN_sha256, SHA256_DIGEST_LENGTH },
106 { OSSL_HPKE_KDF_ID_HKDF_SHA384, LN_sha384, SHA384_DIGEST_LENGTH },
107 { OSSL_HPKE_KDF_ID_HKDF_SHA512, LN_sha512, SHA512_DIGEST_LENGTH }
108 };
109
110 /**
111 * Synonym tables for KEMs, KDFs and AEADs: idea is to allow
112 * mapping strings to suites with a little flexibility in terms
113 * of allowing a name or a couple of forms of number (for
114 * the IANA codepoint). If new IANA codepoints are allocated
115 * then these tables should be updated at the same time as the
116 * others above.
117 *
118 * The function to use these is ossl_hpke_str2suite() further down
119 * this file and shouldn't need modification so long as the table
120 * sizes (i.e. allow exactly 4 synonyms) don't change.
121 */
122 static const synonymttab_t kemstrtab[] = {
123 {OSSL_HPKE_KEM_ID_P256,
124 {OSSL_HPKE_KEMSTR_P256, "0x10", "0x10", "16" }},
125 {OSSL_HPKE_KEM_ID_P384,
126 {OSSL_HPKE_KEMSTR_P384, "0x11", "0x11", "17" }},
127 {OSSL_HPKE_KEM_ID_P521,
128 {OSSL_HPKE_KEMSTR_P521, "0x12", "0x12", "18" }},
129 # ifndef OPENSSL_NO_ECX
130 {OSSL_HPKE_KEM_ID_X25519,
131 {OSSL_HPKE_KEMSTR_X25519, "0x20", "0x20", "32" }},
132 {OSSL_HPKE_KEM_ID_X448,
133 {OSSL_HPKE_KEMSTR_X448, "0x21", "0x21", "33" }}
134 # endif
135 };
136 static const synonymttab_t kdfstrtab[] = {
137 {OSSL_HPKE_KDF_ID_HKDF_SHA256,
138 {OSSL_HPKE_KDFSTR_256, "0x1", "0x01", "1"}},
139 {OSSL_HPKE_KDF_ID_HKDF_SHA384,
140 {OSSL_HPKE_KDFSTR_384, "0x2", "0x02", "2"}},
141 {OSSL_HPKE_KDF_ID_HKDF_SHA512,
142 {OSSL_HPKE_KDFSTR_512, "0x3", "0x03", "3"}}
143 };
144 static const synonymttab_t aeadstrtab[] = {
145 {OSSL_HPKE_AEAD_ID_AES_GCM_128,
146 {OSSL_HPKE_AEADSTR_AES128GCM, "0x1", "0x01", "1"}},
147 {OSSL_HPKE_AEAD_ID_AES_GCM_256,
148 {OSSL_HPKE_AEADSTR_AES256GCM, "0x2", "0x02", "2"}},
149 {OSSL_HPKE_AEAD_ID_CHACHA_POLY1305,
150 {OSSL_HPKE_AEADSTR_CP, "0x3", "0x03", "3"}},
151 {OSSL_HPKE_AEAD_ID_EXPORTONLY,
152 {OSSL_HPKE_AEADSTR_EXP, "ff", "0xff", "255"}}
153 };
154
155 /* Return an object containing KEM constants associated with a EC curve name */
ossl_HPKE_KEM_INFO_find_curve(const char * curve)156 const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_curve(const char *curve)
157 {
158 int i, sz = OSSL_NELEM(hpke_kem_tab);
159
160 for (i = 0; i < sz; ++i) {
161 const char *group = hpke_kem_tab[i].groupname;
162
163 if (group == NULL)
164 group = hpke_kem_tab[i].keytype;
165 if (OPENSSL_strcasecmp(curve, group) == 0)
166 return &hpke_kem_tab[i];
167 }
168 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
169 return NULL;
170 }
171
ossl_HPKE_KEM_INFO_find_id(uint16_t kemid)172 const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_id(uint16_t kemid)
173 {
174 int i, sz = OSSL_NELEM(hpke_kem_tab);
175
176 /*
177 * this check can happen if we're in a no-ec build and there are no
178 * KEMS available
179 */
180 if (kemid == OSSL_HPKE_KEM_ID_RESERVED) {
181 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
182 return NULL;
183 }
184 for (i = 0; i != sz; ++i) {
185 if (hpke_kem_tab[i].kem_id == kemid)
186 return &hpke_kem_tab[i];
187 }
188 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
189 return NULL;
190 }
191
ossl_HPKE_KEM_INFO_find_random(OSSL_LIB_CTX * ctx)192 const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_random(OSSL_LIB_CTX *ctx)
193 {
194 uint32_t rval = 0;
195 int err = 0;
196 size_t sz = OSSL_NELEM(hpke_kem_tab);
197
198 rval = ossl_rand_uniform_uint32(ctx, sz, &err);
199 return (err == 1 ? NULL : &hpke_kem_tab[rval]);
200 }
201
ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid)202 const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid)
203 {
204 int i, sz = OSSL_NELEM(hpke_kdf_tab);
205
206 for (i = 0; i != sz; ++i) {
207 if (hpke_kdf_tab[i].kdf_id == kdfid)
208 return &hpke_kdf_tab[i];
209 }
210 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KDF);
211 return NULL;
212 }
213
ossl_HPKE_KDF_INFO_find_random(OSSL_LIB_CTX * ctx)214 const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_random(OSSL_LIB_CTX *ctx)
215 {
216 uint32_t rval = 0;
217 int err = 0;
218 size_t sz = OSSL_NELEM(hpke_kdf_tab);
219
220 rval = ossl_rand_uniform_uint32(ctx, sz, &err);
221 return (err == 1 ? NULL : &hpke_kdf_tab[rval]);
222 }
223
ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid)224 const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid)
225 {
226 int i, sz = OSSL_NELEM(hpke_aead_tab);
227
228 for (i = 0; i != sz; ++i) {
229 if (hpke_aead_tab[i].aead_id == aeadid)
230 return &hpke_aead_tab[i];
231 }
232 ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_AEAD);
233 return NULL;
234 }
235
ossl_HPKE_AEAD_INFO_find_random(OSSL_LIB_CTX * ctx)236 const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_random(OSSL_LIB_CTX *ctx)
237 {
238 uint32_t rval = 0;
239 int err = 0;
240 /* the minus 1 below is so we don't pick the EXPORTONLY codepoint */
241 size_t sz = OSSL_NELEM(hpke_aead_tab) - 1;
242
243 rval = ossl_rand_uniform_uint32(ctx, sz, &err);
244 return (err == 1 ? NULL : &hpke_aead_tab[rval]);
245 }
246
kdf_derive(EVP_KDF_CTX * kctx,unsigned char * out,size_t outlen,int mode,const unsigned char * salt,size_t saltlen,const unsigned char * ikm,size_t ikmlen,const unsigned char * info,size_t infolen)247 static int kdf_derive(EVP_KDF_CTX *kctx,
248 unsigned char *out, size_t outlen, int mode,
249 const unsigned char *salt, size_t saltlen,
250 const unsigned char *ikm, size_t ikmlen,
251 const unsigned char *info, size_t infolen)
252 {
253 int ret;
254 OSSL_PARAM params[5], *p = params;
255
256 *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
257 if (salt != NULL)
258 *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
259 (char *)salt, saltlen);
260 if (ikm != NULL)
261 *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
262 (char *)ikm, ikmlen);
263 if (info != NULL)
264 *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
265 (char *)info, infolen);
266 *p = OSSL_PARAM_construct_end();
267 ret = EVP_KDF_derive(kctx, out, outlen, params) > 0;
268 if (!ret)
269 ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
270 return ret;
271 }
272
ossl_hpke_kdf_extract(EVP_KDF_CTX * kctx,unsigned char * prk,size_t prklen,const unsigned char * salt,size_t saltlen,const unsigned char * ikm,size_t ikmlen)273 int ossl_hpke_kdf_extract(EVP_KDF_CTX *kctx,
274 unsigned char *prk, size_t prklen,
275 const unsigned char *salt, size_t saltlen,
276 const unsigned char *ikm, size_t ikmlen)
277 {
278 return kdf_derive(kctx, prk, prklen, EVP_KDF_HKDF_MODE_EXTRACT_ONLY,
279 salt, saltlen, ikm, ikmlen, NULL, 0);
280 }
281
282 /* Common code to perform a HKDF expand */
ossl_hpke_kdf_expand(EVP_KDF_CTX * kctx,unsigned char * okm,size_t okmlen,const unsigned char * prk,size_t prklen,const unsigned char * info,size_t infolen)283 int ossl_hpke_kdf_expand(EVP_KDF_CTX *kctx,
284 unsigned char *okm, size_t okmlen,
285 const unsigned char *prk, size_t prklen,
286 const unsigned char *info, size_t infolen)
287 {
288 return kdf_derive(kctx, okm, okmlen, EVP_KDF_HKDF_MODE_EXPAND_ONLY,
289 NULL, 0, prk, prklen, info, infolen);
290 }
291
292 /*
293 * See RFC 9180 Section 4 LabelExtract()
294 */
ossl_hpke_labeled_extract(EVP_KDF_CTX * kctx,unsigned char * prk,size_t prklen,const unsigned char * salt,size_t saltlen,const char * protocol_label,const unsigned char * suiteid,size_t suiteidlen,const char * label,const unsigned char * ikm,size_t ikmlen)295 int ossl_hpke_labeled_extract(EVP_KDF_CTX *kctx,
296 unsigned char *prk, size_t prklen,
297 const unsigned char *salt, size_t saltlen,
298 const char *protocol_label,
299 const unsigned char *suiteid, size_t suiteidlen,
300 const char *label,
301 const unsigned char *ikm, size_t ikmlen)
302 {
303 int ret = 0;
304 size_t label_hpkev1len = 0;
305 size_t protocol_labellen = 0;
306 size_t labellen = 0;
307 size_t labeled_ikmlen = 0;
308 unsigned char *labeled_ikm = NULL;
309 WPACKET pkt;
310
311 label_hpkev1len = strlen(LABEL_HPKEV1);
312 protocol_labellen = strlen(protocol_label);
313 labellen = strlen(label);
314 labeled_ikmlen = label_hpkev1len + protocol_labellen
315 + suiteidlen + labellen + ikmlen;
316 labeled_ikm = OPENSSL_malloc(labeled_ikmlen);
317 if (labeled_ikm == NULL)
318 return 0;
319
320 /* labeled_ikm = concat("HPKE-v1", suiteid, label, ikm) */
321 if (!WPACKET_init_static_len(&pkt, labeled_ikm, labeled_ikmlen, 0)
322 || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, label_hpkev1len)
323 || !WPACKET_memcpy(&pkt, protocol_label, protocol_labellen)
324 || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
325 || !WPACKET_memcpy(&pkt, label, labellen)
326 || !WPACKET_memcpy(&pkt, ikm, ikmlen)
327 || !WPACKET_get_total_written(&pkt, &labeled_ikmlen)
328 || !WPACKET_finish(&pkt)) {
329 ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
330 goto end;
331 }
332
333 ret = ossl_hpke_kdf_extract(kctx, prk, prklen, salt, saltlen,
334 labeled_ikm, labeled_ikmlen);
335 end:
336 WPACKET_cleanup(&pkt);
337 OPENSSL_cleanse(labeled_ikm, labeled_ikmlen);
338 OPENSSL_free(labeled_ikm);
339 return ret;
340 }
341
342 /*
343 * See RFC 9180 Section 4 LabelExpand()
344 */
ossl_hpke_labeled_expand(EVP_KDF_CTX * kctx,unsigned char * okm,size_t okmlen,const unsigned char * prk,size_t prklen,const char * protocol_label,const unsigned char * suiteid,size_t suiteidlen,const char * label,const unsigned char * info,size_t infolen)345 int ossl_hpke_labeled_expand(EVP_KDF_CTX *kctx,
346 unsigned char *okm, size_t okmlen,
347 const unsigned char *prk, size_t prklen,
348 const char *protocol_label,
349 const unsigned char *suiteid, size_t suiteidlen,
350 const char *label,
351 const unsigned char *info, size_t infolen)
352 {
353 int ret = 0;
354 size_t label_hpkev1len = 0;
355 size_t protocol_labellen = 0;
356 size_t labellen = 0;
357 size_t labeled_infolen = 0;
358 unsigned char *labeled_info = NULL;
359 WPACKET pkt;
360
361 label_hpkev1len = strlen(LABEL_HPKEV1);
362 protocol_labellen = strlen(protocol_label);
363 labellen = strlen(label);
364 labeled_infolen = 2 + okmlen + prklen + label_hpkev1len
365 + protocol_labellen + suiteidlen + labellen + infolen;
366 labeled_info = OPENSSL_malloc(labeled_infolen);
367 if (labeled_info == NULL)
368 return 0;
369
370 /* labeled_info = concat(okmlen, "HPKE-v1", suiteid, label, info) */
371 if (!WPACKET_init_static_len(&pkt, labeled_info, labeled_infolen, 0)
372 || !WPACKET_put_bytes_u16(&pkt, okmlen)
373 || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, label_hpkev1len)
374 || !WPACKET_memcpy(&pkt, protocol_label, protocol_labellen)
375 || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
376 || !WPACKET_memcpy(&pkt, label, labellen)
377 || !WPACKET_memcpy(&pkt, info, infolen)
378 || !WPACKET_get_total_written(&pkt, &labeled_infolen)
379 || !WPACKET_finish(&pkt)) {
380 ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
381 goto end;
382 }
383
384 ret = ossl_hpke_kdf_expand(kctx, okm, okmlen,
385 prk, prklen, labeled_info, labeled_infolen);
386 end:
387 WPACKET_cleanup(&pkt);
388 OPENSSL_free(labeled_info);
389 return ret;
390 }
391
392 /* Common code to create a HKDF ctx */
ossl_kdf_ctx_create(const char * kdfname,const char * mdname,OSSL_LIB_CTX * libctx,const char * propq)393 EVP_KDF_CTX *ossl_kdf_ctx_create(const char *kdfname, const char *mdname,
394 OSSL_LIB_CTX *libctx, const char *propq)
395 {
396 EVP_KDF *kdf;
397 EVP_KDF_CTX *kctx = NULL;
398
399 kdf = EVP_KDF_fetch(libctx, kdfname, propq);
400 if (kdf == NULL) {
401 ERR_raise(ERR_LIB_CRYPTO, ERR_R_FETCH_FAILED);
402 return NULL;
403 }
404 kctx = EVP_KDF_CTX_new(kdf);
405 EVP_KDF_free(kdf);
406 if (kctx != NULL && mdname != NULL) {
407 OSSL_PARAM params[3], *p = params;
408
409 if (mdname != NULL)
410 *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
411 (char *)mdname, 0);
412 if (propq != NULL)
413 *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_PROPERTIES,
414 (char *)propq, 0);
415 *p = OSSL_PARAM_construct_end();
416 if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
417 EVP_KDF_CTX_free(kctx);
418 return NULL;
419 }
420 }
421 return kctx;
422 }
423
424 /*
425 * @brief look for a label into the synonym tables, and return its id
426 * @param st is the string value
427 * @param synp is the synonyms labels array
428 * @param arrsize is the previous array size
429 * @return 0 when not found, else the matching item id.
430 */
synonyms_name2id(const char * st,const synonymttab_t * synp,size_t arrsize)431 static uint16_t synonyms_name2id(const char *st, const synonymttab_t *synp,
432 size_t arrsize)
433 {
434 size_t i, j;
435
436 for (i = 0; i < arrsize; ++i) {
437 for (j = 0; j < OSSL_NELEM(synp[i].synonyms); ++j) {
438 if (OPENSSL_strcasecmp(st, synp[i].synonyms[j]) == 0)
439 return synp[i].id;
440 }
441 }
442 return 0;
443 }
444
445 /*
446 * @brief map a string to a HPKE suite based on synonym tables
447 * @param str is the string value
448 * @param suite is the resulting suite
449 * @return 1 for success, otherwise failure
450 */
ossl_hpke_str2suite(const char * suitestr,OSSL_HPKE_SUITE * suite)451 int ossl_hpke_str2suite(const char *suitestr, OSSL_HPKE_SUITE *suite)
452 {
453 uint16_t kem = 0, kdf = 0, aead = 0;
454 char *st = NULL, *instrcp = NULL;
455 size_t inplen;
456 int labels = 0, result = 0;
457 int delim_count = 0;
458
459 if (suitestr == NULL || suitestr[0] == 0x00 || suite == NULL) {
460 ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
461 return 0;
462 }
463 inplen = OPENSSL_strnlen(suitestr, OSSL_HPKE_MAX_SUITESTR);
464 if (inplen >= OSSL_HPKE_MAX_SUITESTR) {
465 ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
466 return 0;
467 }
468
469 /*
470 * we don't want a delimiter at the end of the string;
471 * strtok_r/s() doesn't care about that, so we should
472 */
473 if (suitestr[inplen - 1] == OSSL_HPKE_STR_DELIMCHAR)
474 return 0;
475 /* We want exactly two delimiters in the input string */
476 for (st = (char *)suitestr; *st != '\0'; st++) {
477 if (*st == OSSL_HPKE_STR_DELIMCHAR)
478 delim_count++;
479 }
480 if (delim_count != 2)
481 return 0;
482
483 /* Duplicate `suitestr` to allow its parsing */
484 instrcp = OPENSSL_memdup(suitestr, inplen + 1);
485 if (instrcp == NULL)
486 goto fail;
487
488 /* See if it contains a mix of our strings and numbers */
489 st = instrcp;
490
491 while (st != NULL && labels < 3) {
492 char *cp = strchr(st, OSSL_HPKE_STR_DELIMCHAR);
493
494 /* add a NUL like strtok would if we're not at the end */
495 if (cp != NULL)
496 *cp = '\0';
497
498 /* check if string is known or number and if so handle appropriately */
499 if (labels == 0
500 && (kem = synonyms_name2id(st, kemstrtab,
501 OSSL_NELEM(kemstrtab))) == 0)
502 goto fail;
503 else if (labels == 1
504 && (kdf = synonyms_name2id(st, kdfstrtab,
505 OSSL_NELEM(kdfstrtab))) == 0)
506 goto fail;
507 else if (labels == 2
508 && (aead = synonyms_name2id(st, aeadstrtab,
509 OSSL_NELEM(aeadstrtab))) == 0)
510 goto fail;
511
512 if (cp == NULL)
513 st = NULL;
514 else
515 st = cp + 1;
516 ++labels;
517 }
518 if (st != NULL || labels != 3)
519 goto fail;
520 suite->kem_id = kem;
521 suite->kdf_id = kdf;
522 suite->aead_id = aead;
523 result = 1;
524
525 fail:
526 OPENSSL_free(instrcp);
527 return result;
528 }
529