gateway2/Tongsuo-8.4.0/providers/implementations/exchange/sm2dh_exch.c
2026-07-11 13:40:57 +08:00

479 lines
13 KiB
C

/*
* Copyright 2022 The Tongsuo 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://github.com/Tongsuo-Project/Tongsuo/blob/master/LICENSE.txt
*/
/*
* SM2DH low level APIs are deprecated for public use, but still ok for
* internal use.
*/
#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/err.h>
#include <openssl/proverr.h>
#include "prov/provider_ctx.h"
#include "prov/providercommon.h"
#include "prov/implementations.h"
#include "prov/securitycheck.h"
#include "crypto/evp.h"
#include "crypto/ec.h"
#include "crypto/sm2.h"
static OSSL_FUNC_keyexch_newctx_fn sm2dh_newctx;
static OSSL_FUNC_keyexch_init_fn sm2dh_init;
static OSSL_FUNC_keyexch_set_peer_fn sm2dh_set_peer;
static OSSL_FUNC_keyexch_derive_fn sm2dh_derive;
static OSSL_FUNC_keyexch_freectx_fn sm2dh_freectx;
static OSSL_FUNC_keyexch_dupctx_fn sm2dh_dupctx;
static OSSL_FUNC_keyexch_set_ctx_params_fn sm2dh_set_ctx_params;
static OSSL_FUNC_keyexch_settable_ctx_params_fn sm2dh_settable_ctx_params;
static OSSL_FUNC_keyexch_get_ctx_params_fn sm2dh_get_ctx_params;
static OSSL_FUNC_keyexch_gettable_ctx_params_fn sm2dh_gettable_ctx_params;
/*
* What's passed as an actual key is defined by the KEYMGMT interface.
* We happen to know that our KEYMGMT simply passes EC_KEY structures, so
* we use that here too.
*/
typedef struct {
OSSL_LIB_CTX *libctx;
EC_KEY *k;
EC_KEY *peerk;
/* private key in self encryption certificate */
EC_KEY *enc_k;
/* public key in peer encryption certificate */
EC_KEY *enc_peerk;
uint8_t *id;
size_t id_len;
uint8_t *peer_id;
size_t peer_id_len;
int initiator;
EVP_MD *md;
size_t outlen;
} PROV_SM2DH_CTX;
static
void *sm2dh_newctx(void *provctx)
{
PROV_SM2DH_CTX *pectx;
if (!ossl_prov_is_running())
return NULL;
pectx = OPENSSL_zalloc(sizeof(*pectx));
if (pectx == NULL)
return NULL;
pectx->libctx = PROV_LIBCTX_OF(provctx);
return (void *)pectx;
}
static
int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer)
{
int ret;
BN_CTX *ctx = NULL;
const EC_GROUP *group_priv = EC_KEY_get0_group(priv);
const EC_GROUP *group_peer = EC_KEY_get0_group(peer);
ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv));
if (ctx == NULL) {
ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
return 0;
}
ret = group_priv != NULL
&& group_peer != NULL
&& EC_GROUP_cmp(group_priv, group_peer, ctx) == 0;
if (!ret)
ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
BN_CTX_free(ctx);
return ret;
}
static
int sm2dh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])
{
PROV_SM2DH_CTX *pecdhctx = (PROV_SM2DH_CTX *)vpecdhctx;
if (!ossl_prov_is_running()
|| pecdhctx == NULL
|| vecdh == NULL
|| !EC_KEY_up_ref(vecdh))
return 0;
EC_KEY_free(pecdhctx->k);
pecdhctx->k = vecdh;
return sm2dh_set_ctx_params(pecdhctx, params)
&& ossl_ec_check_key(pecdhctx->libctx, vecdh, 1)
&& pecdhctx->enc_k != NULL
&& pecdhctx->enc_peerk != NULL
&& ecdh_match_params(pecdhctx->enc_k, pecdhctx->enc_peerk)
&& ossl_ec_check_key(pecdhctx->libctx, pecdhctx->enc_k, 1)
&& ossl_ec_check_key(pecdhctx->libctx, pecdhctx->enc_peerk, 1);
}
static
int sm2dh_set_peer(void *vpecdhctx, void *vecdh)
{
PROV_SM2DH_CTX *pecdhctx = (PROV_SM2DH_CTX *)vpecdhctx;
if (!ossl_prov_is_running()
|| pecdhctx == NULL
|| vecdh == NULL
|| !ecdh_match_params(pecdhctx->k, vecdh)
|| !ossl_ec_check_key(pecdhctx->libctx, vecdh, 1)
|| !EC_KEY_up_ref(vecdh))
return 0;
EC_KEY_free(pecdhctx->peerk);
pecdhctx->peerk = vecdh;
return 1;
}
static
void sm2dh_freectx(void *vpecdhctx)
{
PROV_SM2DH_CTX *pecdhctx = (PROV_SM2DH_CTX *)vpecdhctx;
EC_KEY_free(pecdhctx->k);
EC_KEY_free(pecdhctx->peerk);
EC_KEY_free(pecdhctx->enc_k);
EC_KEY_free(pecdhctx->enc_peerk);
OPENSSL_free(pecdhctx->id);
OPENSSL_free(pecdhctx->peer_id);
EVP_MD_free(pecdhctx->md);
OPENSSL_free(pecdhctx);
}
static
void *sm2dh_dupctx(void *vpecdhctx)
{
PROV_SM2DH_CTX *srcctx = (PROV_SM2DH_CTX *)vpecdhctx;
PROV_SM2DH_CTX *dstctx;
if (!ossl_prov_is_running())
return NULL;
dstctx = OPENSSL_zalloc(sizeof(*srcctx));
if (dstctx == NULL)
return NULL;
*dstctx = *srcctx;
/* clear all pointers */
dstctx->k= NULL;
dstctx->peerk = NULL;
dstctx->enc_k = NULL;
dstctx->enc_peerk = NULL;
dstctx->md = NULL;
dstctx->id = NULL;
dstctx->peer_id = NULL;
/* up-ref all ref-counted objects referenced in dstctx */
if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))
goto err;
else
dstctx->k = srcctx->k;
if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))
goto err;
else
dstctx->peerk = srcctx->peerk;
if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))
goto err;
else
dstctx->k = srcctx->k;
if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))
goto err;
else
dstctx->peerk = srcctx->peerk;
if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md))
goto err;
else
dstctx->md = srcctx->md;
if (srcctx->id != NULL && srcctx->id_len > 0) {
dstctx->id = OPENSSL_memdup(srcctx->id, srcctx->id_len);
if (dstctx->id == NULL)
goto err;
}
if (srcctx->peer_id != NULL && srcctx->peer_id_len > 0) {
dstctx->peer_id = OPENSSL_memdup(srcctx->peer_id, srcctx->peer_id_len);
if (dstctx->peer_id == NULL)
goto err;
}
return dstctx;
err:
sm2dh_freectx(dstctx);
return NULL;
}
static
int sm2dh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])
{
PROV_SM2DH_CTX *pectx = (PROV_SM2DH_CTX *)vpecdhctx;
const OSSL_PARAM *p;
EVP_PKEY *key = NULL;
void *tmp_id = NULL;
size_t tmp_len;
char name[80] = { '\0' }; /* should be big enough */
char *str = NULL;
if (pectx == NULL)
return 0;
if (params == NULL)
return 1;
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_INITIATOR);
if (p != NULL) {
int initiator;
if (!OSSL_PARAM_get_int(p, &initiator))
return 0;
pectx->initiator = initiator;
}
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_SELF_ENC_KEY);
if (p != NULL) {
EVP_KEYMGMT *keymgmt = NULL;
if (!OSSL_PARAM_get_octet_ptr(p, (const void **)&key, NULL))
return 0;
if (key == NULL)
return 0;
EC_KEY_free(pectx->enc_k);
pectx->enc_k = (EC_KEY *)evp_pkey_export_to_provider(key, pectx->libctx,
&keymgmt, NULL);
EVP_KEYMGMT_free(keymgmt);
if (pectx->enc_k == NULL)
return 0;
else
EC_KEY_up_ref(pectx->enc_k);
}
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_PEER_ENC_KEY);
if (p != NULL) {
EVP_KEYMGMT *keymgmt = NULL;
if (!OSSL_PARAM_get_octet_ptr(p, (const void **)&key, NULL))
return 0;
if (key == NULL)
return 0;
EC_KEY_free(pectx->enc_peerk);
pectx->enc_peerk = (EC_KEY *)evp_pkey_export_to_provider(key,
pectx->libctx,
&keymgmt,
NULL);
EVP_KEYMGMT_free(keymgmt);
if (pectx->enc_peerk == NULL)
return 0;
else
EC_KEY_up_ref(pectx->enc_peerk);
}
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_OUTLEN);
if (p != NULL) {
size_t outlen;
if (!OSSL_PARAM_get_size_t(p, &outlen))
return 0;
pectx->outlen = outlen;
}
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_DIGEST);
if (p != NULL) {
char mdprops[80] = { '\0' }; /* should be big enough */
str = name;
if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
return 0;
str = mdprops;
p = OSSL_PARAM_locate_const(params,
OSSL_EXCHANGE_PARAM_DIGEST_PROPS);
if (p != NULL) {
if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
return 0;
}
EVP_MD_free(pectx->md);
pectx->md = EVP_MD_fetch(pectx->libctx, name, mdprops);
if (!ossl_digest_is_allowed(pectx->libctx, pectx->md)) {
EVP_MD_free(pectx->md);
pectx->md = NULL;
}
if (pectx->md == NULL)
return 0;
}
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_SELF_ID);
if (p != NULL) {
tmp_id = NULL;
if (!OSSL_PARAM_get_octet_string(p, &tmp_id, 0, &tmp_len))
return 0;
OPENSSL_free(pectx->id);
pectx->id = tmp_id;
pectx->id_len = tmp_len;
}
p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_PEER_ID);
if (p != NULL) {
tmp_id = NULL;
if (!OSSL_PARAM_get_octet_string(p, &tmp_id, 0, &tmp_len))
return 0;
OPENSSL_free(pectx->peer_id);
pectx->peer_id = tmp_id;
pectx->peer_id_len = tmp_len;
}
return 1;
}
static const OSSL_PARAM known_settable_ctx_params[] = {
OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_INITIATOR, NULL),
OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_SELF_ID, NULL, 0),
OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_PEER_ID, NULL, 0),
OSSL_PARAM_octet_ptr(OSSL_EXCHANGE_PARAM_SELF_ENC_KEY, NULL, 0),
OSSL_PARAM_octet_ptr(OSSL_EXCHANGE_PARAM_PEER_ENC_KEY, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_DIGEST, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_DIGEST_PROPS, NULL, 0),
OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_OUTLEN, NULL),
OSSL_PARAM_END
};
static
const OSSL_PARAM *sm2dh_settable_ctx_params(ossl_unused void *vpecdhctx,
ossl_unused void *provctx)
{
return known_settable_ctx_params;
}
static
int sm2dh_get_ctx_params(void *vpecdhctx,
OSSL_PARAM params[])
{
PROV_SM2DH_CTX *pectx = (PROV_SM2DH_CTX *)vpecdhctx;
OSSL_PARAM *p;
if (pectx == NULL)
return 0;
p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_DIGEST);
if (p != NULL
&& !OSSL_PARAM_set_utf8_string(p, pectx->md == NULL
? ""
: EVP_MD_get0_name(pectx->md))){
return 0;
}
p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_OUTLEN);
if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->outlen))
return 0;
return 1;
}
static const OSSL_PARAM known_gettable_ctx_params[] = {
OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_DIGEST, NULL, 0),
OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_OUTLEN, NULL),
OSSL_PARAM_END
};
static
const OSSL_PARAM *sm2dh_gettable_ctx_params(ossl_unused void *vpecdhctx,
ossl_unused void *provctx)
{
return known_gettable_ctx_params;
}
static
int sm2dh_derive(void *vpecdhctx, unsigned char *secret,
size_t *psecretlen, size_t outlen)
{
PROV_SM2DH_CTX *pecdhctx = (PROV_SM2DH_CTX *)vpecdhctx;
if (secret == NULL) {
*psecretlen = pecdhctx->outlen;
return 1;
}
if (pecdhctx->outlen > outlen) {
ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
return 0;
}
if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
return 0;
}
if (SM2_compute_key(secret, pecdhctx->outlen, pecdhctx->initiator,
pecdhctx->peer_id, pecdhctx->peer_id_len,
pecdhctx->id, pecdhctx->id_len,
pecdhctx->peerk, pecdhctx->k,
pecdhctx->enc_peerk, pecdhctx->enc_k,
pecdhctx->md, pecdhctx->libctx, NULL) <= 0)
return 0;
*psecretlen = pecdhctx->outlen;
return 1;
}
const OSSL_DISPATCH ossl_sm2dh_keyexch_functions[] = {
{ OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))sm2dh_newctx },
{ OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))sm2dh_init },
{ OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))sm2dh_derive },
{ OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))sm2dh_set_peer },
{ OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))sm2dh_freectx },
{ OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))sm2dh_dupctx },
{ OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))sm2dh_set_ctx_params },
{ OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
(void (*)(void))sm2dh_settable_ctx_params },
{ OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))sm2dh_get_ctx_params },
{ OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
(void (*)(void))sm2dh_gettable_ctx_params },
{ 0, NULL }
};