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 */ 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 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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