summaryrefslogtreecommitdiff
path: root/providers/implementations/kem/ec_kem.c
diff options
context:
space:
mode:
authorStephen Farrell <stephen.farrell@cs.tcd.ie>2022-11-22 02:42:04 +0000
committerMatt Caswell <matt@openssl.org>2022-11-25 16:26:55 +0000
commitad062480f7490197b174edad8625ce40d74f6e68 (patch)
treef4ac43084558412509820b4d167b4c2906f5cfb2 /providers/implementations/kem/ec_kem.c
parent0dbd3a81e46dd7ea9f7832307fdd0b2ac207a5bf (diff)
downloadopenssl-new-ad062480f7490197b174edad8625ce40d74f6e68.tar.gz
Implements Hybrid Public Key Encryption (HPKE) as per RFC9180.
This supports all the modes, suites and export mechanisms defined in RFC9180 and should be relatively easily extensible if/as new suites are added. The APIs are based on the pseudo-code from the RFC, e.g. OSS_HPKE_encap() roughly maps to SetupBaseS(). External APIs are defined in include/openssl/hpke.h and documented in doc/man3/OSSL_HPKE_CTX_new.pod. Tests (test/hpke_test.c) include verifying a number of the test vectors from the RFC as well as round-tripping for all the modes and suites. We have demonstrated interoperability with other HPKE implementations via a fork [1] that implements TLS Encrypted ClientHello (ECH) which uses HPKE. @slontis provided huge help in getting this done and this makes extensive use of the KEM handling code from his PR#19068. [1] https://github.com/sftcd/openssl/tree/ECH-draft-13c Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Shane Lontis <shane.lontis@oracle.com> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/17172)
Diffstat (limited to 'providers/implementations/kem/ec_kem.c')
-rw-r--r--providers/implementations/kem/ec_kem.c125
1 files changed, 49 insertions, 76 deletions
diff --git a/providers/implementations/kem/ec_kem.c b/providers/implementations/kem/ec_kem.c
index 57dcea4196..cdec509f06 100644
--- a/providers/implementations/kem/ec_kem.c
+++ b/providers/implementations/kem/ec_kem.c
@@ -30,25 +30,12 @@
#include "prov/securitycheck.h"
#include "prov/providercommon.h"
-#include "crypto/hpke.h"
+#include <openssl/hpke.h>
+#include "internal/hpke_util.h"
#include "crypto/ec.h"
#include "prov/ecx.h"
#include "eckem.h"
-/*
- * Used to store constants from Section 7.1 "Table 2 KEM IDs"
- * and the bitmask for curves described in Section 7.1.3 DeriveKeyPair
- */
-typedef struct {
- const char *curve;
- const char *kdfdigestname;
- uint16_t kemid;
- size_t secretlen; /* Nsecret = Nh */
- size_t encodedpublen;
- size_t encodedprivlen;
- uint8_t bitmask;
-} DHKEM_ALG;
-
typedef struct {
EC_KEY *recipient_key;
EC_KEY *sender_authkey;
@@ -59,7 +46,7 @@ typedef struct {
unsigned char *ikm;
size_t ikmlen;
const char *kdfname;
- const DHKEM_ALG *alg;
+ const OSSL_HPKE_KEM_INFO *info;
} PROV_EC_CTX;
static OSSL_FUNC_kem_newctx_fn eckem_newctx;
@@ -73,26 +60,8 @@ static OSSL_FUNC_kem_freectx_fn eckem_freectx;
static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params;
static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params;
-/* See Section 7.1 "Table 2 KEM IDs" */
-static const DHKEM_ALG dhkem_alg[] = {
- { "P-256", "SHA256", 0x0010, 32, 65, 32, 0xFF },
- { "P-384", "SHA384", 0x0011, 48, 97, 48, 0xFF },
- { "P-521", "SHA512", 0x0012, 64, 133, 66, 0x01 },
- { NULL }
-};
-
-/* Return an object containing KEM constants associated with a EC curve name */
-static const DHKEM_ALG *dhkem_ec_find_alg(const char *curve)
-{
- int i;
-
- for (i = 0; dhkem_alg[i].curve != NULL; ++i) {
- if (OPENSSL_strcasecmp(curve, dhkem_alg[i].curve) == 0)
- return &dhkem_alg[i];
- }
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
- return NULL;
-}
+/* ASCII: "KEM", in hex for EBCDIC compatibility */
+static const char LABEL_KEM[] = "\x4b\x45\x4d";
static int eckey_check(const EC_KEY *ec, int requires_privatekey)
{
@@ -151,8 +120,8 @@ static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
if (curve == NULL)
return -2;
- ctx->alg = dhkem_ec_find_alg(curve);
- if (ctx->alg == NULL)
+ ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);
+ if (ctx->info == NULL)
return -2;
if (!EC_KEY_up_ref(ec))
return 0;
@@ -372,7 +341,7 @@ static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
const unsigned char *kemctx,
size_t kemctxlen)
{
- uint8_t suiteid[5];
+ uint8_t suiteid[2];
uint8_t prk[EVP_MAX_MD_SIZE];
size_t prklen = okmlen;
int ret;
@@ -380,13 +349,14 @@ static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
if (prklen > sizeof(prk))
return 0;
- ossl_dhkem_getsuiteid(suiteid, kemid);
+ suiteid[0] = (kemid >> 8) & 0xff;
+ suiteid[1] = kemid & 0xff;
ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
- NULL, 0, suiteid, sizeof(suiteid),
+ NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
&& ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
- suiteid, sizeof(suiteid),
+ LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_SHARED_SECRET,
kemctx, kemctxlen);
OPENSSL_cleanse(prk, prklen);
@@ -413,52 +383,53 @@ int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
{
int ret = 0;
EVP_KDF_CTX *kdfctx = NULL;
- uint8_t suiteid[5];
+ uint8_t suiteid[2];
unsigned char prk[OSSL_HPKE_MAX_SECRET];
unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
const BIGNUM *order;
unsigned char counter = 0;
- const DHKEM_ALG *alg;
const char *curve = ec_curvename_get0(ec);
+ const OSSL_HPKE_KEM_INFO *info;
if (curve == NULL)
return -2;
- alg = dhkem_ec_find_alg(curve);
- if (alg == NULL)
+ info = ossl_HPKE_KEM_INFO_find_curve(curve);
+ if (info == NULL)
return -2;
- kdfctx = ossl_kdf_ctx_create("HKDF", alg->kdfdigestname,
+ kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,
ossl_ec_key_get_libctx(ec),
ossl_ec_key_get0_propq(ec));
if (kdfctx == NULL)
return 0;
/* ikmlen should have a length of at least Nsk */
- if (ikmlen < alg->encodedprivlen) {
+ if (ikmlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
"ikm length is :%zu, should be at least %zu",
- ikmlen, alg->encodedprivlen);
+ ikmlen, info->Nsecret);
goto err;
}
- ossl_dhkem_getsuiteid(suiteid, alg->kemid);
+ suiteid[0] = info->kem_id / 256;
+ suiteid[1] = info->kem_id % 256;
- if (!ossl_hpke_labeled_extract(kdfctx, prk, alg->secretlen,
- NULL, 0, suiteid, sizeof(suiteid),
+ if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
+ NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
goto err;
order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
do {
- if (!ossl_hpke_labeled_expand(kdfctx, privbuf, alg->encodedprivlen,
- prk, alg->secretlen,
- suiteid, sizeof(suiteid),
+ if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,
+ prk, info->Nsecret,
+ LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_CANDIDATE,
&counter, 1))
goto err;
- privbuf[0] &= alg->bitmask;
- if (BN_bin2bn(privbuf, alg->encodedprivlen, priv) == NULL)
+ privbuf[0] &= info->bitmask;
+ if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL)
goto err;
if (counter == 0xFF) {
ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
@@ -499,7 +470,7 @@ static EC_KEY *derivekey(PROV_EC_CTX *ctx,
/* Generate a random seed if there is no input ikm */
if (seed == NULL || seedlen == 0) {
- seedlen = ctx->alg->encodedprivlen;
+ seedlen = ctx->info->Nsk;
if (seedlen > sizeof(tmpbuf))
goto err;
if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
@@ -599,8 +570,9 @@ static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
size_t sender_authpublen;
size_t kemctxlen = 0, dhkmlen = 0;
- size_t encodedpublen = ctx->alg->encodedpublen;
- size_t encodedprivlen = ctx->alg->encodedprivlen;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+ size_t encodedpublen = info->Npk;
+ size_t encodedprivlen = info->Nsk;
int auth = ctx->sender_authkey != NULL;
if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))
@@ -630,17 +602,16 @@ static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
goto err;
/* kemctx is the concat of both sides encoded public key */
- memcpy(kemctx, sender_pub, ctx->alg->encodedpublen);
- memcpy(kemctx + ctx->alg->encodedpublen, recipient_pub,
- ctx->alg->encodedpublen);
+ memcpy(kemctx, sender_pub, info->Npk);
+ memcpy(kemctx + info->Npk, recipient_pub, info->Npk);
if (auth)
memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
- kdfctx = ossl_kdf_ctx_create(ctx->kdfname, ctx->alg->kdfdigestname,
+ kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
ctx->libctx, ctx->propq);
if (kdfctx == NULL)
goto err;
- if (!dhkem_extract_and_expand(kdfctx, secret, ctx->alg->secretlen,
- ctx->alg->kemid, dhkm, dhkmlen,
+ if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
+ info->kem_id, dhkm, dhkmlen,
kemctx, kemctxlen))
goto err;
ret = 1;
@@ -677,22 +648,23 @@ static int dhkem_encap(PROV_EC_CTX *ctx,
unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
size_t sender_publen, recipient_publen;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
if (enc == NULL) {
if (enclen == NULL && secretlen == NULL)
return 0;
if (enclen != NULL)
- *enclen = ctx->alg->encodedpublen;
+ *enclen = info->Nenc;
if (secretlen != NULL)
- *secretlen = ctx->alg->secretlen;
+ *secretlen = info->Nsecret;
return 1;
}
- if (*secretlen < ctx->alg->secretlen) {
+ if (*secretlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
return 0;
}
- if (*enclen < ctx->alg->encodedpublen) {
+ if (*enclen < info->Nenc) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
return 0;
}
@@ -707,7 +679,7 @@ static int dhkem_encap(PROV_EC_CTX *ctx,
&recipient_publen, sizeof(recipient_pub)))
goto err;
- if (sender_publen != ctx->alg->encodedpublen
+ if (sender_publen != info->Npk
|| recipient_publen != sender_publen) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
goto err;
@@ -722,7 +694,7 @@ static int dhkem_encap(PROV_EC_CTX *ctx,
/* Return the senders ephemeral public key in encoded form */
memcpy(enc, sender_pub, sender_publen);
*enclen = sender_publen;
- *secretlen = ctx->alg->secretlen;
+ *secretlen = info->Nsecret;
ret = 1;
err:
EC_KEY_free(sender_ephemkey);
@@ -751,16 +723,17 @@ static int dhkem_decap(PROV_EC_CTX *ctx,
{
int ret = 0;
EC_KEY *sender_ephempubkey = NULL;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
size_t recipient_publen;
- size_t encodedpublen = ctx->alg->encodedpublen;
+ size_t encodedpublen = info->Npk;
if (secret == NULL) {
- *secretlen = ctx->alg->secretlen;
+ *secretlen = info->Nsecret;
return 1;
}
- if (*secretlen < ctx->alg->secretlen) {
+ if (*secretlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
return 0;
}
@@ -785,7 +758,7 @@ static int dhkem_decap(PROV_EC_CTX *ctx,
ctx->recipient_key, ctx->sender_authkey,
enc, recipient_pub))
goto err;
- *secretlen = ctx->alg->secretlen;
+ *secretlen = info->Nsecret;
ret = 1;
err:
EC_KEY_free(sender_ephempubkey);