123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- /*
- * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- #include <string.h>
- #include <openssl/core_names.h>
- #include <openssl/kdf.h>
- #include <openssl/params.h>
- #include <openssl/err.h>
- #include <openssl/proverr.h>
- #include <openssl/hpke.h>
- #include <openssl/sha.h>
- #include <openssl/rand.h>
- #include "crypto/ecx.h"
- #include "internal/hpke_util.h"
- #include "internal/packet.h"
- #include "internal/nelem.h"
- /*
- * Delimiter used in OSSL_HPKE_str2suite
- */
- #define OSSL_HPKE_STR_DELIMCHAR ','
- /*
- * table with identifier and synonym strings
- * right now, there are 4 synonyms for each - a name, a hex string
- * a hex string with a leading zero and a decimal string - more
- * could be added but that seems like enough
- */
- typedef struct {
- uint16_t id;
- char *synonyms[4];
- } synonymttab_t;
- /* max length of string we'll try map to a suite */
- #define OSSL_HPKE_MAX_SUITESTR 38
- /* Define HPKE labels from RFC9180 in hex for EBCDIC compatibility */
- /* ASCII: "HPKE-v1", in hex for EBCDIC compatibility */
- static const char LABEL_HPKEV1[] = "\x48\x50\x4B\x45\x2D\x76\x31";
- /*
- * Note that if additions are made to the set of IANA codepoints
- * and the tables below, corresponding additions should also be
- * made to the synonymtab tables a little further down so that
- * OSSL_HPKE_str2suite() continues to function correctly.
- *
- * The canonical place to check for IANA registered codepoints
- * is: https://www.iana.org/assignments/hpke/hpke.xhtml
- */
- /*
- * @brief table of KEMs
- * See RFC9180 Section 7.1 "Table 2 KEM IDs"
- */
- static const OSSL_HPKE_KEM_INFO hpke_kem_tab[] = {
- #ifndef OPENSSL_NO_EC
- { OSSL_HPKE_KEM_ID_P256, "EC", OSSL_HPKE_KEMSTR_P256,
- LN_sha256, SHA256_DIGEST_LENGTH, 65, 65, 32, 0xFF },
- { OSSL_HPKE_KEM_ID_P384, "EC", OSSL_HPKE_KEMSTR_P384,
- LN_sha384, SHA384_DIGEST_LENGTH, 97, 97, 48, 0xFF },
- { OSSL_HPKE_KEM_ID_P521, "EC", OSSL_HPKE_KEMSTR_P521,
- LN_sha512, SHA512_DIGEST_LENGTH, 133, 133, 66, 0x01 },
- # ifndef OPENSSL_NO_ECX
- { OSSL_HPKE_KEM_ID_X25519, OSSL_HPKE_KEMSTR_X25519, NULL,
- LN_sha256, SHA256_DIGEST_LENGTH,
- X25519_KEYLEN, X25519_KEYLEN, X25519_KEYLEN, 0x00 },
- { OSSL_HPKE_KEM_ID_X448, OSSL_HPKE_KEMSTR_X448, NULL,
- LN_sha512, SHA512_DIGEST_LENGTH,
- X448_KEYLEN, X448_KEYLEN, X448_KEYLEN, 0x00 }
- # endif
- #else
- { OSSL_HPKE_KEM_ID_RESERVED, NULL, NULL, NULL, 0, 0, 0, 0, 0x00 }
- #endif
- };
- /*
- * @brief table of AEADs
- * See RFC9180 Section 7.2 "Table 3 KDF IDs"
- */
- static const OSSL_HPKE_AEAD_INFO hpke_aead_tab[] = {
- { OSSL_HPKE_AEAD_ID_AES_GCM_128, LN_aes_128_gcm, 16, 16,
- OSSL_HPKE_MAX_NONCELEN },
- { OSSL_HPKE_AEAD_ID_AES_GCM_256, LN_aes_256_gcm, 16, 32,
- OSSL_HPKE_MAX_NONCELEN },
- #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
- { OSSL_HPKE_AEAD_ID_CHACHA_POLY1305, LN_chacha20_poly1305, 16, 32,
- OSSL_HPKE_MAX_NONCELEN },
- #endif
- { OSSL_HPKE_AEAD_ID_EXPORTONLY, NULL, 0, 0, 0 }
- };
- /*
- * @brief table of KDFs
- * See RFC9180 Section 7.3 "Table 5 AEAD IDs"
- */
- static const OSSL_HPKE_KDF_INFO hpke_kdf_tab[] = {
- { OSSL_HPKE_KDF_ID_HKDF_SHA256, LN_sha256, SHA256_DIGEST_LENGTH },
- { OSSL_HPKE_KDF_ID_HKDF_SHA384, LN_sha384, SHA384_DIGEST_LENGTH },
- { OSSL_HPKE_KDF_ID_HKDF_SHA512, LN_sha512, SHA512_DIGEST_LENGTH }
- };
- /**
- * Synonym tables for KEMs, KDFs and AEADs: idea is to allow
- * mapping strings to suites with a little flexibility in terms
- * of allowing a name or a couple of forms of number (for
- * the IANA codepoint). If new IANA codepoints are allocated
- * then these tables should be updated at the same time as the
- * others above.
- *
- * The function to use these is ossl_hpke_str2suite() further down
- * this file and shouldn't need modification so long as the table
- * sizes (i.e. allow exactly 4 synonyms) don't change.
- */
- static const synonymttab_t kemstrtab[] = {
- {OSSL_HPKE_KEM_ID_P256,
- {OSSL_HPKE_KEMSTR_P256, "0x10", "0x10", "16" }},
- {OSSL_HPKE_KEM_ID_P384,
- {OSSL_HPKE_KEMSTR_P384, "0x11", "0x11", "17" }},
- {OSSL_HPKE_KEM_ID_P521,
- {OSSL_HPKE_KEMSTR_P521, "0x12", "0x12", "18" }},
- # ifndef OPENSSL_NO_ECX
- {OSSL_HPKE_KEM_ID_X25519,
- {OSSL_HPKE_KEMSTR_X25519, "0x20", "0x20", "32" }},
- {OSSL_HPKE_KEM_ID_X448,
- {OSSL_HPKE_KEMSTR_X448, "0x21", "0x21", "33" }}
- # endif
- };
- static const synonymttab_t kdfstrtab[] = {
- {OSSL_HPKE_KDF_ID_HKDF_SHA256,
- {OSSL_HPKE_KDFSTR_256, "0x1", "0x01", "1"}},
- {OSSL_HPKE_KDF_ID_HKDF_SHA384,
- {OSSL_HPKE_KDFSTR_384, "0x2", "0x02", "2"}},
- {OSSL_HPKE_KDF_ID_HKDF_SHA512,
- {OSSL_HPKE_KDFSTR_512, "0x3", "0x03", "3"}}
- };
- static const synonymttab_t aeadstrtab[] = {
- {OSSL_HPKE_AEAD_ID_AES_GCM_128,
- {OSSL_HPKE_AEADSTR_AES128GCM, "0x1", "0x01", "1"}},
- {OSSL_HPKE_AEAD_ID_AES_GCM_256,
- {OSSL_HPKE_AEADSTR_AES256GCM, "0x2", "0x02", "2"}},
- {OSSL_HPKE_AEAD_ID_CHACHA_POLY1305,
- {OSSL_HPKE_AEADSTR_CP, "0x3", "0x03", "3"}},
- {OSSL_HPKE_AEAD_ID_EXPORTONLY,
- {OSSL_HPKE_AEADSTR_EXP, "ff", "0xff", "255"}}
- };
- /* Return an object containing KEM constants associated with a EC curve name */
- const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_curve(const char *curve)
- {
- int i, sz = OSSL_NELEM(hpke_kem_tab);
- for (i = 0; i < sz; ++i) {
- const char *group = hpke_kem_tab[i].groupname;
- if (group == NULL)
- group = hpke_kem_tab[i].keytype;
- if (OPENSSL_strcasecmp(curve, group) == 0)
- return &hpke_kem_tab[i];
- }
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
- return NULL;
- }
- const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_id(uint16_t kemid)
- {
- int i, sz = OSSL_NELEM(hpke_kem_tab);
- /*
- * this check can happen if we're in a no-ec build and there are no
- * KEMS available
- */
- if (kemid == OSSL_HPKE_KEM_ID_RESERVED) {
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
- return NULL;
- }
- for (i = 0; i != sz; ++i) {
- if (hpke_kem_tab[i].kem_id == kemid)
- return &hpke_kem_tab[i];
- }
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
- return NULL;
- }
- const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_random(OSSL_LIB_CTX *ctx)
- {
- unsigned char rval = 0;
- int sz = OSSL_NELEM(hpke_kem_tab);
- if (RAND_bytes_ex(ctx, &rval, sizeof(rval), 0) <= 0)
- return NULL;
- return &hpke_kem_tab[rval % sz];
- }
- const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid)
- {
- int i, sz = OSSL_NELEM(hpke_kdf_tab);
- for (i = 0; i != sz; ++i) {
- if (hpke_kdf_tab[i].kdf_id == kdfid)
- return &hpke_kdf_tab[i];
- }
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KDF);
- return NULL;
- }
- const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_random(OSSL_LIB_CTX *ctx)
- {
- unsigned char rval = 0;
- int sz = OSSL_NELEM(hpke_kdf_tab);
- if (RAND_bytes_ex(ctx, &rval, sizeof(rval), 0) <= 0)
- return NULL;
- return &hpke_kdf_tab[rval % sz];
- }
- const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid)
- {
- int i, sz = OSSL_NELEM(hpke_aead_tab);
- for (i = 0; i != sz; ++i) {
- if (hpke_aead_tab[i].aead_id == aeadid)
- return &hpke_aead_tab[i];
- }
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_AEAD);
- return NULL;
- }
- const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_random(OSSL_LIB_CTX *ctx)
- {
- unsigned char rval = 0;
- /* the minus 1 below is so we don't pick the EXPORTONLY codepoint */
- int sz = OSSL_NELEM(hpke_aead_tab) - 1;
- if (RAND_bytes_ex(ctx, &rval, sizeof(rval), 0) <= 0)
- return NULL;
- return &hpke_aead_tab[rval % sz];
- }
- static int 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)
- {
- int ret;
- OSSL_PARAM params[5], *p = params;
- *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
- if (salt != NULL)
- *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
- (char *)salt, saltlen);
- if (ikm != NULL)
- *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
- (char *)ikm, ikmlen);
- if (info != NULL)
- *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
- (char *)info, infolen);
- *p = OSSL_PARAM_construct_end();
- ret = EVP_KDF_derive(kctx, out, outlen, params) > 0;
- if (!ret)
- ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
- return ret;
- }
- int 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)
- {
- return kdf_derive(kctx, prk, prklen, EVP_KDF_HKDF_MODE_EXTRACT_ONLY,
- salt, saltlen, ikm, ikmlen, NULL, 0);
- }
- /* Common code to perform a HKDF expand */
- int 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)
- {
- return kdf_derive(kctx, okm, okmlen, EVP_KDF_HKDF_MODE_EXPAND_ONLY,
- NULL, 0, prk, prklen, info, infolen);
- }
- /*
- * See RFC 9180 Section 4 LabelExtract()
- */
- int 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)
- {
- int ret = 0;
- size_t label_hpkev1len = 0;
- size_t protocol_labellen = 0;
- size_t labellen = 0;
- size_t labeled_ikmlen = 0;
- unsigned char *labeled_ikm = NULL;
- WPACKET pkt;
- label_hpkev1len = strlen(LABEL_HPKEV1);
- protocol_labellen = strlen(protocol_label);
- labellen = strlen(label);
- labeled_ikmlen = label_hpkev1len + protocol_labellen
- + suiteidlen + labellen + ikmlen;
- labeled_ikm = OPENSSL_malloc(labeled_ikmlen);
- if (labeled_ikm == NULL)
- return 0;
- /* labeled_ikm = concat("HPKE-v1", suiteid, label, ikm) */
- if (!WPACKET_init_static_len(&pkt, labeled_ikm, labeled_ikmlen, 0)
- || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, label_hpkev1len)
- || !WPACKET_memcpy(&pkt, protocol_label, protocol_labellen)
- || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
- || !WPACKET_memcpy(&pkt, label, labellen)
- || !WPACKET_memcpy(&pkt, ikm, ikmlen)
- || !WPACKET_get_total_written(&pkt, &labeled_ikmlen)
- || !WPACKET_finish(&pkt)) {
- ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
- goto end;
- }
- ret = ossl_hpke_kdf_extract(kctx, prk, prklen, salt, saltlen,
- labeled_ikm, labeled_ikmlen);
- end:
- WPACKET_cleanup(&pkt);
- OPENSSL_cleanse(labeled_ikm, labeled_ikmlen);
- OPENSSL_free(labeled_ikm);
- return ret;
- }
- /*
- * See RFC 9180 Section 4 LabelExpand()
- */
- int 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)
- {
- int ret = 0;
- size_t label_hpkev1len = 0;
- size_t protocol_labellen = 0;
- size_t labellen = 0;
- size_t labeled_infolen = 0;
- unsigned char *labeled_info = NULL;
- WPACKET pkt;
- label_hpkev1len = strlen(LABEL_HPKEV1);
- protocol_labellen = strlen(protocol_label);
- labellen = strlen(label);
- labeled_infolen = 2 + okmlen + prklen + label_hpkev1len
- + protocol_labellen + suiteidlen + labellen + infolen;
- labeled_info = OPENSSL_malloc(labeled_infolen);
- if (labeled_info == NULL)
- return 0;
- /* labeled_info = concat(okmlen, "HPKE-v1", suiteid, label, info) */
- if (!WPACKET_init_static_len(&pkt, labeled_info, labeled_infolen, 0)
- || !WPACKET_put_bytes_u16(&pkt, okmlen)
- || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, label_hpkev1len)
- || !WPACKET_memcpy(&pkt, protocol_label, protocol_labellen)
- || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
- || !WPACKET_memcpy(&pkt, label, labellen)
- || !WPACKET_memcpy(&pkt, info, infolen)
- || !WPACKET_get_total_written(&pkt, &labeled_infolen)
- || !WPACKET_finish(&pkt)) {
- ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
- goto end;
- }
- ret = ossl_hpke_kdf_expand(kctx, okm, okmlen,
- prk, prklen, labeled_info, labeled_infolen);
- end:
- WPACKET_cleanup(&pkt);
- OPENSSL_free(labeled_info);
- return ret;
- }
- /* Common code to create a HKDF ctx */
- EVP_KDF_CTX *ossl_kdf_ctx_create(const char *kdfname, const char *mdname,
- OSSL_LIB_CTX *libctx, const char *propq)
- {
- EVP_KDF *kdf;
- EVP_KDF_CTX *kctx = NULL;
- kdf = EVP_KDF_fetch(libctx, kdfname, propq);
- if (kdf == NULL) {
- ERR_raise(ERR_LIB_CRYPTO, ERR_R_FETCH_FAILED);
- return NULL;
- }
- kctx = EVP_KDF_CTX_new(kdf);
- EVP_KDF_free(kdf);
- if (kctx != NULL && mdname != NULL) {
- OSSL_PARAM params[3], *p = params;
- if (mdname != NULL)
- *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
- (char *)mdname, 0);
- if (propq != NULL)
- *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_PROPERTIES,
- (char *)propq, 0);
- *p = OSSL_PARAM_construct_end();
- if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
- EVP_KDF_CTX_free(kctx);
- return NULL;
- }
- }
- return kctx;
- }
- /*
- * @brief look for a label into the synonym tables, and return its id
- * @param st is the string value
- * @param synp is the synonyms labels array
- * @param arrsize is the previous array size
- * @return 0 when not found, else the matching item id.
- */
- static uint16_t synonyms_name2id(const char *st, const synonymttab_t *synp,
- size_t arrsize)
- {
- size_t i, j;
- for (i = 0; i < arrsize; ++i) {
- for (j = 0; j < OSSL_NELEM(synp[i].synonyms); ++j) {
- if (OPENSSL_strcasecmp(st, synp[i].synonyms[j]) == 0)
- return synp[i].id;
- }
- }
- return 0;
- }
- /*
- * @brief map a string to a HPKE suite based on synonym tables
- * @param str is the string value
- * @param suite is the resulting suite
- * @return 1 for success, otherwise failure
- */
- int ossl_hpke_str2suite(const char *suitestr, OSSL_HPKE_SUITE *suite)
- {
- uint16_t kem = 0, kdf = 0, aead = 0;
- char *st = NULL, *instrcp = NULL;
- size_t inplen;
- int labels = 0, result = 0;
- int delim_count = 0;
- if (suitestr == NULL || suitestr[0] == 0x00 || suite == NULL) {
- ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
- inplen = OPENSSL_strnlen(suitestr, OSSL_HPKE_MAX_SUITESTR);
- if (inplen >= OSSL_HPKE_MAX_SUITESTR) {
- ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
- return 0;
- }
- /*
- * we don't want a delimiter at the end of the string;
- * strtok_r/s() doesn't care about that, so we should
- */
- if (suitestr[inplen - 1] == OSSL_HPKE_STR_DELIMCHAR)
- return 0;
- /* We want exactly two delimiters in the input string */
- for (st = (char *)suitestr; *st != '\0'; st++) {
- if (*st == OSSL_HPKE_STR_DELIMCHAR)
- delim_count++;
- }
- if (delim_count != 2)
- return 0;
- /* Duplicate `suitestr` to allow its parsing */
- instrcp = OPENSSL_memdup(suitestr, inplen + 1);
- if (instrcp == NULL)
- goto fail;
- /* See if it contains a mix of our strings and numbers */
- st = instrcp;
- while (st != NULL && labels < 3) {
- char *cp = strchr(st, OSSL_HPKE_STR_DELIMCHAR);
- /* add a NUL like strtok would if we're not at the end */
- if (cp != NULL)
- *cp = '\0';
- /* check if string is known or number and if so handle appropriately */
- if (labels == 0
- && (kem = synonyms_name2id(st, kemstrtab,
- OSSL_NELEM(kemstrtab))) == 0)
- goto fail;
- else if (labels == 1
- && (kdf = synonyms_name2id(st, kdfstrtab,
- OSSL_NELEM(kdfstrtab))) == 0)
- goto fail;
- else if (labels == 2
- && (aead = synonyms_name2id(st, aeadstrtab,
- OSSL_NELEM(aeadstrtab))) == 0)
- goto fail;
- if (cp == NULL)
- st = NULL;
- else
- st = cp + 1;
- ++labels;
- }
- if (st != NULL || labels != 3)
- goto fail;
- suite->kem_id = kem;
- suite->kdf_id = kdf;
- suite->aead_id = aead;
- result = 1;
- fail:
- OPENSSL_free(instrcp);
- return result;
- }
|