2 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
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
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 "internal/hpke_util.h"
21 #include "internal/packet.h"
22 #include "internal/nelem.h"
25 * Delimiter used in OSSL_HPKE_str2suite
27 #define OSSL_HPKE_STR_DELIMCHAR ','
30 * table with identifier and synonym strings
31 * right now, there are 4 synonyms for each - a name, a hex string
32 * a hex string with a leading zero and a decimal string - more
33 * could be added but that seems like enough
40 /* max length of string we'll try map to a suite */
41 #define OSSL_HPKE_MAX_SUITESTR 38
43 /* Define HPKE labels from RFC9180 in hex for EBCDIC compatibility */
44 /* ASCII: "HPKE-v1", in hex for EBCDIC compatibility */
45 static const char LABEL_HPKEV1
[] = "\x48\x50\x4B\x45\x2D\x76\x31";
48 * Note that if additions are made to the set of IANA codepoints
49 * and the tables below, corresponding additions should also be
50 * made to the synonymtab tables a little further down so that
51 * OSSL_HPKE_str2suite() continues to function correctly.
53 * The canonical place to check for IANA registered codepoints
54 * is: https://www.iana.org/assignments/hpke/hpke.xhtml
58 * @brief table of KEMs
59 * See RFC9180 Section 7.1 "Table 2 KEM IDs"
61 static const OSSL_HPKE_KEM_INFO hpke_kem_tab
[] = {
63 { OSSL_HPKE_KEM_ID_P256
, "EC", OSSL_HPKE_KEMSTR_P256
,
64 LN_sha256
, SHA256_DIGEST_LENGTH
, 65, 65, 32, 0xFF },
65 { OSSL_HPKE_KEM_ID_P384
, "EC", OSSL_HPKE_KEMSTR_P384
,
66 LN_sha384
, SHA384_DIGEST_LENGTH
, 97, 97, 48, 0xFF },
67 { OSSL_HPKE_KEM_ID_P521
, "EC", OSSL_HPKE_KEMSTR_P521
,
68 LN_sha512
, SHA512_DIGEST_LENGTH
, 133, 133, 66, 0x01 },
69 # ifndef OPENSSL_NO_ECX
70 { OSSL_HPKE_KEM_ID_X25519
, OSSL_HPKE_KEMSTR_X25519
, NULL
,
71 LN_sha256
, SHA256_DIGEST_LENGTH
,
72 X25519_KEYLEN
, X25519_KEYLEN
, X25519_KEYLEN
, 0x00 },
73 { OSSL_HPKE_KEM_ID_X448
, OSSL_HPKE_KEMSTR_X448
, NULL
,
74 LN_sha512
, SHA512_DIGEST_LENGTH
,
75 X448_KEYLEN
, X448_KEYLEN
, X448_KEYLEN
, 0x00 }
78 { OSSL_HPKE_KEM_ID_RESERVED
, NULL
, NULL
, NULL
, 0, 0, 0, 0, 0x00 }
83 * @brief table of AEADs
84 * See RFC9180 Section 7.2 "Table 3 KDF IDs"
86 static const OSSL_HPKE_AEAD_INFO hpke_aead_tab
[] = {
87 { OSSL_HPKE_AEAD_ID_AES_GCM_128
, LN_aes_128_gcm
, 16, 16,
88 OSSL_HPKE_MAX_NONCELEN
},
89 { OSSL_HPKE_AEAD_ID_AES_GCM_256
, LN_aes_256_gcm
, 16, 32,
90 OSSL_HPKE_MAX_NONCELEN
},
91 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
92 { OSSL_HPKE_AEAD_ID_CHACHA_POLY1305
, LN_chacha20_poly1305
, 16, 32,
93 OSSL_HPKE_MAX_NONCELEN
},
95 { OSSL_HPKE_AEAD_ID_EXPORTONLY
, NULL
, 0, 0, 0 }
99 * @brief table of KDFs
100 * See RFC9180 Section 7.3 "Table 5 AEAD IDs"
102 static const OSSL_HPKE_KDF_INFO hpke_kdf_tab
[] = {
103 { OSSL_HPKE_KDF_ID_HKDF_SHA256
, LN_sha256
, SHA256_DIGEST_LENGTH
},
104 { OSSL_HPKE_KDF_ID_HKDF_SHA384
, LN_sha384
, SHA384_DIGEST_LENGTH
},
105 { OSSL_HPKE_KDF_ID_HKDF_SHA512
, LN_sha512
, SHA512_DIGEST_LENGTH
}
109 * Synonym tables for KEMs, KDFs and AEADs: idea is to allow
110 * mapping strings to suites with a little flexibility in terms
111 * of allowing a name or a couple of forms of number (for
112 * the IANA codepoint). If new IANA codepoints are allocated
113 * then these tables should be updated at the same time as the
116 * The function to use these is ossl_hpke_str2suite() further down
117 * this file and shouldn't need modification so long as the table
118 * sizes (i.e. allow exactly 4 synonyms) don't change.
120 static const synonymttab_t kemstrtab
[] = {
121 {OSSL_HPKE_KEM_ID_P256
,
122 {OSSL_HPKE_KEMSTR_P256
, "0x10", "0x10", "16" }},
123 {OSSL_HPKE_KEM_ID_P384
,
124 {OSSL_HPKE_KEMSTR_P384
, "0x11", "0x11", "17" }},
125 {OSSL_HPKE_KEM_ID_P521
,
126 {OSSL_HPKE_KEMSTR_P521
, "0x12", "0x12", "18" }},
127 # ifndef OPENSSL_NO_ECX
128 {OSSL_HPKE_KEM_ID_X25519
,
129 {OSSL_HPKE_KEMSTR_X25519
, "0x20", "0x20", "32" }},
130 {OSSL_HPKE_KEM_ID_X448
,
131 {OSSL_HPKE_KEMSTR_X448
, "0x21", "0x21", "33" }}
134 static const synonymttab_t kdfstrtab
[] = {
135 {OSSL_HPKE_KDF_ID_HKDF_SHA256
,
136 {OSSL_HPKE_KDFSTR_256
, "0x1", "0x01", "1"}},
137 {OSSL_HPKE_KDF_ID_HKDF_SHA384
,
138 {OSSL_HPKE_KDFSTR_384
, "0x2", "0x02", "2"}},
139 {OSSL_HPKE_KDF_ID_HKDF_SHA512
,
140 {OSSL_HPKE_KDFSTR_512
, "0x3", "0x03", "3"}}
142 static const synonymttab_t aeadstrtab
[] = {
143 {OSSL_HPKE_AEAD_ID_AES_GCM_128
,
144 {OSSL_HPKE_AEADSTR_AES128GCM
, "0x1", "0x01", "1"}},
145 {OSSL_HPKE_AEAD_ID_AES_GCM_256
,
146 {OSSL_HPKE_AEADSTR_AES256GCM
, "0x2", "0x02", "2"}},
147 {OSSL_HPKE_AEAD_ID_CHACHA_POLY1305
,
148 {OSSL_HPKE_AEADSTR_CP
, "0x3", "0x03", "3"}},
149 {OSSL_HPKE_AEAD_ID_EXPORTONLY
,
150 {OSSL_HPKE_AEADSTR_EXP
, "ff", "0xff", "255"}}
153 /* Return an object containing KEM constants associated with a EC curve name */
154 const OSSL_HPKE_KEM_INFO
*ossl_HPKE_KEM_INFO_find_curve(const char *curve
)
156 int i
, sz
= OSSL_NELEM(hpke_kem_tab
);
158 for (i
= 0; i
< sz
; ++i
) {
159 const char *group
= hpke_kem_tab
[i
].groupname
;
162 group
= hpke_kem_tab
[i
].keytype
;
163 if (OPENSSL_strcasecmp(curve
, group
) == 0)
164 return &hpke_kem_tab
[i
];
166 ERR_raise(ERR_LIB_PROV
, PROV_R_INVALID_CURVE
);
170 const OSSL_HPKE_KEM_INFO
*ossl_HPKE_KEM_INFO_find_id(uint16_t kemid
)
172 int i
, sz
= OSSL_NELEM(hpke_kem_tab
);
175 * this check can happen if we're in a no-ec build and there are no
178 if (kemid
== OSSL_HPKE_KEM_ID_RESERVED
) {
179 ERR_raise(ERR_LIB_PROV
, PROV_R_INVALID_CURVE
);
182 for (i
= 0; i
!= sz
; ++i
) {
183 if (hpke_kem_tab
[i
].kem_id
== kemid
)
184 return &hpke_kem_tab
[i
];
186 ERR_raise(ERR_LIB_PROV
, PROV_R_INVALID_CURVE
);
190 const OSSL_HPKE_KEM_INFO
*ossl_HPKE_KEM_INFO_find_random(OSSL_LIB_CTX
*ctx
)
192 unsigned char rval
= 0;
193 int sz
= OSSL_NELEM(hpke_kem_tab
);
195 if (RAND_bytes_ex(ctx
, &rval
, sizeof(rval
), 0) <= 0)
197 return &hpke_kem_tab
[rval
% sz
];
200 const OSSL_HPKE_KDF_INFO
*ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid
)
202 int i
, sz
= OSSL_NELEM(hpke_kdf_tab
);
204 for (i
= 0; i
!= sz
; ++i
) {
205 if (hpke_kdf_tab
[i
].kdf_id
== kdfid
)
206 return &hpke_kdf_tab
[i
];
208 ERR_raise(ERR_LIB_PROV
, PROV_R_INVALID_KDF
);
212 const OSSL_HPKE_KDF_INFO
*ossl_HPKE_KDF_INFO_find_random(OSSL_LIB_CTX
*ctx
)
214 unsigned char rval
= 0;
215 int sz
= OSSL_NELEM(hpke_kdf_tab
);
217 if (RAND_bytes_ex(ctx
, &rval
, sizeof(rval
), 0) <= 0)
219 return &hpke_kdf_tab
[rval
% sz
];
222 const OSSL_HPKE_AEAD_INFO
*ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid
)
224 int i
, sz
= OSSL_NELEM(hpke_aead_tab
);
226 for (i
= 0; i
!= sz
; ++i
) {
227 if (hpke_aead_tab
[i
].aead_id
== aeadid
)
228 return &hpke_aead_tab
[i
];
230 ERR_raise(ERR_LIB_PROV
, PROV_R_INVALID_AEAD
);
234 const OSSL_HPKE_AEAD_INFO
*ossl_HPKE_AEAD_INFO_find_random(OSSL_LIB_CTX
*ctx
)
236 unsigned char rval
= 0;
237 /* the minus 1 below is so we don't pick the EXPORTONLY codepoint */
238 int sz
= OSSL_NELEM(hpke_aead_tab
) - 1;
240 if (RAND_bytes_ex(ctx
, &rval
, sizeof(rval
), 0) <= 0)
242 return &hpke_aead_tab
[rval
% sz
];
245 static int kdf_derive(EVP_KDF_CTX
*kctx
,
246 unsigned char *out
, size_t outlen
, int mode
,
247 const unsigned char *salt
, size_t saltlen
,
248 const unsigned char *ikm
, size_t ikmlen
,
249 const unsigned char *info
, size_t infolen
)
252 OSSL_PARAM params
[5], *p
= params
;
254 *p
++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE
, &mode
);
256 *p
++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT
,
257 (char *)salt
, saltlen
);
259 *p
++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY
,
260 (char *)ikm
, ikmlen
);
262 *p
++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO
,
263 (char *)info
, infolen
);
264 *p
= OSSL_PARAM_construct_end();
265 ret
= EVP_KDF_derive(kctx
, out
, outlen
, params
) > 0;
267 ERR_raise(ERR_LIB_PROV
, PROV_R_FAILED_DURING_DERIVATION
);
271 int ossl_hpke_kdf_extract(EVP_KDF_CTX
*kctx
,
272 unsigned char *prk
, size_t prklen
,
273 const unsigned char *salt
, size_t saltlen
,
274 const unsigned char *ikm
, size_t ikmlen
)
276 return kdf_derive(kctx
, prk
, prklen
, EVP_KDF_HKDF_MODE_EXTRACT_ONLY
,
277 salt
, saltlen
, ikm
, ikmlen
, NULL
, 0);
280 /* Common code to perform a HKDF expand */
281 int ossl_hpke_kdf_expand(EVP_KDF_CTX
*kctx
,
282 unsigned char *okm
, size_t okmlen
,
283 const unsigned char *prk
, size_t prklen
,
284 const unsigned char *info
, size_t infolen
)
286 return kdf_derive(kctx
, okm
, okmlen
, EVP_KDF_HKDF_MODE_EXPAND_ONLY
,
287 NULL
, 0, prk
, prklen
, info
, infolen
);
291 * See RFC 9180 Section 4 LabelExtract()
293 int ossl_hpke_labeled_extract(EVP_KDF_CTX
*kctx
,
294 unsigned char *prk
, size_t prklen
,
295 const unsigned char *salt
, size_t saltlen
,
296 const char *protocol_label
,
297 const unsigned char *suiteid
, size_t suiteidlen
,
299 const unsigned char *ikm
, size_t ikmlen
)
302 size_t label_hpkev1len
= 0;
303 size_t protocol_labellen
= 0;
305 size_t labeled_ikmlen
= 0;
306 unsigned char *labeled_ikm
= NULL
;
309 label_hpkev1len
= strlen(LABEL_HPKEV1
);
310 protocol_labellen
= strlen(protocol_label
);
311 labellen
= strlen(label
);
312 labeled_ikmlen
= label_hpkev1len
+ protocol_labellen
313 + suiteidlen
+ labellen
+ ikmlen
;
314 labeled_ikm
= OPENSSL_malloc(labeled_ikmlen
);
315 if (labeled_ikm
== NULL
)
318 /* labeled_ikm = concat("HPKE-v1", suiteid, label, ikm) */
319 if (!WPACKET_init_static_len(&pkt
, labeled_ikm
, labeled_ikmlen
, 0)
320 || !WPACKET_memcpy(&pkt
, LABEL_HPKEV1
, label_hpkev1len
)
321 || !WPACKET_memcpy(&pkt
, protocol_label
, protocol_labellen
)
322 || !WPACKET_memcpy(&pkt
, suiteid
, suiteidlen
)
323 || !WPACKET_memcpy(&pkt
, label
, labellen
)
324 || !WPACKET_memcpy(&pkt
, ikm
, ikmlen
)
325 || !WPACKET_get_total_written(&pkt
, &labeled_ikmlen
)
326 || !WPACKET_finish(&pkt
)) {
327 ERR_raise(ERR_LIB_PROV
, PROV_R_OUTPUT_BUFFER_TOO_SMALL
);
331 ret
= ossl_hpke_kdf_extract(kctx
, prk
, prklen
, salt
, saltlen
,
332 labeled_ikm
, labeled_ikmlen
);
334 WPACKET_cleanup(&pkt
);
335 OPENSSL_cleanse(labeled_ikm
, labeled_ikmlen
);
336 OPENSSL_free(labeled_ikm
);
341 * See RFC 9180 Section 4 LabelExpand()
343 int ossl_hpke_labeled_expand(EVP_KDF_CTX
*kctx
,
344 unsigned char *okm
, size_t okmlen
,
345 const unsigned char *prk
, size_t prklen
,
346 const char *protocol_label
,
347 const unsigned char *suiteid
, size_t suiteidlen
,
349 const unsigned char *info
, size_t infolen
)
352 size_t label_hpkev1len
= 0;
353 size_t protocol_labellen
= 0;
355 size_t labeled_infolen
= 0;
356 unsigned char *labeled_info
= NULL
;
359 label_hpkev1len
= strlen(LABEL_HPKEV1
);
360 protocol_labellen
= strlen(protocol_label
);
361 labellen
= strlen(label
);
362 labeled_infolen
= 2 + okmlen
+ prklen
+ label_hpkev1len
363 + protocol_labellen
+ suiteidlen
+ labellen
+ infolen
;
364 labeled_info
= OPENSSL_malloc(labeled_infolen
);
365 if (labeled_info
== NULL
)
368 /* labeled_info = concat(okmlen, "HPKE-v1", suiteid, label, info) */
369 if (!WPACKET_init_static_len(&pkt
, labeled_info
, labeled_infolen
, 0)
370 || !WPACKET_put_bytes_u16(&pkt
, okmlen
)
371 || !WPACKET_memcpy(&pkt
, LABEL_HPKEV1
, label_hpkev1len
)
372 || !WPACKET_memcpy(&pkt
, protocol_label
, protocol_labellen
)
373 || !WPACKET_memcpy(&pkt
, suiteid
, suiteidlen
)
374 || !WPACKET_memcpy(&pkt
, label
, labellen
)
375 || !WPACKET_memcpy(&pkt
, info
, infolen
)
376 || !WPACKET_get_total_written(&pkt
, &labeled_infolen
)
377 || !WPACKET_finish(&pkt
)) {
378 ERR_raise(ERR_LIB_PROV
, PROV_R_OUTPUT_BUFFER_TOO_SMALL
);
382 ret
= ossl_hpke_kdf_expand(kctx
, okm
, okmlen
,
383 prk
, prklen
, labeled_info
, labeled_infolen
);
385 WPACKET_cleanup(&pkt
);
386 OPENSSL_free(labeled_info
);
390 /* Common code to create a HKDF ctx */
391 EVP_KDF_CTX
*ossl_kdf_ctx_create(const char *kdfname
, const char *mdname
,
392 OSSL_LIB_CTX
*libctx
, const char *propq
)
395 EVP_KDF_CTX
*kctx
= NULL
;
397 kdf
= EVP_KDF_fetch(libctx
, kdfname
, propq
);
399 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_FETCH_FAILED
);
402 kctx
= EVP_KDF_CTX_new(kdf
);
404 if (kctx
!= NULL
&& mdname
!= NULL
) {
405 OSSL_PARAM params
[3], *p
= params
;
408 *p
++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST
,
411 *p
++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_PROPERTIES
,
413 *p
= OSSL_PARAM_construct_end();
414 if (EVP_KDF_CTX_set_params(kctx
, params
) <= 0) {
415 EVP_KDF_CTX_free(kctx
);
423 * @brief look for a label into the synonym tables, and return its id
424 * @param st is the string value
425 * @param synp is the synonyms labels array
426 * @param arrsize is the previous array size
427 * @return 0 when not found, else the matching item id.
429 static uint16_t synonyms_name2id(const char *st
, const synonymttab_t
*synp
,
434 for (i
= 0; i
< arrsize
; ++i
) {
435 for (j
= 0; j
< OSSL_NELEM(synp
[i
].synonyms
); ++j
) {
436 if (OPENSSL_strcasecmp(st
, synp
[i
].synonyms
[j
]) == 0)
444 * @brief map a string to a HPKE suite based on synonym tables
445 * @param str is the string value
446 * @param suite is the resulting suite
447 * @return 1 for success, otherwise failure
449 int ossl_hpke_str2suite(const char *suitestr
, OSSL_HPKE_SUITE
*suite
)
451 uint16_t kem
= 0, kdf
= 0, aead
= 0;
452 char *st
= NULL
, *instrcp
= NULL
;
454 int labels
= 0, result
= 0;
457 if (suitestr
== NULL
|| suitestr
[0] == 0x00 || suite
== NULL
) {
458 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_PASSED_NULL_PARAMETER
);
461 inplen
= OPENSSL_strnlen(suitestr
, OSSL_HPKE_MAX_SUITESTR
);
462 if (inplen
>= OSSL_HPKE_MAX_SUITESTR
) {
463 ERR_raise(ERR_LIB_CRYPTO
, ERR_R_PASSED_INVALID_ARGUMENT
);
468 * we don't want a delimiter at the end of the string;
469 * strtok_r/s() doesn't care about that, so we should
471 if (suitestr
[inplen
- 1] == OSSL_HPKE_STR_DELIMCHAR
)
473 /* We want exactly two delimiters in the input string */
474 for (st
= (char *)suitestr
; *st
!= '\0'; st
++) {
475 if (*st
== OSSL_HPKE_STR_DELIMCHAR
)
478 if (delim_count
!= 2)
481 /* Duplicate `suitestr` to allow its parsing */
482 instrcp
= OPENSSL_memdup(suitestr
, inplen
+ 1);
486 /* See if it contains a mix of our strings and numbers */
489 while (st
!= NULL
&& labels
< 3) {
490 char *cp
= strchr(st
, OSSL_HPKE_STR_DELIMCHAR
);
492 /* add a NUL like strtok would if we're not at the end */
496 /* check if string is known or number and if so handle appropriately */
498 && (kem
= synonyms_name2id(st
, kemstrtab
,
499 OSSL_NELEM(kemstrtab
))) == 0)
502 && (kdf
= synonyms_name2id(st
, kdfstrtab
,
503 OSSL_NELEM(kdfstrtab
))) == 0)
506 && (aead
= synonyms_name2id(st
, aeadstrtab
,
507 OSSL_NELEM(aeadstrtab
))) == 0)
516 if (st
!= NULL
|| labels
!= 3)
520 suite
->aead_id
= aead
;
524 OPENSSL_free(instrcp
);