Adding in curl and openssl repos

This commit is contained in:
2025-08-14 12:09:30 -04:00
parent af2117b574
commit 0ace93e303
21174 changed files with 3607720 additions and 2 deletions

View File

@@ -0,0 +1,14 @@
# We make separate GOAL variables for each algorithm, to make it easy to
# switch each to the Legacy provider when needed.
$RSA_KEM_GOAL=../../libdefault.a ../../libfips.a
$EC_KEM_GOAL=../../libdefault.a
SOURCE[$RSA_KEM_GOAL]=rsa_kem.c
IF[{- !$disabled{ec} -}]
SOURCE[$EC_KEM_GOAL]=kem_util.c ec_kem.c
IF[{- !$disabled{ecx} -}]
SOURCE[$EC_KEM_GOAL]=ecx_kem.c
ENDIF
ENDIF

View File

@@ -0,0 +1,814 @@
/*
* 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
*/
/*
* The following implementation is part of RFC 9180 related to DHKEM using
* EC keys (i.e. P-256, P-384 and P-521)
* References to Sections in the comments below refer to RFC 9180.
*/
#include "internal/deprecated.h"
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/ec.h>
#include <openssl/params.h>
#include <openssl/err.h>
#include <openssl/proverr.h>
#include <openssl/kdf.h>
#include <openssl/rand.h>
#include "prov/provider_ctx.h"
#include "prov/implementations.h"
#include "prov/securitycheck.h"
#include "prov/providercommon.h"
#include <openssl/hpke.h>
#include "internal/hpke_util.h"
#include "crypto/ec.h"
#include "prov/ecx.h"
#include "eckem.h"
typedef struct {
EC_KEY *recipient_key;
EC_KEY *sender_authkey;
OSSL_LIB_CTX *libctx;
char *propq;
unsigned int mode;
unsigned int op;
unsigned char *ikm;
size_t ikmlen;
const char *kdfname;
const OSSL_HPKE_KEM_INFO *info;
} PROV_EC_CTX;
static OSSL_FUNC_kem_newctx_fn eckem_newctx;
static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init;
static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init;
static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate;
static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init;
static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init;
static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate;
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;
/* 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)
{
int rv = 0;
BN_CTX *bnctx = NULL;
BIGNUM *rem = NULL;
const BIGNUM *priv = EC_KEY_get0_private_key(ec);
const EC_POINT *pub = EC_KEY_get0_public_key(ec);
/* Keys always require a public component */
if (pub == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
return 0;
}
if (priv == NULL) {
return (requires_privatekey == 0);
} else {
/* If there is a private key, check that is non zero (mod order) */
const EC_GROUP *group = EC_KEY_get0_group(ec);
const BIGNUM *order = EC_GROUP_get0_order(group);
bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
rem = BN_new();
if (order != NULL && rem != NULL && bnctx != NULL) {
rv = BN_mod(rem, priv, order, bnctx)
&& !BN_is_zero(rem);
}
}
BN_free(rem);
BN_CTX_free(bnctx);
return rv;
}
/* Returns NULL if the curve is not supported */
static const char *ec_curvename_get0(const EC_KEY *ec)
{
const EC_GROUP *group = EC_KEY_get0_group(ec);
return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
}
/*
* Set the recipient key, and free any existing key.
* ec can be NULL.
* The ec key may have only a private or public component
* (but it must have a group).
*/
static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
{
EC_KEY_free(ctx->recipient_key);
ctx->recipient_key = NULL;
if (ec != NULL) {
const char *curve = ec_curvename_get0(ec);
if (curve == NULL)
return -2;
ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);
if (ctx->info == NULL)
return -2;
if (!EC_KEY_up_ref(ec))
return 0;
ctx->recipient_key = ec;
ctx->kdfname = "HKDF";
}
return 1;
}
/*
* Set the senders auth key, and free any existing auth key.
* ec can be NULL.
*/
static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)
{
EC_KEY_free(ctx->sender_authkey);
ctx->sender_authkey = NULL;
if (ec != NULL) {
if (!EC_KEY_up_ref(ec))
return 0;
ctx->sender_authkey = ec;
}
return 1;
}
/*
* Serializes a encoded public key buffer into a EC public key.
* Params:
* in Contains the group.
* pubbuf The encoded public key buffer
* Returns: The created public EC key, or NULL if there is an error.
*/
static EC_KEY *eckey_frompub(EC_KEY *in,
const unsigned char *pubbuf, size_t pubbuflen)
{
EC_KEY *key;
key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));
if (key == NULL)
goto err;
if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))
goto err;
if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))
goto err;
return key;
err:
EC_KEY_free(key);
return NULL;
}
/*
* Deserialises a EC public key into a encoded byte array.
* Returns: 1 if successful or 0 otherwise.
*/
static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,
size_t maxoutlen)
{
const EC_POINT *pub;
const EC_GROUP *group;
group = EC_KEY_get0_group(ec);
pub = EC_KEY_get0_public_key(ec);
*outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,
out, maxoutlen, NULL);
return *outlen != 0;
}
static void *eckem_newctx(void *provctx)
{
PROV_EC_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_EC_CTX));
if (ctx == NULL)
return NULL;
ctx->libctx = PROV_LIBCTX_OF(provctx);
return ctx;
}
static void eckem_freectx(void *vectx)
{
PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
recipient_key_set(ctx, NULL);
sender_authkey_set(ctx, NULL);
OPENSSL_free(ctx);
}
static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
{
int ret;
BN_CTX *ctx = NULL;
const EC_GROUP *group1 = EC_KEY_get0_group(key1);
const EC_GROUP *group2 = EC_KEY_get0_group(key2);
ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
if (ctx == NULL)
return 0;
ret = group1 != NULL
&& group2 != NULL
&& EC_GROUP_cmp(group1, group2, ctx) == 0;
if (!ret)
ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
BN_CTX_free(ctx);
return ret;
}
static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
const OSSL_PARAM params[])
{
int rv;
PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
EC_KEY *ec = vec;
EC_KEY *auth = vauth;
if (!ossl_prov_is_running())
return 0;
if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
return 0;
rv = recipient_key_set(ctx, ec);
if (rv <= 0)
return rv;
if (auth != NULL) {
if (!ossl_ec_match_params(ec, auth)
|| !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
|| !sender_authkey_set(ctx, auth))
return 0;
}
ctx->op = operation;
return eckem_set_ctx_params(vctx, params);
}
static int eckem_encapsulate_init(void *vctx, void *vec,
const OSSL_PARAM params[])
{
return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
}
static int eckem_decapsulate_init(void *vctx, void *vec,
const OSSL_PARAM params[])
{
return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
}
static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
const OSSL_PARAM params[])
{
return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
}
static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
const OSSL_PARAM params[])
{
return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
}
static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
{
PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
const OSSL_PARAM *p;
int mode;
if (params == NULL)
return 1;
p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
if (p != NULL) {
void *tmp = NULL;
size_t tmplen = 0;
if (p->data != NULL && p->data_size != 0) {
if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
return 0;
}
OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
/* Set the ephemeral seed */
ctx->ikm = tmp;
ctx->ikmlen = tmplen;
}
p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
if (p != NULL) {
if (p->data_type != OSSL_PARAM_UTF8_STRING)
return 0;
mode = ossl_eckem_modename2id(p->data);
if (mode == KEM_MODE_UNDEFINED)
return 0;
ctx->mode = mode;
}
return 1;
}
static const OSSL_PARAM known_settable_eckem_ctx_params[] = {
OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
OSSL_PARAM_END
};
static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
ossl_unused void *provctx)
{
return known_settable_eckem_ctx_params;
}
/*
* See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
*/
static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
unsigned char *okm, size_t okmlen,
uint16_t kemid,
const unsigned char *dhkm, size_t dhkmlen,
const unsigned char *kemctx,
size_t kemctxlen)
{
uint8_t suiteid[2];
uint8_t prk[EVP_MAX_MD_SIZE];
size_t prklen = okmlen;
int ret;
if (prklen > sizeof(prk))
return 0;
suiteid[0] = (kemid >> 8) & 0xff;
suiteid[1] = kemid & 0xff;
ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
&& ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_SHARED_SECRET,
kemctx, kemctxlen);
OPENSSL_cleanse(prk, prklen);
return ret;
}
/*
* See Section 7.1.3 DeriveKeyPair.
*
* This function is used by ec keygen.
* (For this reason it does not use any of the state stored in PROV_EC_CTX).
*
* Params:
* ec An initialized ec key.
* priv The buffer to store the generated private key into (it is assumed
* this is of length alg->encodedprivlen).
* ikm buffer containing the input key material (seed). This must be set.
* ikmlen size of the ikm buffer in bytes
* Returns:
* 1 if successful or 0 otherwise.
*/
int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
const unsigned char *ikm, size_t ikmlen)
{
int ret = 0;
EVP_KDF_CTX *kdfctx = NULL;
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 char *curve = ec_curvename_get0(ec);
const OSSL_HPKE_KEM_INFO *info;
if (curve == NULL)
return -2;
info = ossl_HPKE_KEM_INFO_find_curve(curve);
if (info == NULL)
return -2;
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 < info->Nsk) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
"ikm length is :%zu, should be at least %zu",
ikmlen, info->Nsk);
goto err;
}
suiteid[0] = info->kem_id / 256;
suiteid[1] = info->kem_id % 256;
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, info->Nsk,
prk, info->Nsecret,
LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_CANDIDATE,
&counter, 1))
goto err;
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);
goto err;
}
counter++;
} while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
ret = 1;
err:
OPENSSL_cleanse(prk, sizeof(prk));
OPENSSL_cleanse(privbuf, sizeof(privbuf));
EVP_KDF_CTX_free(kdfctx);
return ret;
}
/*
* Do a keygen operation without having to use EVP_PKEY.
* Params:
* ctx Context object
* ikm The seed material - if this is NULL, then a random seed is used.
* Returns:
* The generated EC key, or NULL on failure.
*/
static EC_KEY *derivekey(PROV_EC_CTX *ctx,
const unsigned char *ikm, size_t ikmlen)
{
int ret = 0;
EC_KEY *key;
unsigned char *seed = (unsigned char *)ikm;
size_t seedlen = ikmlen;
unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
if (key == NULL)
goto err;
if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
goto err;
/* Generate a random seed if there is no input ikm */
if (seed == NULL || seedlen == 0) {
seedlen = ctx->info->Nsk;
if (seedlen > sizeof(tmpbuf))
goto err;
if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
goto err;
seed = tmpbuf;
}
ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
err:
if (seed != ikm)
OPENSSL_cleanse(seed, seedlen);
if (ret <= 0) {
EC_KEY_free(key);
key = NULL;
}
return key;
}
/*
* Before doing a key exchange the public key of the peer needs to be checked
* Note that the group check is not done here as we have already checked
* that it only uses one of the approved curve names when the key was set.
*
* Returns 1 if the public key is valid, or 0 if it fails.
*/
static int check_publickey(const EC_KEY *pub)
{
int ret = 0;
BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
if (bnctx == NULL)
return 0;
ret = ossl_ec_key_public_check(pub, bnctx);
BN_CTX_free(bnctx);
return ret;
}
/*
* Do an ecdh key exchange.
* dhkm = DH(sender, peer)
*
* NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations
* to avoid messy conversions back to EVP_PKEY.
*
* Returns the size of the secret if successful, or 0 otherwise,
*/
static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,
unsigned char *out, size_t maxout,
unsigned int secretsz)
{
const EC_GROUP *group = EC_KEY_get0_group(sender);
size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
if (secretlen != secretsz || secretlen > maxout) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "secretsz invalid");
return 0;
}
if (!check_publickey(peer))
return 0;
return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
sender, NULL) > 0;
}
/*
* Derive a secret using ECDH (code is shared by the encap and decap)
*
* dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
* kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
* secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
*
* Params:
* ctx Object that contains algorithm state and constants.
* secret The returned secret (with a length ctx->alg->secretlen bytes).
* privkey1 A private key used for ECDH key derivation.
* peerkey1 A public key used for ECDH key derivation with privkey1
* privkey2 A optional private key used for a second ECDH key derivation.
* It can be NULL.
* peerkey2 A optional public key used for a second ECDH key derivation
* with privkey2,. It can be NULL.
* sender_pub The senders public key in encoded form.
* recipient_pub The recipients public key in encoded form.
* Notes:
* The second ecdh() is only used for the HPKE auth modes when both privkey2
* and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
*/
static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
const EC_KEY *privkey1, const EC_KEY *peerkey1,
const EC_KEY *privkey2, const EC_KEY *peerkey2,
const unsigned char *sender_pub,
const unsigned char *recipient_pub)
{
int ret = 0;
EVP_KDF_CTX *kdfctx = NULL;
unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
size_t sender_authpublen;
size_t kemctxlen = 0, dhkmlen = 0;
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))
goto err;
dhkmlen = encodedprivlen;
kemctxlen = 2 * encodedpublen;
/* Concat the optional second ECDH (used for Auth) */
if (auth) {
/* Get the public key of the auth sender in encoded form */
if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
&sender_authpublen, sizeof(sender_authpub)))
goto err;
if (sender_authpublen != encodedpublen) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
"Invalid sender auth public key");
goto err;
}
if (!generate_ecdhkm(privkey2, peerkey2,
dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
encodedprivlen))
goto err;
dhkmlen += encodedprivlen;
kemctxlen += encodedpublen;
}
if (kemctxlen > sizeof(kemctx))
goto err;
/* kemctx is the concat of both sides encoded public key */
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, info->mdname,
ctx->libctx, ctx->propq);
if (kdfctx == NULL)
goto err;
if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
info->kem_id, dhkm, dhkmlen,
kemctx, kemctxlen))
goto err;
ret = 1;
err:
OPENSSL_cleanse(dhkm, dhkmlen);
EVP_KDF_CTX_free(kdfctx);
return ret;
}
/*
* Do a DHKEM encapsulate operation.
*
* See Section 4.1 Encap() and AuthEncap()
*
* Params:
* ctx A context object holding the recipients public key and the
* optional senders auth private key.
* enc A buffer to return the senders ephemeral public key.
* Setting this to NULL allows the enclen and secretlen to return
* values, without calculating the secret.
* enclen Passes in the max size of the enc buffer and returns the
* encoded public key length.
* secret A buffer to return the calculated shared secret.
* secretlen Passes in the max size of the secret buffer and returns the
* secret length.
* Returns: 1 on success or 0 otherwise.
*/
static int dhkem_encap(PROV_EC_CTX *ctx,
unsigned char *enc, size_t *enclen,
unsigned char *secret, size_t *secretlen)
{
int ret = 0;
EC_KEY *sender_ephemkey = NULL;
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 = info->Nenc;
if (secretlen != NULL)
*secretlen = info->Nsecret;
return 1;
}
if (*secretlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
return 0;
}
if (*enclen < info->Nenc) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
return 0;
}
/* Create an ephemeral key */
sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
if (sender_ephemkey == NULL)
goto err;
if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
sizeof(sender_pub))
|| !ecpubkey_todata(ctx->recipient_key, recipient_pub,
&recipient_publen, sizeof(recipient_pub)))
goto err;
if (sender_publen != info->Npk
|| recipient_publen != sender_publen) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
goto err;
}
if (!derive_secret(ctx, secret,
sender_ephemkey, ctx->recipient_key,
ctx->sender_authkey, ctx->recipient_key,
sender_pub, recipient_pub))
goto err;
/* Return the senders ephemeral public key in encoded form */
memcpy(enc, sender_pub, sender_publen);
*enclen = sender_publen;
*secretlen = info->Nsecret;
ret = 1;
err:
EC_KEY_free(sender_ephemkey);
return ret;
}
/*
* Do a DHKEM decapsulate operation.
* See Section 4.1 Decap() and Auth Decap()
*
* Params:
* ctx A context object holding the recipients private key and the
* optional senders auth public key.
* secret A buffer to return the calculated shared secret. Setting this to
* NULL can be used to return the secretlen.
* secretlen Passes in the max size of the secret buffer and returns the
* secret length.
* enc A buffer containing the senders ephemeral public key that was returned
* from dhkem_encap().
* enclen The length in bytes of enc.
* Returns: 1 If the shared secret is returned or 0 on error.
*/
static int dhkem_decap(PROV_EC_CTX *ctx,
unsigned char *secret, size_t *secretlen,
const unsigned char *enc, size_t enclen)
{
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 = info->Npk;
if (secret == NULL) {
*secretlen = info->Nsecret;
return 1;
}
if (*secretlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
return 0;
}
if (enclen != encodedpublen) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
return 0;
}
sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
if (sender_ephempubkey == NULL)
goto err;
if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
sizeof(recipient_pub)))
goto err;
if (recipient_publen != encodedpublen) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
goto err;
}
if (!derive_secret(ctx, secret,
ctx->recipient_key, sender_ephempubkey,
ctx->recipient_key, ctx->sender_authkey,
enc, recipient_pub))
goto err;
*secretlen = info->Nsecret;
ret = 1;
err:
EC_KEY_free(sender_ephempubkey);
return ret;
}
static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
unsigned char *secret, size_t *secretlen)
{
PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
switch (ctx->mode) {
case KEM_MODE_DHKEM:
return dhkem_encap(ctx, out, outlen, secret, secretlen);
default:
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
return -2;
}
}
static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen)
{
PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
switch (ctx->mode) {
case KEM_MODE_DHKEM:
return dhkem_decap(ctx, out, outlen, in, inlen);
default:
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
return -2;
}
}
const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = {
{ OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx },
{ OSSL_FUNC_KEM_ENCAPSULATE_INIT,
(void (*)(void))eckem_encapsulate_init },
{ OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate },
{ OSSL_FUNC_KEM_DECAPSULATE_INIT,
(void (*)(void))eckem_decapsulate_init },
{ OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate },
{ OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx },
{ OSSL_FUNC_KEM_SET_CTX_PARAMS,
(void (*)(void))eckem_set_ctx_params },
{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
(void (*)(void))eckem_settable_ctx_params },
{ OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
(void (*)(void))eckem_auth_encapsulate_init },
{ OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
(void (*)(void))eckem_auth_decapsulate_init },
OSSL_DISPATCH_END
};

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2022 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
*/
#define KEM_MODE_UNDEFINED 0
#define KEM_MODE_DHKEM 1
int ossl_eckem_modename2id(const char *name);

View File

@@ -0,0 +1,704 @@
/*
* 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
*/
/*
* The following implementation is part of RFC 9180 related to DHKEM using
* ECX keys (i.e. X25519 and X448)
* References to Sections in the comments below refer to RFC 9180.
*/
#include "internal/deprecated.h"
#include <string.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/params.h>
#include <openssl/kdf.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/proverr.h>
#include "prov/provider_ctx.h"
#include "prov/implementations.h"
#include "prov/securitycheck.h"
#include "prov/providercommon.h"
#include "prov/ecx.h"
#include "crypto/ecx.h"
#include <openssl/hpke.h>
#include "internal/hpke_util.h"
#include "eckem.h"
#define MAX_ECX_KEYLEN X448_KEYLEN
/* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */
#define KEMID_X25519_HKDF_SHA256 0x20
#define KEMID_X448_HKDF_SHA512 0x21
/* ASCII: "KEM", in hex for EBCDIC compatibility */
static const char LABEL_KEM[] = "\x4b\x45\x4d";
typedef struct {
ECX_KEY *recipient_key;
ECX_KEY *sender_authkey;
OSSL_LIB_CTX *libctx;
char *propq;
unsigned int mode;
unsigned int op;
unsigned char *ikm;
size_t ikmlen;
const char *kdfname;
const OSSL_HPKE_KEM_INFO *info;
} PROV_ECX_CTX;
static OSSL_FUNC_kem_newctx_fn ecxkem_newctx;
static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init;
static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate;
static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init;
static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate;
static OSSL_FUNC_kem_freectx_fn ecxkem_freectx;
static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params;
static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init;
static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init;
/*
* Set KEM values as specified in Section 7.1 "Table 2 KEM IDs"
* There is only one set of values for X25519 and X448.
* Additional values could be set via set_params if required.
*/
static const OSSL_HPKE_KEM_INFO *get_kem_info(ECX_KEY *ecx)
{
const char *name = NULL;
if (ecx->type == ECX_KEY_TYPE_X25519)
name = SN_X25519;
else
name = SN_X448;
return ossl_HPKE_KEM_INFO_find_curve(name);
}
/*
* Set the recipient key, and free any existing key.
* ecx can be NULL. The ecx key may have only a private or public component.
*/
static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
{
ossl_ecx_key_free(ctx->recipient_key);
ctx->recipient_key = NULL;
if (ecx != NULL) {
ctx->info = get_kem_info(ecx);
if (ctx->info == NULL)
return -2;
ctx->kdfname = "HKDF";
if (!ossl_ecx_key_up_ref(ecx))
return 0;
ctx->recipient_key = ecx;
}
return 1;
}
/*
* Set the senders auth key, and free any existing auth key.
* ecx can be NULL.
*/
static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
{
ossl_ecx_key_free(ctx->sender_authkey);
ctx->sender_authkey = NULL;
if (ecx != NULL) {
if (!ossl_ecx_key_up_ref(ecx))
return 0;
ctx->sender_authkey = ecx;
}
return 1;
}
/*
* Serialize a public key from byte array's for the encoded public keys.
* ctx is used to access the key type.
* Returns: The created ECX_KEY or NULL on error.
*/
static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx,
const unsigned char *pubbuf, size_t pubbuflen)
{
ECX_KEY *ecx = NULL;
OSSL_PARAM params[2], *p = params;
*p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
(char *)pubbuf, pubbuflen);
*p = OSSL_PARAM_construct_end();
ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq);
if (ecx == NULL)
return NULL;
if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) {
ossl_ecx_key_free(ecx);
ecx = NULL;
}
return ecx;
}
static unsigned char *ecx_pubkey(ECX_KEY *ecx)
{
if (ecx == NULL || !ecx->haspubkey) {
ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
return 0;
}
return ecx->pubkey;
}
static void *ecxkem_newctx(void *provctx)
{
PROV_ECX_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX));
if (ctx == NULL)
return NULL;
ctx->libctx = PROV_LIBCTX_OF(provctx);
return ctx;
}
static void ecxkem_freectx(void *vectx)
{
PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx;
OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
recipient_key_set(ctx, NULL);
sender_authkey_set(ctx, NULL);
OPENSSL_free(ctx);
}
static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2)
{
return (key1->type == key2->type && key1->keylen == key2->keylen);
}
static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey)
{
if (ecx->privkey == NULL)
return (requires_privatekey == 0);
return 1;
}
static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth,
ossl_unused const OSSL_PARAM params[])
{
int rv;
PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx;
ECX_KEY *ecx = vecx;
ECX_KEY *auth = vauth;
if (!ossl_prov_is_running())
return 0;
if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE))
return 0;
rv = recipient_key_set(ctx, ecx);
if (rv <= 0)
return rv;
if (auth != NULL) {
if (!ecx_match_params(auth, ctx->recipient_key)
|| !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
|| !sender_authkey_set(ctx, auth))
return 0;
}
ctx->op = operation;
return ecxkem_set_ctx_params(vecxctx, params);
}
static int ecxkem_encapsulate_init(void *vecxctx, void *vecx,
const OSSL_PARAM params[])
{
return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params);
}
static int ecxkem_decapsulate_init(void *vecxctx, void *vecx,
const OSSL_PARAM params[])
{
return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params);
}
static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
const OSSL_PARAM params[])
{
return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
}
static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
const OSSL_PARAM params[])
{
return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
}
static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
{
PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
const OSSL_PARAM *p;
int mode;
if (ctx == NULL)
return 0;
if (params == NULL)
return 1;
p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
if (p != NULL) {
void *tmp = NULL;
size_t tmplen = 0;
if (p->data != NULL && p->data_size != 0) {
if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
return 0;
}
OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
ctx->ikm = tmp;
ctx->ikmlen = tmplen;
}
p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
if (p != NULL) {
if (p->data_type != OSSL_PARAM_UTF8_STRING)
return 0;
mode = ossl_eckem_modename2id(p->data);
if (mode == KEM_MODE_UNDEFINED)
return 0;
ctx->mode = mode;
}
return 1;
}
static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = {
OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
OSSL_PARAM_END
};
static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx,
ossl_unused void *provctx)
{
return known_settable_ecxkem_ctx_params;
}
/*
* See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
*/
static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
unsigned char *okm, size_t okmlen,
uint16_t kemid,
const unsigned char *dhkm, size_t dhkmlen,
const unsigned char *kemctx,
size_t kemctxlen)
{
uint8_t suiteid[2];
uint8_t prk[EVP_MAX_MD_SIZE];
size_t prklen = okmlen; /* Nh */
int ret;
if (prklen > sizeof(prk))
return 0;
suiteid[0] = (kemid >> 8) &0xff;
suiteid[1] = kemid & 0xff;
ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
&& ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_SHARED_SECRET,
kemctx, kemctxlen);
OPENSSL_cleanse(prk, prklen);
return ret;
}
/*
* See Section 7.1.3 DeriveKeyPair.
*
* This function is used by ecx keygen.
* (For this reason it does not use any of the state stored in PROV_ECX_CTX).
*
* Params:
* ecx An initialized ecx key.
* privout The buffer to store the generated private key into (it is assumed
* this is of length ecx->keylen).
* ikm buffer containing the input key material (seed). This must be non NULL.
* ikmlen size of the ikm buffer in bytes
* Returns:
* 1 if successful or 0 otherwise.
*/
int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,
const unsigned char *ikm, size_t ikmlen)
{
int ret = 0;
EVP_KDF_CTX *kdfctx = NULL;
unsigned char prk[EVP_MAX_MD_SIZE];
uint8_t suiteid[2];
const OSSL_HPKE_KEM_INFO *info = get_kem_info(ecx);
/* ikmlen should have a length of at least Nsk */
if (ikmlen < info->Nsk) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
"ikm length is :%zu, should be at least %zu",
ikmlen, info->Nsk);
goto err;
}
kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, ecx->libctx, ecx->propq);
if (kdfctx == NULL)
return 0;
suiteid[0] = info->kem_id / 256;
suiteid[1] = info->kem_id % 256;
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;
if (!ossl_hpke_labeled_expand(kdfctx, privout, info->Nsk, prk, info->Nsecret,
LABEL_KEM, suiteid, sizeof(suiteid),
OSSL_DHKEM_LABEL_SK, NULL, 0))
goto err;
ret = 1;
err:
OPENSSL_cleanse(prk, sizeof(prk));
EVP_KDF_CTX_free(kdfctx);
return ret;
}
/*
* Do a keygen operation without having to use EVP_PKEY.
* Params:
* ctx Context object
* ikm The seed material - if this is NULL, then a random seed is used.
* Returns:
* The generated ECX key, or NULL on failure.
*/
static ECX_KEY *derivekey(PROV_ECX_CTX *ctx,
const unsigned char *ikm, size_t ikmlen)
{
int ok = 0;
ECX_KEY *key;
unsigned char *privkey;
unsigned char *seed = (unsigned char *)ikm;
size_t seedlen = ikmlen;
unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
const OSSL_HPKE_KEM_INFO *info = ctx->info;
key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq);
if (key == NULL)
return NULL;
privkey = ossl_ecx_key_allocate_privkey(key);
if (privkey == NULL)
goto err;
/* Generate a random seed if there is no input ikm */
if (seed == NULL || seedlen == 0) {
if (info->Nsk > sizeof(tmpbuf))
goto err;
if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, info->Nsk, 0) <= 0)
goto err;
seed = tmpbuf;
seedlen = info->Nsk;
}
if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen))
goto err;
if (!ossl_ecx_public_from_private(key))
goto err;
key->haspubkey = 1;
ok = 1;
err:
if (!ok) {
ossl_ecx_key_free(key);
key = NULL;
}
if (seed != ikm)
OPENSSL_cleanse(seed, seedlen);
return key;
}
/*
* Do an ecxdh key exchange.
* dhkm = DH(sender, peer)
*
* NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations
* to avoid messy conversions back to EVP_PKEY.
*
* Returns the size of the secret if successful, or 0 otherwise,
*/
static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer,
unsigned char *out, size_t maxout,
unsigned int secretsz)
{
size_t len = 0;
/* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */
return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender,
sender->keylen, out, &len, maxout);
}
/*
* Derive a secret using ECXDH (code is shared by the encap and decap)
*
* dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
* kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
* secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
*
* Params:
* ctx Object that contains algorithm state and constants.
* secret The returned secret (with a length ctx->alg->secretlen bytes).
* privkey1 A private key used for ECXDH key derivation.
* peerkey1 A public key used for ECXDH key derivation with privkey1
* privkey2 A optional private key used for a second ECXDH key derivation.
* It can be NULL.
* peerkey2 A optional public key used for a second ECXDH key derivation
* with privkey2,. It can be NULL.
* sender_pub The senders public key in encoded form.
* recipient_pub The recipients public key in encoded form.
* Notes:
* The second ecdh() is only used for the HPKE auth modes when both privkey2
* and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
*/
static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret,
const ECX_KEY *privkey1, const ECX_KEY *peerkey1,
const ECX_KEY *privkey2, const ECX_KEY *peerkey2,
const unsigned char *sender_pub,
const unsigned char *recipient_pub)
{
int ret = 0;
EVP_KDF_CTX *kdfctx = NULL;
unsigned char *sender_authpub = NULL;
unsigned char dhkm[MAX_ECX_KEYLEN * 2];
unsigned char kemctx[MAX_ECX_KEYLEN * 3];
size_t kemctxlen = 0, dhkmlen = 0;
const OSSL_HPKE_KEM_INFO *info = ctx->info;
int auth = ctx->sender_authkey != NULL;
size_t encodedkeylen = info->Npk;
if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen))
goto err;
dhkmlen = encodedkeylen;
/* Concat the optional second ECXDH (used for Auth) */
if (auth) {
if (!generate_ecxdhkm(privkey2, peerkey2,
dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
encodedkeylen))
goto err;
/* Get the public key of the auth sender in encoded form */
sender_authpub = ecx_pubkey(ctx->sender_authkey);
if (sender_authpub == NULL)
goto err;
dhkmlen += encodedkeylen;
}
kemctxlen = encodedkeylen + dhkmlen;
if (kemctxlen > sizeof(kemctx))
goto err;
/* kemctx is the concat of both sides encoded public key */
memcpy(kemctx, sender_pub, encodedkeylen);
memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen);
if (auth)
memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen);
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, info->Nsecret,
info->kem_id, dhkm, dhkmlen,
kemctx, kemctxlen))
goto err;
ret = 1;
err:
OPENSSL_cleanse(dhkm, dhkmlen);
EVP_KDF_CTX_free(kdfctx);
return ret;
}
/*
* Do a DHKEM encapsulate operation.
*
* See Section 4.1 Encap() and AuthEncap()
*
* Params:
* ctx A context object holding the recipients public key and the
* optional senders auth private key.
* enc A buffer to return the senders ephemeral public key.
* Setting this to NULL allows the enclen and secretlen to return
* values, without calculating the secret.
* enclen Passes in the max size of the enc buffer and returns the
* encoded public key length.
* secret A buffer to return the calculated shared secret.
* secretlen Passes in the max size of the secret buffer and returns the
* secret length.
* Returns: 1 on success or 0 otherwise.
*/
static int dhkem_encap(PROV_ECX_CTX *ctx,
unsigned char *enc, size_t *enclen,
unsigned char *secret, size_t *secretlen)
{
int ret = 0;
ECX_KEY *sender_ephemkey = NULL;
unsigned char *sender_ephempub, *recipient_pub;
const OSSL_HPKE_KEM_INFO *info = ctx->info;
if (enc == NULL) {
if (enclen == NULL && secretlen == NULL)
return 0;
if (enclen != NULL)
*enclen = info->Nenc;
if (secretlen != NULL)
*secretlen = info->Nsecret;
return 1;
}
if (*secretlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
return 0;
}
if (*enclen < info->Nenc) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
return 0;
}
/* Create an ephemeral key */
sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
sender_ephempub = ecx_pubkey(sender_ephemkey);
recipient_pub = ecx_pubkey(ctx->recipient_key);
if (sender_ephempub == NULL || recipient_pub == NULL)
goto err;
if (!derive_secret(ctx, secret,
sender_ephemkey, ctx->recipient_key,
ctx->sender_authkey, ctx->recipient_key,
sender_ephempub, recipient_pub))
goto err;
/* Return the public part of the ephemeral key */
memcpy(enc, sender_ephempub, info->Nenc);
*enclen = info->Nenc;
*secretlen = info->Nsecret;
ret = 1;
err:
ossl_ecx_key_free(sender_ephemkey);
return ret;
}
/*
* Do a DHKEM decapsulate operation.
* See Section 4.1 Decap() and Auth Decap()
*
* Params:
* ctx A context object holding the recipients private key and the
* optional senders auth public key.
* secret A buffer to return the calculated shared secret. Setting this to
* NULL can be used to return the secretlen.
* secretlen Passes in the max size of the secret buffer and returns the
* secret length.
* enc A buffer containing the senders ephemeral public key that was returned
* from dhkem_encap().
* enclen The length in bytes of enc.
* Returns: 1 If the shared secret is returned or 0 on error.
*/
static int dhkem_decap(PROV_ECX_CTX *ctx,
unsigned char *secret, size_t *secretlen,
const unsigned char *enc, size_t enclen)
{
int ret = 0;
ECX_KEY *recipient_privkey = ctx->recipient_key;
ECX_KEY *sender_ephempubkey = NULL;
const OSSL_HPKE_KEM_INFO *info = ctx->info;
unsigned char *recipient_pub;
if (secret == NULL) {
*secretlen = info->Nsecret;
return 1;
}
if (*secretlen < info->Nsecret) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
return 0;
}
if (enclen != info->Nenc) {
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
return 0;
}
/* Get the public part of the ephemeral key created by encap */
sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen);
if (sender_ephempubkey == NULL)
goto err;
recipient_pub = ecx_pubkey(recipient_privkey);
if (recipient_pub == NULL)
goto err;
if (!derive_secret(ctx, secret,
ctx->recipient_key, sender_ephempubkey,
ctx->recipient_key, ctx->sender_authkey,
enc, recipient_pub))
goto err;
*secretlen = info->Nsecret;
ret = 1;
err:
ossl_ecx_key_free(sender_ephempubkey);
return ret;
}
static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
unsigned char *secret, size_t *secretlen)
{
PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
switch (ctx->mode) {
case KEM_MODE_DHKEM:
return dhkem_encap(ctx, out, outlen, secret, secretlen);
default:
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
return -2;
}
}
static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen)
{
PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
switch (ctx->mode) {
case KEM_MODE_DHKEM:
return dhkem_decap(vctx, out, outlen, in, inlen);
default:
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
return -2;
}
}
const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = {
{ OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx },
{ OSSL_FUNC_KEM_ENCAPSULATE_INIT,
(void (*)(void))ecxkem_encapsulate_init },
{ OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate },
{ OSSL_FUNC_KEM_DECAPSULATE_INIT,
(void (*)(void))ecxkem_decapsulate_init },
{ OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate },
{ OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx },
{ OSSL_FUNC_KEM_SET_CTX_PARAMS,
(void (*)(void))ecxkem_set_ctx_params },
{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
(void (*)(void))ecxkem_settable_ctx_params },
{ OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
(void (*)(void))ecxkem_auth_encapsulate_init },
{ OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
(void (*)(void))ecxkem_auth_decapsulate_init },
OSSL_DISPATCH_END
};

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2022 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> /* for memcpy() */
#include <openssl/core_names.h>
#include <openssl/crypto.h>
#include "eckem.h"
typedef struct {
unsigned int id;
const char *mode;
} KEM_MODE;
static const KEM_MODE eckem_modename_id_map[] = {
{ KEM_MODE_DHKEM, OSSL_KEM_PARAM_OPERATION_DHKEM },
{ 0, NULL }
};
int ossl_eckem_modename2id(const char *name)
{
size_t i;
if (name == NULL)
return KEM_MODE_UNDEFINED;
for (i = 0; eckem_modename_id_map[i].mode != NULL; ++i) {
if (OPENSSL_strcasecmp(name, eckem_modename_id_map[i].mode) == 0)
return eckem_modename_id_map[i].id;
}
return KEM_MODE_UNDEFINED;
}

View File

@@ -0,0 +1,27 @@
providers/implementations/kem/libdefault-lib-ec_kem.o: \
providers/implementations/kem/ec_kem.c include/internal/deprecated.h \
include/openssl/configuration.h include/openssl/macros.h \
include/openssl/opensslconf.h include/openssl/opensslv.h \
include/openssl/crypto.h include/openssl/e_os2.h \
include/openssl/safestack.h include/openssl/stack.h \
include/openssl/types.h include/openssl/cryptoerr.h \
include/openssl/symhacks.h include/openssl/cryptoerr_legacy.h \
include/openssl/core.h include/openssl/evp.h \
include/openssl/core_dispatch.h include/openssl/indicator.h \
include/openssl/params.h include/openssl/bn.h include/openssl/bnerr.h \
include/openssl/bio.h include/openssl/bioerr.h include/openssl/evperr.h \
include/openssl/objects.h include/openssl/obj_mac.h \
include/openssl/asn1.h include/openssl/asn1err.h \
include/openssl/objectserr.h include/openssl/core_names.h \
include/openssl/ec.h include/openssl/ecerr.h include/openssl/err.h \
include/openssl/lhash.h include/openssl/proverr.h include/openssl/kdf.h \
include/openssl/rand.h include/openssl/randerr.h \
providers/common/include/prov/provider_ctx.h \
providers/implementations/include/prov/implementations.h \
providers/common/include/prov/securitycheck.h include/crypto/types.h \
providers/fips/include/fips/fipsindicator.h \
providers/common/include/prov/providercommon.h \
include/openssl/provider.h include/openssl/hpke.h \
include/internal/hpke_util.h include/crypto/ec.h \
providers/implementations/include/prov/ecx.h \
providers/implementations/kem/eckem.h

View File

@@ -0,0 +1,27 @@
providers/implementations/kem/libdefault-lib-ecx_kem.o: \
providers/implementations/kem/ecx_kem.c include/internal/deprecated.h \
include/openssl/configuration.h include/openssl/macros.h \
include/openssl/opensslconf.h include/openssl/opensslv.h \
include/openssl/crypto.h include/openssl/e_os2.h \
include/openssl/safestack.h include/openssl/stack.h \
include/openssl/types.h include/openssl/cryptoerr.h \
include/openssl/symhacks.h include/openssl/cryptoerr_legacy.h \
include/openssl/core.h include/openssl/evp.h \
include/openssl/core_dispatch.h include/openssl/indicator.h \
include/openssl/params.h include/openssl/bn.h include/openssl/bnerr.h \
include/openssl/bio.h include/openssl/bioerr.h include/openssl/evperr.h \
include/openssl/objects.h include/openssl/obj_mac.h \
include/openssl/asn1.h include/openssl/asn1err.h \
include/openssl/objectserr.h include/openssl/core_names.h \
include/openssl/kdf.h include/openssl/err.h include/openssl/lhash.h \
include/openssl/sha.h include/openssl/rand.h include/openssl/randerr.h \
include/openssl/proverr.h providers/common/include/prov/provider_ctx.h \
providers/implementations/include/prov/implementations.h \
providers/common/include/prov/securitycheck.h include/crypto/types.h \
include/openssl/ec.h include/openssl/ecerr.h \
providers/fips/include/fips/fipsindicator.h \
providers/common/include/prov/providercommon.h \
include/openssl/provider.h providers/implementations/include/prov/ecx.h \
include/crypto/ecx.h include/internal/refcount.h include/openssl/trace.h \
include/openssl/hpke.h include/internal/hpke_util.h \
providers/implementations/kem/eckem.h

View File

@@ -0,0 +1,9 @@
providers/implementations/kem/libdefault-lib-kem_util.o: \
providers/implementations/kem/kem_util.c include/openssl/core_names.h \
include/openssl/crypto.h include/openssl/macros.h \
include/openssl/opensslconf.h include/openssl/configuration.h \
include/openssl/opensslv.h include/openssl/e_os2.h \
include/openssl/safestack.h include/openssl/stack.h \
include/openssl/types.h include/openssl/cryptoerr.h \
include/openssl/symhacks.h include/openssl/cryptoerr_legacy.h \
include/openssl/core.h providers/implementations/kem/eckem.h

View File

@@ -0,0 +1,24 @@
providers/implementations/kem/libdefault-lib-rsa_kem.o: \
providers/implementations/kem/rsa_kem.c include/internal/deprecated.h \
include/openssl/configuration.h include/openssl/macros.h \
include/openssl/opensslconf.h include/openssl/opensslv.h \
include/internal/nelem.h include/openssl/crypto.h \
include/openssl/e_os2.h include/openssl/safestack.h \
include/openssl/stack.h include/openssl/types.h \
include/openssl/cryptoerr.h include/openssl/symhacks.h \
include/openssl/cryptoerr_legacy.h include/openssl/core.h \
include/openssl/evp.h include/openssl/core_dispatch.h \
include/openssl/indicator.h include/openssl/params.h \
include/openssl/bn.h include/openssl/bnerr.h include/openssl/bio.h \
include/openssl/bioerr.h include/openssl/evperr.h \
include/openssl/objects.h include/openssl/obj_mac.h \
include/openssl/asn1.h include/openssl/asn1err.h \
include/openssl/objectserr.h include/openssl/core_names.h \
include/openssl/rsa.h include/openssl/rsaerr.h include/openssl/err.h \
include/openssl/lhash.h include/openssl/proverr.h include/crypto/rsa.h \
include/crypto/types.h providers/common/include/prov/provider_ctx.h \
providers/common/include/prov/providercommon.h \
include/openssl/provider.h \
providers/implementations/include/prov/implementations.h \
providers/common/include/prov/securitycheck.h include/openssl/ec.h \
include/openssl/ecerr.h providers/fips/include/fips/fipsindicator.h

View File

@@ -0,0 +1,449 @@
/*
* Copyright 2020-2024 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
*/
/*
* RSA low level APIs are deprecated for public use, but still ok for
* internal use.
*/
#include "internal/deprecated.h"
#include "internal/nelem.h"
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/rsa.h>
#include <openssl/params.h>
#include <openssl/err.h>
#include <openssl/proverr.h>
#include "crypto/rsa.h"
#include "prov/provider_ctx.h"
#include "prov/providercommon.h"
#include "prov/implementations.h"
#include "prov/securitycheck.h"
static OSSL_FUNC_kem_newctx_fn rsakem_newctx;
static OSSL_FUNC_kem_encapsulate_init_fn rsakem_encapsulate_init;
static OSSL_FUNC_kem_encapsulate_fn rsakem_generate;
static OSSL_FUNC_kem_decapsulate_init_fn rsakem_decapsulate_init;
static OSSL_FUNC_kem_decapsulate_fn rsakem_recover;
static OSSL_FUNC_kem_freectx_fn rsakem_freectx;
static OSSL_FUNC_kem_dupctx_fn rsakem_dupctx;
static OSSL_FUNC_kem_get_ctx_params_fn rsakem_get_ctx_params;
static OSSL_FUNC_kem_gettable_ctx_params_fn rsakem_gettable_ctx_params;
static OSSL_FUNC_kem_set_ctx_params_fn rsakem_set_ctx_params;
static OSSL_FUNC_kem_settable_ctx_params_fn rsakem_settable_ctx_params;
/*
* Only the KEM for RSASVE as defined in SP800-56b r2 is implemented
* currently.
*/
#define KEM_OP_UNDEFINED -1
#define KEM_OP_RSASVE 0
/*
* What's passed as an actual key is defined by the KEYMGMT interface.
* We happen to know that our KEYMGMT simply passes RSA structures, so
* we use that here too.
*/
typedef struct {
OSSL_LIB_CTX *libctx;
RSA *rsa;
int op;
OSSL_FIPS_IND_DECLARE
} PROV_RSA_CTX;
static const OSSL_ITEM rsakem_opname_id_map[] = {
{ KEM_OP_RSASVE, OSSL_KEM_PARAM_OPERATION_RSASVE },
};
static int name2id(const char *name, const OSSL_ITEM *map, size_t sz)
{
size_t i;
if (name == NULL)
return -1;
for (i = 0; i < sz; ++i) {
if (OPENSSL_strcasecmp(map[i].ptr, name) == 0)
return map[i].id;
}
return -1;
}
static int rsakem_opname2id(const char *name)
{
return name2id(name, rsakem_opname_id_map, OSSL_NELEM(rsakem_opname_id_map));
}
static void *rsakem_newctx(void *provctx)
{
PROV_RSA_CTX *prsactx;
if (!ossl_prov_is_running())
return NULL;
prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX));
if (prsactx == NULL)
return NULL;
prsactx->libctx = PROV_LIBCTX_OF(provctx);
prsactx->op = KEM_OP_UNDEFINED;
OSSL_FIPS_IND_INIT(prsactx)
return prsactx;
}
static void rsakem_freectx(void *vprsactx)
{
PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
RSA_free(prsactx->rsa);
OPENSSL_free(prsactx);
}
static void *rsakem_dupctx(void *vprsactx)
{
PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx;
PROV_RSA_CTX *dstctx;
if (!ossl_prov_is_running())
return NULL;
dstctx = OPENSSL_zalloc(sizeof(*srcctx));
if (dstctx == NULL)
return NULL;
*dstctx = *srcctx;
if (dstctx->rsa != NULL && !RSA_up_ref(dstctx->rsa)) {
OPENSSL_free(dstctx);
return NULL;
}
return dstctx;
}
static int rsakem_init(void *vprsactx, void *vrsa,
const OSSL_PARAM params[], int operation,
const char *desc)
{
PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
int protect = 0;
if (!ossl_prov_is_running())
return 0;
if (prsactx == NULL || vrsa == NULL)
return 0;
if (!ossl_rsa_key_op_get_protect(vrsa, operation, &protect))
return 0;
if (!RSA_up_ref(vrsa))
return 0;
RSA_free(prsactx->rsa);
prsactx->rsa = vrsa;
OSSL_FIPS_IND_SET_APPROVED(prsactx)
if (!rsakem_set_ctx_params(prsactx, params))
return 0;
#ifdef FIPS_MODULE
if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx),
OSSL_FIPS_IND_SETTABLE0, prsactx->libctx,
prsactx->rsa, desc, protect))
return 0;
#endif
return 1;
}
static int rsakem_encapsulate_init(void *vprsactx, void *vrsa,
const OSSL_PARAM params[])
{
return rsakem_init(vprsactx, vrsa, params, EVP_PKEY_OP_ENCAPSULATE,
"RSA Encapsulate Init");
}
static int rsakem_decapsulate_init(void *vprsactx, void *vrsa,
const OSSL_PARAM params[])
{
return rsakem_init(vprsactx, vrsa, params, EVP_PKEY_OP_DECAPSULATE,
"RSA Decapsulate Init");
}
static int rsakem_get_ctx_params(void *vprsactx, OSSL_PARAM *params)
{
PROV_RSA_CTX *ctx = (PROV_RSA_CTX *)vprsactx;
if (ctx == NULL)
return 0;
if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
return 0;
return 1;
}
static const OSSL_PARAM known_gettable_rsakem_ctx_params[] = {
OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
OSSL_PARAM_END
};
static const OSSL_PARAM *rsakem_gettable_ctx_params(ossl_unused void *vprsactx,
ossl_unused void *provctx)
{
return known_gettable_rsakem_ctx_params;
}
static int rsakem_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
{
PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
const OSSL_PARAM *p;
int op;
if (prsactx == NULL)
return 0;
if (params == NULL)
return 1;
if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params,
OSSL_KEM_PARAM_FIPS_KEY_CHECK))
return 0;
p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
if (p != NULL) {
if (p->data_type != OSSL_PARAM_UTF8_STRING)
return 0;
op = rsakem_opname2id(p->data);
if (op < 0)
return 0;
prsactx->op = op;
}
return 1;
}
static const OSSL_PARAM known_settable_rsakem_ctx_params[] = {
OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KEM_PARAM_FIPS_KEY_CHECK)
OSSL_PARAM_END
};
static const OSSL_PARAM *rsakem_settable_ctx_params(ossl_unused void *vprsactx,
ossl_unused void *provctx)
{
return known_settable_rsakem_ctx_params;
}
/*
* NIST.SP.800-56Br2
* 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE).
*
* Generate a random in the range 1 < z < (n 1)
*/
static int rsasve_gen_rand_bytes(RSA *rsa_pub,
unsigned char *out, int outlen)
{
int ret = 0;
BN_CTX *bnctx;
BIGNUM *z, *nminus3;
bnctx = BN_CTX_secure_new_ex(ossl_rsa_get0_libctx(rsa_pub));
if (bnctx == NULL)
return 0;
/*
* Generate a random in the range 1 < z < (n 1).
* Since BN_priv_rand_range_ex() returns a value in range 0 <= r < max
* We can achieve this by adding 2.. but then we need to subtract 3 from
* the upper bound i.e: 2 + (0 <= r < (n - 3))
*/
BN_CTX_start(bnctx);
nminus3 = BN_CTX_get(bnctx);
z = BN_CTX_get(bnctx);
ret = (z != NULL
&& (BN_copy(nminus3, RSA_get0_n(rsa_pub)) != NULL)
&& BN_sub_word(nminus3, 3)
&& BN_priv_rand_range_ex(z, nminus3, 0, bnctx)
&& BN_add_word(z, 2)
&& (BN_bn2binpad(z, out, outlen) == outlen));
BN_CTX_end(bnctx);
BN_CTX_free(bnctx);
return ret;
}
/*
* NIST.SP.800-56Br2
* 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE).
*/
static int rsasve_generate(PROV_RSA_CTX *prsactx,
unsigned char *out, size_t *outlen,
unsigned char *secret, size_t *secretlen)
{
int ret;
size_t nlen;
/* Step (1): nlen = Ceil(len(n)/8) */
nlen = RSA_size(prsactx->rsa);
if (out == NULL) {
if (nlen == 0) {
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
return 0;
}
if (outlen == NULL && secretlen == NULL)
return 0;
if (outlen != NULL)
*outlen = nlen;
if (secretlen != NULL)
*secretlen = nlen;
return 1;
}
/*
* If outlen is specified, then it must report the length
* of the out buffer on input so that we can confirm
* its size is sufficent for encapsulation
*/
if (outlen != NULL && *outlen < nlen) {
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
return 0;
}
/*
* Step (2): Generate a random byte string z of nlen bytes where
* 1 < z < n - 1
*/
if (!rsasve_gen_rand_bytes(prsactx->rsa, secret, nlen))
return 0;
/* Step(3): out = RSAEP((n,e), z) */
ret = RSA_public_encrypt(nlen, secret, out, prsactx->rsa, RSA_NO_PADDING);
if (ret) {
ret = 1;
if (outlen != NULL)
*outlen = nlen;
if (secretlen != NULL)
*secretlen = nlen;
} else {
OPENSSL_cleanse(secret, nlen);
}
return ret;
}
/**
* rsasve_recover - Recovers a secret value from ciphertext using an RSA
* private key. Once, recovered, the secret value is considered to be a
* shared secret. Algorithm is preformed as per
* NIST SP 800-56B Rev 2
* 7.2.1.3 RSASVE Recovery Operation (RSASVE.RECOVER).
*
* This function performs RSA decryption using the private key from the
* provided RSA context (`prsactx`). It takes the input ciphertext, decrypts
* it, and writes the decrypted message to the output buffer.
*
* @prsactx: The RSA context containing the private key.
* @out: The output buffer to store the decrypted message.
* @outlen: On input, the size of the output buffer. On successful
* completion, the actual length of the decrypted message.
* @in: The input buffer containing the ciphertext to be decrypted.
* @inlen: The length of the input ciphertext in bytes.
*
* Returns 1 on success, or 0 on error. In case of error, appropriate
* error messages are raised using the ERR_raise function.
*/
static int rsasve_recover(PROV_RSA_CTX *prsactx,
unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen)
{
size_t nlen;
int ret;
/* Step (1): get the byte length of n */
nlen = RSA_size(prsactx->rsa);
if (out == NULL) {
if (nlen == 0) {
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
return 0;
}
*outlen = nlen;
return 1;
}
/*
* Step (2): check the input ciphertext 'inlen' matches the nlen
* and that outlen is at least nlen bytes
*/
if (inlen != nlen) {
ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH);
return 0;
}
/*
* If outlen is specified, then it must report the length
* of the out buffer, so that we can confirm that it is of
* sufficient size to hold the output of decapsulation
*/
if (outlen != NULL && *outlen < nlen) {
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
return 0;
}
/* Step (3): out = RSADP((n,d), in) */
ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, RSA_NO_PADDING);
if (ret > 0 && outlen != NULL)
*outlen = ret;
return ret > 0;
}
static int rsakem_generate(void *vprsactx, unsigned char *out, size_t *outlen,
unsigned char *secret, size_t *secretlen)
{
PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
if (!ossl_prov_is_running())
return 0;
switch (prsactx->op) {
case KEM_OP_RSASVE:
return rsasve_generate(prsactx, out, outlen, secret, secretlen);
default:
return -2;
}
}
static int rsakem_recover(void *vprsactx, unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen)
{
PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
if (!ossl_prov_is_running())
return 0;
switch (prsactx->op) {
case KEM_OP_RSASVE:
return rsasve_recover(prsactx, out, outlen, in, inlen);
default:
return -2;
}
}
const OSSL_DISPATCH ossl_rsa_asym_kem_functions[] = {
{ OSSL_FUNC_KEM_NEWCTX, (void (*)(void))rsakem_newctx },
{ OSSL_FUNC_KEM_ENCAPSULATE_INIT,
(void (*)(void))rsakem_encapsulate_init },
{ OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))rsakem_generate },
{ OSSL_FUNC_KEM_DECAPSULATE_INIT,
(void (*)(void))rsakem_decapsulate_init },
{ OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))rsakem_recover },
{ OSSL_FUNC_KEM_FREECTX, (void (*)(void))rsakem_freectx },
{ OSSL_FUNC_KEM_DUPCTX, (void (*)(void))rsakem_dupctx },
{ OSSL_FUNC_KEM_GET_CTX_PARAMS,
(void (*)(void))rsakem_get_ctx_params },
{ OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS,
(void (*)(void))rsakem_gettable_ctx_params },
{ OSSL_FUNC_KEM_SET_CTX_PARAMS,
(void (*)(void))rsakem_set_ctx_params },
{ OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
(void (*)(void))rsakem_settable_ctx_params },
OSSL_DISPATCH_END
};