openHiTLS开源发布HPKE(混合公钥加密)特性:让数据加密在 “鱼与熊掌”间找到最优解
引言
数字世界里,信息传递都面临着两难挑战,我们既要跑得够快,又要防止被不法分子半路 “抢包”或者“偷换”。HPKE(混合公钥加密)可以结合传统对称和非对称算法优势,兼具高速传输与强安全性,成为构筑未来网络安全体系的重要基石。
1. 混合公钥加密机制优势
当前传统加密存在明显的局限性,对称加密虽高效,但密钥分发存在安全隐患,非对称加密解决了密钥分发问题,但算法复杂度较高导致性能偏低。HPKE是一种融合二者优势的混合加密体系,既保留了公钥加密的安全性与灵活性,又继承了对称加密的高效性,让数据加密不再陷入 “安全与效率不可兼得” 的困境。
而TLS协议同为混合加密体系,需处理身份验证、连接握手等复杂流程,协议栈复杂且资源消耗高,适用于浏览器与服务器的长连接场景。而 HPKE 专注于 “单条信息加密传输”,无需建立持久连接,流程更加轻量,应用场景更加灵活。二者定位互补,可共通守护互联网的数字安全。
2. 三步流程解析HPKE算法
RFC9180中定义了HPKE算法的主要流程, 算法设定了消息的发送者和接收者, 以下简单介绍Base模式算法流程:
发送者首先需要获取接收者的长期公钥,然后执行以下流程:
- 临时生成密钥对,使用密钥封装算法(KEM)计算共享密钥, 并输出封装密钥;
- 通过密钥派生函数(KDF)对共享密钥及其上下文信息派生出对称密钥与base_nonce,以及其他密钥导出材料;
- 使用对称算法(AEAD)对数据进行加密, 输出密文。
接收者收到发送者的封装密钥与密文后执行以下流程:
- 使用密钥封装算法(KEM)计算共享密钥,输入为收到的封装密钥与自己的长期私钥;
- 通过密钥派生函数(KDF)对共享密钥及其上下文信息派生出对称密钥与其他所需信息;
- 数据加密:使用对称算法(AEAD)对数据进行解密, 输出明文。
详细交互流程可参考下图

3. HPKE核心技术优势
HPKE 的技术竞争力体现在以下四个方面:
1.安全性:采用临时密钥机制,每次加密生成全新密钥对,降低泄露风险。在密钥封装阶段,通过哈希函数对密钥材料进行处理,有效抵抗碰撞攻击。
2. 高效性:非对称加密仅用于密钥交换环节,数据传输依赖高效对称加密,在大文件传输、高频通信场景表现优异,尤其适配算力受限的物联网设备。
3. 灵活性:HPKE算法不依赖特定的网络协议或通信模式,不依赖通信双方同时在线,可以能在各类场景中灵活部署。
4. 可扩展性:HPKE的“密钥封装 + 数据加密”架构设计为功能扩展预留了充足空间,对于融合后量子算法有天然优势,可从容应对未来的安全挑战。
4. 典型应用场景
HPKE凭借其特有优势,可在多个领域展现出强大的应用价值,如:
1. 即时通讯场景:在即时通讯场景中,用户的聊天内容属于高度隐私信息。HPKE 能为每一条消息提供端到端的加密保护,发送方通过接收方的公钥对消息加密,只有拥有对应私钥的接收方才能解密查看。即使是通讯服务提供商,也无法获取消息的真实内容,有效防止了聊天记录被监听、泄露,为用户的私密对话保驾护航。
2. 物联网场景:物联网设备数量庞大且类型多样,传感器实时采集的温度、湿度、设备运行状态等数据在传输过程中面临被篡改、窃取的风险。HPKE 的轻量化特性使其能在算力有限的物联网设备上高效运行,确保数据从采集端到平台端的安全传输。
3. 邮件系统场景:商务邮件和个人私密邮件往往包含重要信息,一旦泄露可能造成严重后果。HPKE能实现邮件的端到端加密,发件人撰写邮件后,利用收件人的公钥对邮件内容加密,邮件服务器仅负责传输加密后的内容,无法解密查看。收件人收到邮件后,用自己的私钥解密即可读取。这种方式有效防范了邮件在传输环节和存储环节的安全风险,让邮件通信更加安全可靠。
5. 未来展望
在隐私保护需求激增的背景下,混合加密融合对称加密和非对称加密算法优势,兼顾安全与性能,优势日益凸显。值得关注的是,openHiTLS已高效完成了该算法的落地实现,进一步推动了该技术在实际场景中的落地应用,从理论走向实践,为不同行业、不同场景提供了 “量体裁衣” 的加密解决方案,也让HPKE成为数字时代加密技术的重要选择。
未来,随着后量子算法的不断成熟和普及,HPKE也有望成为连接传统加密体系与后量子加密体系的重要桥梁,为数字世界在量子时代的安全提供持续保障,推动安全技术迈向新的高度。
开源实践:openHiTLS开源HPKE代码实现
代码仓链接:GitCode - 全球开发者的开源社区,开源代码托管平台
/*
* This file is part of the openHiTLS project.
*
* openHiTLS is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "hitls_build.h"
#if defined(HITLS_CRYPTO_EAL) && defined(HITLS_CRYPTO_HPKE)
#include <string.h>
#include "securec.h"
#include "crypt_eal_pkey.h"
#include "crypt_eal_kdf.h"
#include "crypt_eal_cipher.h"
#include "crypt_eal_rand.h"
#include "crypt_algid.h"
#include "crypt_errno.h"
#include "crypt_bn.h"
#include "crypt_params_key.h"
#include "bsl_err_internal.h"
#include "bsl_sal.h"
#include "bsl_bytes.h"
#include "crypt_eal_hpke.h"
// Data from RFC9180
#define HPKE_HKDF_MAX_EXTRACT_KEY_LEN 64
#define HPKE_KEM_MAX_SHARED_KEY_LEN 64
#define HPKE_KEM_MAX_ENCAPSULATED_KEY_LEN 133
#define HPKE_KEM_MAX_PUBLIC_KEY_LEN 133
#define HPKE_KEM_MAX_PRIVATE_KEY_LEN 66
#define HPKE_KEM_DH_MAX_SHARED_KEY_LEN 66 // p521 key length
#define MAX_ECC_PARAM_LEN 66
#define HPKE_AEAD_NONCE_LEN 12
#define HPKE_AEAD_TAG_LEN 16
#define HPKE_KEM_SUITEID_LEN 5
#define HPKE_HPKE_SUITEID_LEN 10
typedef struct {
// PSK mode
uint8_t *psk;
uint32_t pskLen;
uint8_t *pskId;
uint32_t pskIdLen;
// AUTH mode, Sender's private key held by the sender, Sender's public key held by the recipient
CRYPT_EAL_PkeyCtx *authPkey;
} AuthInfo;
struct CRYPT_EAL_HpkeCtx {
uint8_t role; // Sender or Recipient
uint8_t mode; // HPKE mode
uint8_t kemIndex;
uint8_t kdfIndex;
uint8_t aeadIndex;
uint8_t *symKey;
uint8_t *baseNonce;
uint32_t symKeyLen;
uint32_t baseNonceLen;
uint8_t *exporterSecret;
uint8_t *sharedSecret;
uint32_t exporterSecretLen;
uint32_t sharedSecretLen;
uint64_t seq; // Message sequence number
CRYPT_EAL_KdfCTX *kdfCtx;
CRYPT_EAL_CipherCtx *cipherCtx;
CRYPT_EAL_LibCtx *libCtx;
char *attrName;
AuthInfo *authInfo;
};
typedef struct {
uint16_t hpkeKemId;
CRYPT_PKEY_AlgId pkeyId;
CRYPT_PKEY_ParaId curveId;
CRYPT_MAC_AlgId macId;
uint16_t privateKeyLen;
uint16_t sharedKeyLen;
uint16_t encapsulatedKeyLen;
uint16_t hkdfExtractKeyLen;
} HPKE_KemAlgInfo;
typedef struct {
uint16_t hpkeKdfId;
uint16_t hkdfExtractKeyLen;
CRYPT_MAC_AlgId macId;
} HPKE_KdfAlgInfo;
typedef struct {
uint16_t hpkeAeadId;
uint16_t keyLen;
CRYPT_CIPHER_AlgId cipherId;
} HPKE_AeadAlgInfo;
#define HPKE_INVALID_ALG_INDEX 0xFF
static HPKE_KemAlgInfo g_hpkeKemAlgInfo[] = {
{CRYPT_KEM_DHKEM_P256_HKDF_SHA256, CRYPT_PKEY_ECDH, CRYPT_ECC_NISTP256, CRYPT_MAC_HMAC_SHA256, 32, 32, 65, 32},
{CRYPT_KEM_DHKEM_P384_HKDF_SHA384, CRYPT_PKEY_ECDH, CRYPT_ECC_NISTP384, CRYPT_MAC_HMAC_SHA384, 48, 48, 97, 48},
{CRYPT_KEM_DHKEM_P521_HKDF_SHA512, CRYPT_PKEY_ECDH, CRYPT_ECC_NISTP521, CRYPT_MAC_HMAC_SHA512, 66, 64, 133, 64},
{CRYPT_KEM_DHKEM_X25519_HKDF_SHA256, CRYPT_PKEY_X25519, CRYPT_PKEY_PARAID_MAX, CRYPT_MAC_HMAC_SHA256, 32, 32, 32,
32},
};
static HPKE_KdfAlgInfo g_hpkeKdfAlgInfo[] = {
{CRYPT_KDF_HKDF_SHA256, 32, CRYPT_MAC_HMAC_SHA256},
{CRYPT_KDF_HKDF_SHA384, 48, CRYPT_MAC_HMAC_SHA384},
{CRYPT_KDF_HKDF_SHA512, 64, CRYPT_MAC_HMAC_SHA512},
};
static HPKE_AeadAlgInfo g_hpkeAeadAlgInfo[] = {
{CRYPT_AEAD_AES_128_GCM, 16, CRYPT_CIPHER_AES128_GCM},
{CRYPT_AEAD_AES_256_GCM, 32, CRYPT_CIPHER_AES256_GCM},
{CRYPT_AEAD_CHACHA20_POLY1305, 32, CRYPT_CIPHER_CHACHA20_POLY1305},
{CRYPT_AEAD_EXPORT_ONLY, 0, CRYPT_CIPHER_MAX},
};
static int32_t HpkeCheckCipherSuite(const CRYPT_HPKE_CipherSuite *cipherSuite, uint8_t *kemIndex, uint8_t *kdfIndex,
uint8_t *aeadIndex)
{
uint8_t kemPosition = HPKE_INVALID_ALG_INDEX;
uint8_t kdfPosition = HPKE_INVALID_ALG_INDEX;
uint8_t aeadPosition = HPKE_INVALID_ALG_INDEX;
uint8_t i;
for (i = 0; i < sizeof(g_hpkeKemAlgInfo) / sizeof(HPKE_KemAlgInfo); i++) {
if (cipherSuite->kemId == g_hpkeKemAlgInfo[i].hpkeKemId) {
kemPosition = i;
break;
}
}
for (i = 0; i < sizeof(g_hpkeKdfAlgInfo) / sizeof(HPKE_KdfAlgInfo); i++) {
if (cipherSuite->kdfId == g_hpkeKdfAlgInfo[i].hpkeKdfId) {
kdfPosition = i;
break;
}
}
for (i = 0; i < sizeof(g_hpkeAeadAlgInfo) / sizeof(HPKE_AeadAlgInfo); i++) {
if (cipherSuite->aeadId == g_hpkeAeadAlgInfo[i].hpkeAeadId) {
aeadPosition = i;
break;
}
}
if (kemPosition == HPKE_INVALID_ALG_INDEX || kdfPosition == HPKE_INVALID_ALG_INDEX ||
aeadPosition == HPKE_INVALID_ALG_INDEX) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
if (kemIndex != NULL) {
*kemIndex = kemPosition;
}
if (kdfIndex != NULL) {
*kdfIndex = kdfPosition;
}
if (aeadIndex != NULL) {
*aeadIndex = aeadPosition;
}
return CRYPT_SUCCESS;
}
static int32_t InitCipherSuiteCtx(CRYPT_EAL_HpkeCtx *ctx, uint8_t aeadIndex, CRYPT_EAL_LibCtx *libCtx,
const char *attrName)
{
CRYPT_EAL_KdfCTX *kdfCtx = NULL;
CRYPT_EAL_CipherCtx *cipherCtx = NULL;
kdfCtx = CRYPT_EAL_ProviderKdfNewCtx(libCtx, CRYPT_KDF_HKDF, attrName);
if (kdfCtx == NULL) {
return CRYPT_HPKE_FAILED_FETCH_KDF;
}
if (g_hpkeAeadAlgInfo[aeadIndex].hpkeAeadId != CRYPT_AEAD_EXPORT_ONLY) {
cipherCtx = CRYPT_EAL_ProviderCipherNewCtx(libCtx, g_hpkeAeadAlgInfo[aeadIndex].cipherId, attrName);
if (cipherCtx == NULL) {
CRYPT_EAL_KdfFreeCtx(kdfCtx);
return CRYPT_HPKE_FAILED_FETCH_CIPHER;
}
}
ctx->kdfCtx = kdfCtx;
ctx->cipherCtx = cipherCtx;
return CRYPT_SUCCESS;
}
static int32_t HpkeInitCipherSuite(CRYPT_EAL_HpkeCtx *ctx, CRYPT_HPKE_CipherSuite *cipherSuite,
CRYPT_EAL_LibCtx *libCtx, const char *attrName)
{
uint8_t kemIndex;
uint8_t kdfIndex;
uint8_t aeadIndex;
int32_t ret;
ret = HpkeCheckCipherSuite(cipherSuite, &kemIndex, &kdfIndex, &aeadIndex);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = InitCipherSuiteCtx(ctx, aeadIndex, libCtx, attrName);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ctx->kemIndex = kemIndex;
ctx->aeadIndex = aeadIndex;
ctx->kdfIndex = kdfIndex;
return CRYPT_SUCCESS;
}
CRYPT_EAL_HpkeCtx *CRYPT_EAL_HpkeNewCtx(CRYPT_EAL_LibCtx *libCtx, const char *attrName, CRYPT_HPKE_Role role,
CRYPT_HPKE_Mode mode, CRYPT_HPKE_CipherSuite cipherSuite)
{
if (role != CRYPT_HPKE_SENDER && role != CRYPT_HPKE_RECIPIENT) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return NULL;
}
if (mode != CRYPT_HPKE_MODE_BASE && mode != CRYPT_HPKE_MODE_PSK && mode != CRYPT_HPKE_MODE_AUTH &&
mode != CRYPT_HPKE_MODE_AUTH_PSK) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return NULL;
}
CRYPT_EAL_HpkeCtx *ctx = (CRYPT_EAL_HpkeCtx*)BSL_SAL_Calloc(1, sizeof(CRYPT_EAL_HpkeCtx));
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
int32_t ret = HpkeInitCipherSuite(ctx, &cipherSuite, libCtx, attrName);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_HpkeFreeCtx(ctx);
return NULL;
}
if (attrName != NULL && strlen(attrName) > 0) {
ctx->attrName = BSL_SAL_Dump(attrName, (uint32_t)strlen(attrName) + 1);
if (ctx->attrName == NULL) {
CRYPT_EAL_HpkeFreeCtx(ctx);
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
}
if (mode == CRYPT_HPKE_MODE_PSK || mode == CRYPT_HPKE_MODE_AUTH || mode == CRYPT_HPKE_MODE_AUTH_PSK) {
AuthInfo *authInfo = (AuthInfo *)BSL_SAL_Calloc(1, sizeof(AuthInfo));
if (authInfo == NULL) {
CRYPT_EAL_HpkeFreeCtx(ctx);
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return NULL;
}
ctx->authInfo = authInfo;
}
ctx->mode = mode;
ctx->role = role;
ctx->libCtx = libCtx;
return ctx;
}
int32_t CRYPT_EAL_HpkeGetEncapKeyLen(CRYPT_HPKE_CipherSuite cipherSuite, uint32_t *encapKeyLen)
{
if (encapKeyLen == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
uint8_t kemIndex;
int32_t ret = HpkeCheckCipherSuite(&cipherSuite, &kemIndex, NULL, NULL);
if (ret != CRYPT_SUCCESS) {
return ret;
}
*encapKeyLen = g_hpkeKemAlgInfo[kemIndex].encapsulatedKeyLen;
return CRYPT_SUCCESS;
}
static int32_t HpkeCreatePkeyCtx(uint8_t kemIdex, CRYPT_EAL_PkeyCtx **pkeyCtx, CRYPT_EAL_LibCtx *libCtx,
const char *attrName)
{
CRYPT_PKEY_AlgId algId = g_hpkeKemAlgInfo[kemIdex].pkeyId;
CRYPT_EAL_PkeyCtx *pkey = NULL;
#ifdef HITLS_CRYPTO_PROVIDER
pkey = CRYPT_EAL_ProviderPkeyNewCtx(libCtx, algId, CRYPT_EAL_PKEY_EXCH_OPERATE, attrName);
#else
(void)libCtx;
(void)attrName;
pkey = CRYPT_EAL_PkeyNewCtx(algId);
#endif
if (pkey == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_FAILED_FETCH_PKEY);
return CRYPT_HPKE_FAILED_FETCH_PKEY;
}
if (algId == CRYPT_PKEY_ECDH) {
CRYPT_PKEY_ParaId curveId = g_hpkeKemAlgInfo[kemIdex].curveId;
int32_t ret = CRYPT_EAL_PkeySetParaById(pkey, curveId);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_PkeyFreeCtx(pkey);
return ret;
}
}
*pkeyCtx = pkey;
return CRYPT_SUCCESS;
}
static int32_t HpkeCreatePubKey(uint8_t kemIdex, uint8_t *pubKey, uint32_t pubKeyLen, CRYPT_EAL_PkeyCtx **pkey,
CRYPT_EAL_LibCtx *libCtx, const char *attrName)
{
CRYPT_EAL_PkeyCtx *tmpPkey = NULL;
int32_t ret = HpkeCreatePkeyCtx(kemIdex, &tmpPkey, libCtx, attrName);
if (ret != CRYPT_SUCCESS) {
return ret;
}
CRYPT_EAL_PkeyPub pub = {0};
pub.id = CRYPT_EAL_PkeyGetId(tmpPkey);
pub.key.eccPub.data = pubKey; // compatible curve25519Pub
pub.key.eccPub.len = pubKeyLen;
ret = CRYPT_EAL_PkeySetPub(tmpPkey, &pub);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_PkeyFreeCtx(tmpPkey);
return ret;
}
*pkey = tmpPkey;
return CRYPT_SUCCESS;
}
static int32_t HpkeCreatePriKey(uint8_t kemIdex, uint8_t *priKey, uint32_t priKeyLen, CRYPT_EAL_PkeyCtx **pkey,
CRYPT_EAL_LibCtx *libCtx, const char *attrName)
{
CRYPT_EAL_PkeyCtx *tmpPkey = *pkey;
int32_t ret;
if (tmpPkey == NULL) {
ret = HpkeCreatePkeyCtx(kemIdex, &tmpPkey, libCtx, attrName);
if (ret != CRYPT_SUCCESS) {
return ret;
}
}
CRYPT_EAL_PkeyPrv prv = {0};
prv.id = CRYPT_EAL_PkeyGetId(tmpPkey);
prv.key.eccPrv.data = priKey;
prv.key.eccPrv.len = priKeyLen;
ret = CRYPT_EAL_PkeySetPrv(tmpPkey, &prv);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
if (g_hpkeKemAlgInfo[kemIdex].hpkeKemId == CRYPT_KEM_DHKEM_X25519_HKDF_SHA256) {
ret = CRYPT_EAL_PkeyCtrl(tmpPkey, CRYPT_CTRL_GEN_X25519_PUBLICKEY, NULL, 0);
} else {
ret = CRYPT_EAL_PkeyCtrl(tmpPkey, CRYPT_CTRL_GEN_ECC_PUBLICKEY, NULL, 0);
}
if (ret == CRYPT_SUCCESS) {
*pkey = tmpPkey;
return CRYPT_SUCCESS;
}
EXIT:
if (*pkey == NULL) {
CRYPT_EAL_PkeyFreeCtx(tmpPkey);
}
return ret;
}
static inline void HpkeGenerateHpkeSuiteId(uint8_t kemIndex, uint8_t kdfIndex, uint8_t aeadIndex, uint8_t *suiteId,
uint32_t suiteIdLen)
{
(void)memcpy_s(suiteId, suiteIdLen, "HPKE", strlen("HPKE"));
uint32_t offset = strlen("HPKE");
BSL_Uint16ToByte(g_hpkeKemAlgInfo[kemIndex].hpkeKemId, suiteId + offset);
offset += sizeof(uint16_t);
BSL_Uint16ToByte(g_hpkeKdfAlgInfo[kdfIndex].hpkeKdfId, suiteId + offset);
offset += sizeof(uint16_t);
BSL_Uint16ToByte(g_hpkeAeadAlgInfo[aeadIndex].hpkeAeadId, suiteId + offset);
}
static inline void HpkeGenerateKemSuiteId(uint8_t kemIdex, uint8_t *suiteId, uint32_t suiteIdLen)
{
uint16_t kemId = g_hpkeKemAlgInfo[kemIdex].hpkeKemId;
(void)memcpy_s(suiteId, suiteIdLen, "KEM", strlen("KEM"));
uint32_t offset = strlen("KEM");
BSL_Uint16ToByte(kemId, suiteId + offset);
}
typedef struct {
int32_t macId;
uint8_t *key;
uint32_t keyLen;
uint8_t *salt;
uint32_t saltLen;
} HPKE_HkdfExtractParams;
typedef struct {
int32_t macId;
uint8_t *prk;
uint32_t prkLen;
uint8_t *info;
uint32_t infoLen;
} HPKE_HkdfExpandParam;
static int32_t HpkeHkdfExtract(CRYPT_EAL_KdfCTX *hkdfCtx, HPKE_HkdfExtractParams *extractParams, uint8_t *out,
uint32_t outLen)
{
int32_t ret;
CRYPT_HKDF_MODE mode = CRYPT_KDF_HKDF_MODE_EXTRACT;
BSL_Param params[6] = {{0}, {0}, {0}, {0}, {0}, BSL_PARAM_END}; // 6 parameters
ret = BSL_PARAM_InitValue(¶ms[0], CRYPT_PARAM_KDF_MAC_ID, BSL_PARAM_TYPE_UINT32, (void *)&extractParams->macId,
sizeof(int32_t));
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[1], CRYPT_PARAM_KDF_MODE, BSL_PARAM_TYPE_UINT32, (void *)&mode, sizeof(mode));
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[2], CRYPT_PARAM_KDF_KEY, BSL_PARAM_TYPE_OCTETS, // param index 2
(void *)extractParams->key, extractParams->keyLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[3], CRYPT_PARAM_KDF_SALT, BSL_PARAM_TYPE_OCTETS, // param index 3
(void *)extractParams->salt, extractParams->saltLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[4], CRYPT_PARAM_KDF_EXLEN, BSL_PARAM_TYPE_UINT32_PTR, // param index 4
(void *)&outLen, sizeof(outLen));
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = CRYPT_EAL_KdfSetParam(hkdfCtx, params);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = CRYPT_EAL_KdfDerive(hkdfCtx, out, outLen);
CRYPT_EAL_KdfDeInitCtx(hkdfCtx);
return ret;
}
static int32_t HpkeHkdfExpand(CRYPT_EAL_KdfCTX *hkdfCtx, HPKE_HkdfExpandParam *expandParams, uint8_t *out,
uint32_t outLen)
{
int32_t ret;
CRYPT_HKDF_MODE mode = CRYPT_KDF_HKDF_MODE_EXPAND;
BSL_Param params[5] = {{0}, {0}, {0}, {0}, BSL_PARAM_END}; // 5 parameters
ret = BSL_PARAM_InitValue(¶ms[0], CRYPT_PARAM_KDF_MAC_ID, BSL_PARAM_TYPE_UINT32, (void *)&expandParams->macId,
sizeof(int32_t));
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[1], CRYPT_PARAM_KDF_MODE, BSL_PARAM_TYPE_UINT32, (void *)&mode, sizeof(mode));
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[2], CRYPT_PARAM_KDF_PRK, BSL_PARAM_TYPE_OCTETS, // param index 2
(void *)expandParams->prk, expandParams->prkLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = BSL_PARAM_InitValue(¶ms[3], CRYPT_PARAM_KDF_INFO, BSL_PARAM_TYPE_OCTETS, // param index 3
(void *)expandParams->info, expandParams->infoLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = CRYPT_EAL_KdfSetParam(hkdfCtx, params);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = CRYPT_EAL_KdfDerive(hkdfCtx, out, outLen);
CRYPT_EAL_KdfDeInitCtx(hkdfCtx);
return ret;
}
typedef struct {
int32_t macId;
uint8_t *salt;
uint32_t saltLen;
uint8_t *label;
uint32_t labelLen;
uint8_t *ikm;
uint32_t ikmLen;
uint8_t *suiteId;
uint32_t suiteIdLen;
} HPKE_LabeledExtractParams;
typedef struct {
int32_t macId;
uint8_t *prk;
uint32_t prkLen;
uint8_t *label;
uint32_t labelLen;
uint8_t *info;
uint32_t infoLen;
uint8_t *suiteId;
uint32_t suiteIdLen;
} HPKE_LabeledExpandParams;
static int32_t HpkeLabeledExtract(CRYPT_EAL_KdfCTX *hkdfCtx, HPKE_LabeledExtractParams *params, uint8_t *out,
uint32_t outLen)
{
// labeled_ikm = "HPKE-v1" || suite_id || label || ikm
const uint8_t *version = (const uint8_t *)"HPKE-v1";
uint32_t versionLen = strlen("HPKE-v1");
uint32_t partialLen = versionLen + params->suiteIdLen + params->labelLen;
if (params->ikmLen > (UINT32_MAX - partialLen)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
uint32_t labeledIkmLen = partialLen + params->ikmLen;
uint8_t *labeledIkm = (uint8_t *)BSL_SAL_Malloc(labeledIkmLen);
if (labeledIkm == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
uint32_t offset = 0;
(void)memcpy_s(labeledIkm + offset, labeledIkmLen - offset, version, versionLen);
offset += versionLen;
(void)memcpy_s(labeledIkm + offset, labeledIkmLen - offset, params->suiteId, params->suiteIdLen);
offset += params->suiteIdLen;
(void)memcpy_s(labeledIkm + offset, labeledIkmLen - offset, params->label, params->labelLen);
offset += params->labelLen;
(void)memcpy_s(labeledIkm + offset, labeledIkmLen - offset, params->ikm, params->ikmLen);
HPKE_HkdfExtractParams extractParams = {params->macId, labeledIkm, labeledIkmLen, params->salt, params->saltLen};
int32_t ret = HpkeHkdfExtract(hkdfCtx, &extractParams, out, outLen);
BSL_SAL_ClearFree(labeledIkm, labeledIkmLen);
return ret;
}
static int32_t HpkeLabeledExpand(CRYPT_EAL_KdfCTX *hkdfCtx, HPKE_LabeledExpandParams *params, uint8_t *out,
uint32_t outLen)
{
// labeled_info = I2OSP(L, 2) || "HPKE-v1" || suite_id || label || info
const uint8_t *version = (const uint8_t *)"HPKE-v1";
uint32_t versionLen = strlen("HPKE-v1");
uint32_t partialLen = sizeof(uint16_t) + versionLen + params->suiteIdLen + params->labelLen;
if (params->infoLen > (UINT32_MAX - partialLen)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
uint32_t labeledInfoLen = partialLen + params->infoLen;
uint8_t *labeledInfo = (uint8_t *)BSL_SAL_Malloc(labeledInfoLen);
if (labeledInfo == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
BSL_Uint16ToByte((uint16_t)outLen, labeledInfo);
uint32_t offset = sizeof(uint16_t);
(void)memcpy_s(labeledInfo + offset, labeledInfoLen - offset, version, versionLen);
offset += versionLen;
(void)memcpy_s(labeledInfo + offset, labeledInfoLen - offset, params->suiteId, params->suiteIdLen);
offset += params->suiteIdLen;
(void)memcpy_s(labeledInfo + offset, labeledInfoLen - offset, params->label, params->labelLen);
offset += params->labelLen;
(void)memcpy_s(labeledInfo + offset, labeledInfoLen - offset, params->info, params->infoLen);
HPKE_HkdfExpandParam expandParams = {params->macId, params->prk, params->prkLen, labeledInfo, labeledInfoLen};
int32_t ret = HpkeHkdfExpand(hkdfCtx, &expandParams, out, outLen);
BSL_SAL_FREE(labeledInfo);
return ret;
}
static int32_t GetPubKeyData(CRYPT_EAL_PkeyCtx *pkey, uint8_t *out, uint32_t *outLen)
{
CRYPT_EAL_PkeyPub ephemPub = { 0 };
ephemPub.id = CRYPT_EAL_PkeyGetId(pkey);
ephemPub.key.eccPub.data = out;
ephemPub.key.eccPub.len = *outLen; // compatible curve25519Pub, CRYPT_Data type.
int32_t ret = CRYPT_EAL_PkeyGetPub(pkey, &ephemPub);
if (ret != CRYPT_SUCCESS) {
return ret;
}
*outLen = ephemPub.key.eccPub.len;
return CRYPT_SUCCESS;
}
static int32_t HpkeComputeSharedSecret(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *priKey, CRYPT_EAL_PkeyCtx *pubKey,
CRYPT_EAL_PkeyCtx *authKey, uint8_t *kemContext, uint32_t kemContextLen, uint8_t *sharedSecret,
uint32_t sharedSecretLen)
{
uint8_t dh[HPKE_KEM_DH_MAX_SHARED_KEY_LEN * 2];
uint32_t dhLen = HPKE_KEM_DH_MAX_SHARED_KEY_LEN;
int32_t ret = CRYPT_EAL_PkeyComputeShareKey(priKey, pubKey, dh, &dhLen);
if (ret != CRYPT_SUCCESS) {
memset_s(dh, dhLen, 0, dhLen);
return ret;
}
if (ctx->mode == CRYPT_HPKE_MODE_AUTH || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
uint32_t dh0Len = HPKE_KEM_DH_MAX_SHARED_KEY_LEN;
if (ctx->role == CRYPT_HPKE_SENDER) {
ret = CRYPT_EAL_PkeyComputeShareKey(authKey, pubKey, dh + dhLen, &dh0Len);
}
if (ctx->role == CRYPT_HPKE_RECIPIENT) {
ret = CRYPT_EAL_PkeyComputeShareKey(priKey, authKey, dh + dhLen, &dh0Len);
}
if (ret != CRYPT_SUCCESS) {
memset_s(dh, dhLen + dh0Len, 0, dhLen + dh0Len);
return ret;
}
dhLen = dhLen + dh0Len;
}
uint8_t suiteId[HPKE_KEM_SUITEID_LEN];
HpkeGenerateKemSuiteId(ctx->kemIndex, suiteId, HPKE_KEM_SUITEID_LEN);
CRYPT_MAC_AlgId macId = g_hpkeKemAlgInfo[ctx->kemIndex].macId;
uint32_t eaePrkLen = g_hpkeKemAlgInfo[ctx->kemIndex].hkdfExtractKeyLen;
uint8_t eaePrk[HPKE_HKDF_MAX_EXTRACT_KEY_LEN];
HPKE_LabeledExtractParams extractParams = {macId, NULL, 0, (uint8_t *)"eae_prk", strlen("eae_prk"), dh, dhLen,
suiteId, HPKE_KEM_SUITEID_LEN};
ret = HpkeLabeledExtract(ctx->kdfCtx, &extractParams, eaePrk, eaePrkLen);
BSL_SAL_CleanseData(dh, dhLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
HPKE_LabeledExpandParams expandParams = {macId, eaePrk, eaePrkLen, (uint8_t *)"shared_secret",
strlen("shared_secret"), kemContext, kemContextLen, suiteId, HPKE_KEM_SUITEID_LEN};
ret = HpkeLabeledExpand(ctx->kdfCtx, &expandParams, sharedSecret, sharedSecretLen);
BSL_SAL_CleanseData(eaePrk, eaePrkLen);
return ret;
}
static int32_t HpkeCreateKemContext(uint8_t *enc, uint32_t encLen, uint8_t *pkR, uint32_t pkRLen,
CRYPT_EAL_PkeyCtx *authKey, uint8_t **out, uint32_t *outLen)
{
uint8_t pkSm[HPKE_KEM_MAX_PUBLIC_KEY_LEN] = { 0 };
uint32_t pkSmLen = HPKE_KEM_MAX_PUBLIC_KEY_LEN;
if (authKey != NULL) {
int32_t ret = GetPubKeyData(authKey, pkSm, &pkSmLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
} else {
pkSmLen = 0;
}
// kemContext = enc || pkRm || pkSm
uint32_t kemContextLen = encLen + pkRLen + pkSmLen;
uint8_t *kemContext = (uint8_t *)BSL_SAL_Malloc(kemContextLen);
if (kemContext == NULL) {
return CRYPT_MEM_ALLOC_FAIL;
}
(void)memcpy_s(kemContext, encLen, enc, encLen);
(void)memcpy_s(kemContext + encLen, pkRLen, pkR, pkRLen);
if (authKey != NULL) {
(void)memcpy_s(kemContext + encLen + pkRLen, pkSmLen, pkSm, pkSmLen);
}
*out = kemContext;
*outLen = kemContextLen;
return CRYPT_SUCCESS;
}
static int32_t HpkeEncap(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *pkey, uint8_t *pkR, uint32_t pkRLen,
uint8_t *encapsulatedKey, uint32_t *encapsulatedKeyLen, uint8_t *sharedSecret, uint32_t sharedSecretLen)
{
int32_t ret;
CRYPT_EAL_PkeyCtx *pkeyS = pkey;
if (pkeyS == NULL) {
CRYPT_HPKE_CipherSuite cipherSuite = {g_hpkeKemAlgInfo[ctx->kemIndex].hpkeKemId,
g_hpkeKdfAlgInfo[ctx->kdfIndex].hpkeKdfId, g_hpkeAeadAlgInfo[ctx->aeadIndex].hpkeAeadId};
ret = CRYPT_EAL_HpkeGenerateKeyPair(ctx->libCtx, ctx->attrName, cipherSuite, NULL, 0, &pkeyS);
if (ret != CRYPT_SUCCESS) {
return ret;
}
}
CRYPT_EAL_PkeyCtx *pkeyR = NULL;
uint8_t enc[HPKE_KEM_MAX_PUBLIC_KEY_LEN] = { 0 };
uint32_t encLen = HPKE_KEM_MAX_PUBLIC_KEY_LEN;
uint32_t kemContextLen = 0;
uint8_t *kemContext = NULL;
CRYPT_EAL_PkeyCtx *authKey = NULL;
if (ctx->mode == CRYPT_HPKE_MODE_AUTH || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
authKey = ctx->authInfo->authPkey;
}
ret = GetPubKeyData(pkeyS, enc, &encLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeCreatePubKey(ctx->kemIndex, pkR, pkRLen, &pkeyR, ctx->libCtx, ctx->attrName);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeCreateKemContext(enc, encLen, pkR, pkRLen, authKey, &kemContext, &kemContextLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeComputeSharedSecret(ctx, pkeyS, pkeyR, authKey, kemContext, kemContextLen, sharedSecret, sharedSecretLen);
if (ret == CRYPT_SUCCESS) {
(void)memcpy_s(encapsulatedKey, *encapsulatedKeyLen, enc, encLen);
*encapsulatedKeyLen = encLen;
}
EXIT:
BSL_SAL_FREE(kemContext);
CRYPT_EAL_PkeyFreeCtx(pkeyR);
if (pkey == NULL) {
CRYPT_EAL_PkeyFreeCtx(pkeyS);
}
return ret;
}
static int32_t HpkeGenKeyScheduleCtx(CRYPT_EAL_HpkeCtx *ctx, uint8_t *info, uint32_t infoLen, uint8_t *pskId,
uint32_t pskIdLen, uint8_t *suiteId, uint32_t suiteIdLen, uint8_t **keyScheduleContext,
uint32_t *keyScheduleContextLen)
{
uint32_t extractKeyLen = g_hpkeKdfAlgInfo[ctx->kdfIndex].hkdfExtractKeyLen;
uint32_t contextLen = sizeof(uint8_t) + extractKeyLen + extractKeyLen;
uint8_t *context = (uint8_t *)BSL_SAL_Malloc(contextLen);
if (context == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
context[0] = ctx->mode;
uint32_t offset = sizeof(uint8_t);
CRYPT_MAC_AlgId macId = g_hpkeKdfAlgInfo[ctx->kdfIndex].macId;
HPKE_LabeledExtractParams params = {macId, NULL, 0, (uint8_t*)"psk_id_hash", strlen("psk_id_hash"), pskId, pskIdLen,
suiteId, suiteIdLen};
int32_t ret = HpkeLabeledExtract(ctx->kdfCtx, ¶ms, context + offset, extractKeyLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
offset += extractKeyLen;
params.label = (uint8_t*)"info_hash";
params.labelLen = strlen("info_hash");
params.ikm = info;
params.ikmLen = infoLen;
ret = HpkeLabeledExtract(ctx->kdfCtx, ¶ms, context + offset, extractKeyLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
*keyScheduleContext = context;
*keyScheduleContextLen = contextLen;
return CRYPT_SUCCESS;
EXIT:
BSL_SAL_ClearFree(context, contextLen);
return ret;
}
static void HpkeFreeKeyInfo(CRYPT_EAL_HpkeCtx *ctx)
{
BSL_SAL_ClearFree(ctx->symKey, ctx->symKeyLen);
ctx->symKey = NULL;
ctx->symKeyLen = 0;
BSL_SAL_ClearFree(ctx->baseNonce, ctx->baseNonceLen);
ctx->baseNonce = NULL;
ctx->baseNonceLen = 0;
BSL_SAL_ClearFree(ctx->exporterSecret, ctx->exporterSecretLen);
ctx->exporterSecret = NULL;
ctx->exporterSecretLen = 0;
}
static int32_t HpkeMallocKeyInfo(CRYPT_EAL_HpkeCtx *ctx)
{
if (g_hpkeAeadAlgInfo[ctx->aeadIndex].hpkeAeadId != CRYPT_AEAD_EXPORT_ONLY) {
ctx->symKeyLen = g_hpkeAeadAlgInfo[ctx->aeadIndex].keyLen;
ctx->symKey = BSL_SAL_Malloc(ctx->symKeyLen);
ctx->baseNonceLen = HPKE_AEAD_NONCE_LEN;
ctx->baseNonce = BSL_SAL_Malloc(HPKE_AEAD_NONCE_LEN);
if (ctx->symKey == NULL || ctx->baseNonce == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
HpkeFreeKeyInfo(ctx);
return CRYPT_MEM_ALLOC_FAIL;
}
}
ctx->exporterSecretLen = g_hpkeKdfAlgInfo[ctx->kdfIndex].hkdfExtractKeyLen;
ctx->exporterSecret = BSL_SAL_Malloc(ctx->exporterSecretLen);
if (ctx->exporterSecret == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
HpkeFreeKeyInfo(ctx);
return CRYPT_MEM_ALLOC_FAIL;
}
return CRYPT_SUCCESS;
}
static int32_t HpkeDeriveKeyInfo(CRYPT_EAL_HpkeCtx *ctx, HPKE_LabeledExpandParams *expandParams)
{
CRYPT_HPKE_AEAD_AlgId aeadId = g_hpkeAeadAlgInfo[ctx->aeadIndex].hpkeAeadId;
if (aeadId != CRYPT_AEAD_EXPORT_ONLY) {
int32_t ret = HpkeLabeledExpand(ctx->kdfCtx, expandParams, ctx->symKey, ctx->symKeyLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
expandParams->label = (uint8_t*)"base_nonce";
expandParams->labelLen = strlen("base_nonce");
ret = HpkeLabeledExpand(ctx->kdfCtx, expandParams, ctx->baseNonce, ctx->baseNonceLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
}
expandParams->label = (uint8_t*)"exp";
expandParams->labelLen = strlen("exp");
return HpkeLabeledExpand(ctx->kdfCtx, expandParams, ctx->exporterSecret, ctx->exporterSecretLen);
}
static int32_t HpkeKeySchedule(CRYPT_EAL_HpkeCtx *ctx, uint8_t *sharedSecret, uint32_t sharedSecretLen, uint8_t *info,
uint32_t infoLen)
{
uint8_t suiteId[HPKE_HPKE_SUITEID_LEN];
uint8_t suiteIdLen = HPKE_HPKE_SUITEID_LEN;
HpkeGenerateHpkeSuiteId(ctx->kemIndex, ctx->kdfIndex, ctx->aeadIndex, suiteId, HPKE_HPKE_SUITEID_LEN);
uint32_t contextLen;
uint8_t *context = NULL;
uint8_t *pskId = (uint8_t *)"";
uint32_t pskIdLen = 0;
uint8_t *psk = (uint8_t *)"";
uint32_t pskLen = 0;
if (ctx->mode == CRYPT_HPKE_MODE_PSK || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
pskId = ctx->authInfo->pskId;
pskIdLen = ctx->authInfo->pskIdLen;
psk = ctx->authInfo->psk;
pskLen = ctx->authInfo->pskLen;
}
int32_t ret = HpkeGenKeyScheduleCtx(ctx, info, infoLen, pskId, pskIdLen, suiteId, suiteIdLen, &context,
&contextLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
CRYPT_MAC_AlgId macId = g_hpkeKdfAlgInfo[ctx->kdfIndex].macId;
uint8_t secret[HPKE_KEM_MAX_SHARED_KEY_LEN] = {0};
uint32_t secretLen = g_hpkeKdfAlgInfo[ctx->kdfIndex].hkdfExtractKeyLen;
HPKE_LabeledExtractParams extractparams = {macId, sharedSecret, sharedSecretLen, (uint8_t*)"secret",
strlen("secret"), psk, pskLen, suiteId, suiteIdLen};
HPKE_LabeledExpandParams expandParams = {macId, secret, secretLen, (uint8_t*)"key", strlen("key"), context,
contextLen, suiteId, suiteIdLen};
ret = HpkeLabeledExtract(ctx->kdfCtx, &extractparams, secret, secretLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeMallocKeyInfo(ctx);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeDeriveKeyInfo(ctx, &expandParams);
EXIT:
BSL_SAL_CleanseData(secret, HPKE_KEM_MAX_SHARED_KEY_LEN);
BSL_SAL_ClearFree(context, contextLen);
if (ret != CRYPT_SUCCESS) {
HpkeFreeKeyInfo(ctx);
}
return ret;
}
static int32_t HpkeCheckAuthInfo(CRYPT_EAL_HpkeCtx *ctx)
{
if (ctx->mode == CRYPT_HPKE_MODE_AUTH || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
if (ctx->authInfo == NULL || ctx->authInfo->authPkey == NULL) {
return CRYPT_HPKE_ERR_CALL;
}
}
if (ctx->mode == CRYPT_HPKE_MODE_PSK || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
if (ctx->authInfo == NULL || ctx->authInfo->psk == NULL || ctx->authInfo->pskId == NULL) {
return CRYPT_HPKE_ERR_CALL;
}
}
return CRYPT_SUCCESS;
}
static void HpkeFreeAuthInfo(CRYPT_EAL_HpkeCtx *ctx)
{
if (ctx->authInfo == NULL) {
return;
}
BSL_SAL_ClearFree(ctx->authInfo->psk, ctx->authInfo->pskLen);
ctx->authInfo->psk = NULL;
ctx->authInfo->pskLen = 0;
BSL_SAL_ClearFree(ctx->authInfo->pskId, ctx->authInfo->pskIdLen);
ctx->authInfo->pskId = NULL;
ctx->authInfo->pskIdLen = 0;
CRYPT_EAL_PkeyFreeCtx(ctx->authInfo->authPkey);
ctx->authInfo->authPkey = NULL;
BSL_SAL_FREE(ctx->authInfo);
}
static int32_t HpkeCheckSenderParams(CRYPT_EAL_HpkeCtx *ctx, uint8_t *info, uint32_t infoLen, const uint8_t *pkR,
uint32_t pkRLen, uint8_t *encapsulatedKey, uint32_t *encapsulatedKeyLen)
{
if (ctx == NULL) {
return CRYPT_NULL_INPUT;
}
if (ctx->role != CRYPT_HPKE_SENDER) {
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->sharedSecret != NULL) {
return CRYPT_HPKE_ERR_CALL;
}
if (pkR == NULL || encapsulatedKey == NULL || encapsulatedKeyLen == NULL) {
return CRYPT_NULL_INPUT;
}
if ((info == NULL && infoLen != 0) || (info != NULL && infoLen == 0)) {
return CRYPT_INVALID_ARG;
}
uint32_t encLen = g_hpkeKemAlgInfo[ctx->kemIndex].encapsulatedKeyLen;
if (pkRLen != encLen) {
return CRYPT_INVALID_ARG;
}
if (*encapsulatedKeyLen < encLen) {
return CRYPT_INVALID_ARG;
}
return HpkeCheckAuthInfo(ctx);
}
int32_t CRYPT_EAL_HpkeSetupSender(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *pkey, uint8_t *info, uint32_t infoLen,
uint8_t *pkR, uint32_t pkRLen, uint8_t *encapKey, uint32_t *encapKeyLen)
{
int32_t ret = HpkeCheckSenderParams(ctx, info, infoLen, pkR, pkRLen, encapKey, encapKeyLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
uint32_t sharedSecretLen = g_hpkeKemAlgInfo[ctx->kemIndex].sharedKeyLen;
uint8_t *sharedSecret = BSL_SAL_Malloc(sharedSecretLen);
if (sharedSecret == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ret = HpkeEncap(ctx, pkey, pkR, pkRLen, encapKey, encapKeyLen, sharedSecret, sharedSecretLen);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_ClearFree(sharedSecret, sharedSecretLen);
return ret;
}
ret = HpkeKeySchedule(ctx, sharedSecret, sharedSecretLen, info, infoLen);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_ClearFree(sharedSecret, sharedSecretLen);
return ret;
}
ctx->sharedSecret = sharedSecret;
ctx->sharedSecretLen = sharedSecretLen;
HpkeFreeAuthInfo(ctx); // Derived key successfully, no longer requires authinfo
return ret;
}
static int32_t HpkeAeadEncrypt(CRYPT_EAL_HpkeCtx *ctx, const uint8_t *nonce, uint32_t nonceLen, uint8_t *aad,
uint32_t aadLen, const uint8_t *plainText, uint32_t plainTextLen, uint8_t *cipherText, uint32_t *cipherTextLen)
{
CRYPT_EAL_CipherCtx *cipherCtx = ctx->cipherCtx;
uint32_t outLen = *cipherTextLen;
int32_t ret = CRYPT_EAL_CipherInit(cipherCtx, ctx->symKey, ctx->symKeyLen, nonce, nonceLen, true);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
if (aad != NULL && aadLen > 0) {
ret = CRYPT_EAL_CipherCtrl(cipherCtx, CRYPT_CTRL_SET_AAD, aad, aadLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
}
ret = CRYPT_EAL_CipherUpdate(cipherCtx, plainText, plainTextLen, cipherText, &outLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = CRYPT_EAL_CipherCtrl(cipherCtx, CRYPT_CTRL_GET_TAG, cipherText + outLen, HPKE_AEAD_TAG_LEN);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
*cipherTextLen = outLen + HPKE_AEAD_TAG_LEN;
EXIT:
CRYPT_EAL_CipherDeinit(cipherCtx);
return ret;
}
int32_t CRYPT_EAL_HpkeSetSeq(CRYPT_EAL_HpkeCtx *ctx, uint64_t seq)
{
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (seq == UINT64_MAX) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
ctx->seq = seq;
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeGetSeq(CRYPT_EAL_HpkeCtx *ctx, uint64_t *seq)
{
if (ctx == NULL || seq == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
*seq = ctx->seq;
return CRYPT_SUCCESS;
}
static void HpkeComputeNonce(CRYPT_EAL_HpkeCtx *ctx, uint8_t *nonce, uint32_t nonceLen)
{
uint64_t seq = ctx->seq;
for (uint32_t i = 0; i < sizeof(seq); i++) {
nonce[nonceLen - i - 1] = seq & UINT8_MAX;
seq = seq >> 8; // 8 bits
}
for (uint32_t i = 0; i < nonceLen; i++) {
nonce[i] ^= ctx->baseNonce[i];
}
}
static int32_t HpkeCheckSealParams(CRYPT_EAL_HpkeCtx *ctx, const uint8_t *plainText, uint32_t plainTextLen,
uint32_t *cipherTextLen)
{
if (ctx == NULL) {
return CRYPT_NULL_INPUT;
}
if (ctx->role != CRYPT_HPKE_SENDER) {
return CRYPT_HPKE_ERR_CALL;
}
if (g_hpkeAeadAlgInfo[ctx->aeadIndex].hpkeAeadId == CRYPT_AEAD_EXPORT_ONLY) {
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->symKey == NULL || ctx->baseNonce == NULL) {
return CRYPT_HPKE_ERR_CALL;
}
if (plainText == NULL || plainTextLen == 0 || cipherTextLen == NULL) {
return CRYPT_NULL_INPUT;
}
if (plainTextLen > (UINT32_MAX - HPKE_AEAD_TAG_LEN)) {
return CRYPT_INVALID_ARG;
}
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeSeal(CRYPT_EAL_HpkeCtx *ctx, uint8_t *aad, uint32_t aadLen, const uint8_t *plainText,
uint32_t plainTextLen, uint8_t *cipherText, uint32_t *cipherTextLen)
{
int32_t ret = HpkeCheckSealParams(ctx, plainText, plainTextLen, cipherTextLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
if (ctx->seq + 1 == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (cipherText == NULL) {
*cipherTextLen = plainTextLen + HPKE_AEAD_TAG_LEN;
return CRYPT_SUCCESS;
}
if (*cipherTextLen < (plainTextLen + HPKE_AEAD_TAG_LEN)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
uint8_t nonce[HPKE_AEAD_NONCE_LEN] = { 0 };
HpkeComputeNonce(ctx, nonce, HPKE_AEAD_NONCE_LEN);
ret = HpkeAeadEncrypt(ctx, nonce, HPKE_AEAD_NONCE_LEN, aad, aadLen, plainText, plainTextLen, cipherText,
cipherTextLen);
if (ret == CRYPT_SUCCESS) {
ctx->seq++;
}
return ret;
}
static int32_t HpkeDecap(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *pkey, uint8_t *encKey, uint32_t encKeyLen,
uint8_t *sharedSecret, uint32_t sharedSecretLen)
{
CRYPT_EAL_PkeyCtx *pkeyS = NULL;
int32_t ret = HpkeCreatePubKey(ctx->kemIndex, encKey, encKeyLen, &pkeyS, ctx->libCtx, ctx->attrName);
if (ret != CRYPT_SUCCESS) {
return ret;
}
uint8_t *kemContext = NULL;
uint32_t kemContextLen;
uint8_t pubKeyData[HPKE_KEM_MAX_PUBLIC_KEY_LEN];
uint32_t pubKeyDataLen = HPKE_KEM_MAX_PUBLIC_KEY_LEN;
CRYPT_EAL_PkeyCtx *authKey = NULL;
if (ctx->mode == CRYPT_HPKE_MODE_AUTH || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
authKey = ctx->authInfo->authPkey;
}
ret = GetPubKeyData(pkey, pubKeyData, &pubKeyDataLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeCreateKemContext(encKey, encKeyLen, pubKeyData, pubKeyDataLen, authKey, &kemContext, &kemContextLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeComputeSharedSecret(ctx, pkey, pkeyS, authKey, kemContext, kemContextLen, sharedSecret, sharedSecretLen);
EXIT:
CRYPT_EAL_PkeyFreeCtx(pkeyS);
BSL_SAL_FREE(kemContext);
return ret;
}
static int32_t HpkeCheckRecipientParams(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *pkey, uint8_t *info,
uint32_t infoLen, const uint8_t *encapsulatedKey, uint32_t encapsulatedKeyLen)
{
if (ctx == NULL) {
return CRYPT_NULL_INPUT;
}
if (ctx->role != CRYPT_HPKE_RECIPIENT) {
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->sharedSecret != NULL) {
return CRYPT_HPKE_ERR_CALL;
}
if ((info == NULL && infoLen != 0) || (info != NULL && infoLen == 0)) {
return CRYPT_INVALID_ARG;
}
if (pkey == NULL || encapsulatedKey == NULL) {
return CRYPT_NULL_INPUT;
}
if (encapsulatedKeyLen != g_hpkeKemAlgInfo[ctx->kemIndex].encapsulatedKeyLen) {
return CRYPT_INVALID_ARG;
}
return HpkeCheckAuthInfo(ctx);
}
int32_t CRYPT_EAL_HpkeSetupRecipient(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *pkey, uint8_t *info, uint32_t infoLen,
uint8_t *encapKey, uint32_t encapKeyLen)
{
int32_t ret = HpkeCheckRecipientParams(ctx, pkey, info, infoLen, encapKey, encapKeyLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
uint32_t sharedSecretLen = g_hpkeKemAlgInfo[ctx->kemIndex].sharedKeyLen;
uint8_t *sharedSecret = BSL_SAL_Malloc(sharedSecretLen);
if (sharedSecret == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ret = HpkeDecap(ctx, pkey, encapKey, encapKeyLen, sharedSecret, sharedSecretLen);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_ClearFree(sharedSecret, sharedSecretLen);
return ret;
}
ret = HpkeKeySchedule(ctx, sharedSecret, sharedSecretLen, info, infoLen);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_ClearFree(sharedSecret, sharedSecretLen);
return ret;
}
ctx->sharedSecret = sharedSecret;
ctx->sharedSecretLen = sharedSecretLen;
HpkeFreeAuthInfo(ctx); // Derived key successfully, no longer requires authinfo
return ret;
}
static int32_t HpkeAeadDecrypt(CRYPT_EAL_HpkeCtx *ctx, const uint8_t *nonce, uint32_t nonceLen, uint8_t *aad,
uint32_t aadLen, const uint8_t *cipherText, uint32_t cipherTextLen, uint8_t *plainText, uint32_t *plainTextLen)
{
CRYPT_EAL_CipherCtx *cipherCtx = ctx->cipherCtx;
int32_t ret = CRYPT_EAL_CipherInit(cipherCtx, ctx->symKey, ctx->symKeyLen, nonce, nonceLen, false);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_CipherDeinit(cipherCtx);
return ret;
}
if (aad != NULL && aadLen > 0) {
ret = CRYPT_EAL_CipherCtrl(cipherCtx, CRYPT_CTRL_SET_AAD, (void *)aad, aadLen);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_CipherDeinit(cipherCtx);
return ret;
}
}
ret = CRYPT_EAL_CipherUpdate(cipherCtx, cipherText, cipherTextLen - HPKE_AEAD_TAG_LEN, plainText, plainTextLen);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_CipherDeinit(cipherCtx);
return ret;
}
uint8_t tag[HPKE_AEAD_TAG_LEN];
ret = CRYPT_EAL_CipherCtrl(cipherCtx, CRYPT_CTRL_GET_TAG, (void *)tag, HPKE_AEAD_TAG_LEN);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
if (memcmp(tag, cipherText + (cipherTextLen - HPKE_AEAD_TAG_LEN), HPKE_AEAD_TAG_LEN) != 0) {
ret = CRYPT_HPKE_ERR_AEAD_TAG;
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_AEAD_TAG);
}
EXIT:
if (ret != CRYPT_SUCCESS) {
BSL_SAL_CleanseData(plainText, *plainTextLen);
}
CRYPT_EAL_CipherDeinit(cipherCtx);
return ret;
}
static int32_t HpkeCheckOpenParams(CRYPT_EAL_HpkeCtx *ctx, const uint8_t *cipherText, uint32_t cipherTextLen,
uint32_t *plainTextLen)
{
if (ctx == NULL) {
return CRYPT_NULL_INPUT;
}
if (ctx->role != CRYPT_HPKE_RECIPIENT) {
return CRYPT_HPKE_ERR_CALL;
}
if (g_hpkeAeadAlgInfo[ctx->aeadIndex].hpkeAeadId == CRYPT_AEAD_EXPORT_ONLY) {
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->symKey == NULL || ctx->baseNonce == NULL) {
return CRYPT_HPKE_ERR_CALL;
}
if (cipherText == NULL || cipherTextLen == 0 || plainTextLen == NULL) {
return CRYPT_NULL_INPUT;
}
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeOpen(CRYPT_EAL_HpkeCtx *ctx, uint8_t *aad, uint32_t aadLen, const uint8_t *cipherText,
uint32_t cipherTextLen, uint8_t *plainText, uint32_t *plainTextLen)
{
int32_t ret = HpkeCheckOpenParams(ctx, cipherText, cipherTextLen, plainTextLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
if (ctx->seq + 1 == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (cipherTextLen <= HPKE_AEAD_TAG_LEN) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
if (plainText == NULL) {
*plainTextLen = cipherTextLen - HPKE_AEAD_TAG_LEN;
return CRYPT_SUCCESS;
}
uint8_t nonce[HPKE_AEAD_NONCE_LEN] = { 0 };
HpkeComputeNonce(ctx, nonce, HPKE_AEAD_NONCE_LEN);
ret = HpkeAeadDecrypt(ctx, nonce, HPKE_AEAD_NONCE_LEN, aad, aadLen, cipherText, cipherTextLen, plainText,
plainTextLen);
if (ret == CRYPT_SUCCESS) {
ctx->seq++;
}
return ret;
}
void CRYPT_EAL_HpkeFreeCtx(CRYPT_EAL_HpkeCtx *ctx)
{
if (ctx == NULL) {
return;
}
BSL_SAL_ClearFree(ctx->sharedSecret, ctx->sharedSecretLen);
HpkeFreeKeyInfo(ctx);
CRYPT_EAL_CipherFreeCtx(ctx->cipherCtx);
CRYPT_EAL_KdfFreeCtx(ctx->kdfCtx);
BSL_SAL_FREE(ctx->attrName);
HpkeFreeAuthInfo(ctx);
BSL_SAL_ClearFree(ctx, sizeof(CRYPT_EAL_HpkeCtx));
}
static int32_t HpkeGetEccOrder(CRYPT_EAL_PkeyCtx *pkey, BN_BigNum **order)
{
uint8_t ecP[MAX_ECC_PARAM_LEN];
uint8_t ecA[MAX_ECC_PARAM_LEN];
uint8_t ecB[MAX_ECC_PARAM_LEN];
uint8_t ecN[MAX_ECC_PARAM_LEN];
uint8_t ecH[MAX_ECC_PARAM_LEN];
uint8_t ecX[MAX_ECC_PARAM_LEN];
uint8_t ecY[MAX_ECC_PARAM_LEN];
CRYPT_EAL_PkeyPara para = {0};
para.id = CRYPT_EAL_PkeyGetId(pkey);
para.para.eccPara.p = ecP;
para.para.eccPara.a = ecA;
para.para.eccPara.b = ecB;
para.para.eccPara.n = ecN;
para.para.eccPara.h = ecH;
para.para.eccPara.x = ecX;
para.para.eccPara.y = ecY;
para.para.eccPara.pLen = MAX_ECC_PARAM_LEN;
para.para.eccPara.aLen = MAX_ECC_PARAM_LEN;
para.para.eccPara.bLen = MAX_ECC_PARAM_LEN;
para.para.eccPara.nLen = MAX_ECC_PARAM_LEN;
para.para.eccPara.hLen = MAX_ECC_PARAM_LEN;
para.para.eccPara.xLen = MAX_ECC_PARAM_LEN;
para.para.eccPara.yLen = MAX_ECC_PARAM_LEN;
int32_t ret = CRYPT_EAL_PkeyGetPara(pkey, ¶);
if (ret != CRYPT_SUCCESS) {
return ret;
}
BN_BigNum *bn = BN_Create(para.para.eccPara.nLen * 8);
if (bn == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ret = BN_Bin2Bn(bn, para.para.eccPara.n, para.para.eccPara.nLen);
if (ret != CRYPT_SUCCESS) {
BN_Destroy(bn);
return ret;
}
*order = bn;
return CRYPT_SUCCESS;
}
static int32_t HpkeExpandEccPriKey(CRYPT_EAL_PkeyCtx *pkey, CRYPT_EAL_KdfCTX *hkdfCtx, uint32_t kemIndex,
HPKE_LabeledExpandParams *params, uint8_t *sk, uint32_t skLen)
{
BN_BigNum *order = NULL;
int32_t ret = HpkeGetEccOrder(pkey, &order);
if (ret != CRYPT_SUCCESS) {
return ret;
}
BN_BigNum *skBn = BN_Create(skLen * 8);
if (skBn == NULL) {
BN_Destroy(order);
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
uint8_t counter = 0;
uint8_t bitmask = 0xFF; // 0xFF for P256 P384
if (g_hpkeKemAlgInfo[kemIndex].hpkeKemId == CRYPT_KEM_DHKEM_P521_HKDF_SHA512) {
bitmask = 0x01;
}
do {
if (counter == 255) { // RFC9180 7.1.3, up to 255 attempts.
ret = CRYPT_HPKE_ERR_GEN_ASYM_KEY;
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_GEN_ASYM_KEY);
break;
}
*(params->info) = counter;
ret = HpkeLabeledExpand(hkdfCtx, params, sk, skLen);
if (ret != CRYPT_SUCCESS) {
break;
}
sk[0] = sk[0] & bitmask;
ret = BN_Bin2Bn(skBn, sk, skLen);
if (ret != CRYPT_SUCCESS) {
break;
}
counter++;
} while (BN_IsZero(skBn) || BN_Cmp(skBn, order) >= 0);
BN_Destroy(skBn);
BN_Destroy(order);
if (ret != CRYPT_SUCCESS) {
BSL_SAL_CleanseData(sk, skLen);
}
return ret;
}
static int32_t DeriveSk(uint8_t kemIndex, CRYPT_EAL_KdfCTX *kdfCtx, CRYPT_EAL_PkeyCtx *pkey,
HPKE_LabeledExpandParams *expandParams, uint8_t *sk, uint32_t skLen)
{
if (g_hpkeKemAlgInfo[kemIndex].hpkeKemId == CRYPT_KEM_DHKEM_X25519_HKDF_SHA256) {
return HpkeLabeledExpand(kdfCtx, expandParams, sk, skLen);
} else {
uint8_t counter = 0;
expandParams->label = (uint8_t *)"candidate";
expandParams->labelLen = strlen("candidate");
expandParams->info = (uint8_t *)&counter;
expandParams->infoLen = sizeof(uint8_t);
return HpkeExpandEccPriKey(pkey, kdfCtx, kemIndex, expandParams, sk, skLen);
}
}
static int32_t HpkeDeriveKeyPair(uint8_t kemIndex, uint8_t *ikm, uint32_t ikmLen,
CRYPT_EAL_PkeyCtx **pctx, CRYPT_EAL_LibCtx *libCtx, const char *attrName)
{
uint8_t suiteId[HPKE_KEM_SUITEID_LEN];
HpkeGenerateKemSuiteId(kemIndex, suiteId, HPKE_KEM_SUITEID_LEN);
uint8_t dkpPrk[HPKE_HKDF_MAX_EXTRACT_KEY_LEN];
uint8_t sk[HPKE_KEM_MAX_PRIVATE_KEY_LEN] = { 0 };
uint32_t dkpPrkLen = g_hpkeKemAlgInfo[kemIndex].hkdfExtractKeyLen;
CRYPT_MAC_AlgId macId = g_hpkeKemAlgInfo[kemIndex].macId;
uint32_t skLen = g_hpkeKemAlgInfo[kemIndex].privateKeyLen;
CRYPT_EAL_KdfCTX *kdfCtx = NULL;
kdfCtx = CRYPT_EAL_ProviderKdfNewCtx(libCtx, CRYPT_KDF_HKDF, attrName);
if (kdfCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_FAILED_FETCH_KDF);
return CRYPT_HPKE_FAILED_FETCH_KDF;
}
CRYPT_EAL_PkeyCtx *pkey = NULL;
int32_t ret = HpkeCreatePkeyCtx(kemIndex, &pkey, libCtx, attrName);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_KdfFreeCtx(kdfCtx);
return ret;
}
HPKE_LabeledExtractParams extractParams = {macId, (uint8_t *)"", 0, (uint8_t *)"dkp_prk", strlen("dkp_prk"),
ikm, ikmLen, suiteId, HPKE_KEM_SUITEID_LEN};
HPKE_LabeledExpandParams expandParams = {macId, dkpPrk, dkpPrkLen, (uint8_t *)"sk", strlen("sk"), (uint8_t *)"", 0,
suiteId, HPKE_KEM_SUITEID_LEN};
ret = HpkeLabeledExtract(kdfCtx, &extractParams, dkpPrk, dkpPrkLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = DeriveSk(kemIndex, kdfCtx, pkey, &expandParams, sk, skLen);
if (ret != CRYPT_SUCCESS) {
goto EXIT;
}
ret = HpkeCreatePriKey(kemIndex, sk, skLen, &pkey, libCtx, attrName);
EXIT:
CRYPT_EAL_KdfFreeCtx(kdfCtx);
BSL_SAL_CleanseData(sk, skLen);
BSL_SAL_CleanseData(dkpPrk, HPKE_HKDF_MAX_EXTRACT_KEY_LEN);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_PkeyFreeCtx(pkey);
return ret;
}
*pctx = pkey;
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeGenerateKeyPair(CRYPT_EAL_LibCtx *libCtx, const char *attrName,
CRYPT_HPKE_CipherSuite cipherSuite, uint8_t *ikm, uint32_t ikmLen, CRYPT_EAL_PkeyCtx **pctx)
{
if (pctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (*pctx != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
uint8_t kemIndex;
int32_t ret = HpkeCheckCipherSuite(&cipherSuite, &kemIndex, NULL, NULL);
if (ret != CRYPT_SUCCESS) {
return ret;
}
uint32_t ikmNewLen = g_hpkeKemAlgInfo[kemIndex].privateKeyLen;
if (ikm != NULL && ikmLen != 0) {
if (ikmLen < ikmNewLen) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
return HpkeDeriveKeyPair(kemIndex, ikm, ikmLen, pctx, libCtx, attrName);
}
uint8_t ikmNew[HPKE_KEM_MAX_PRIVATE_KEY_LEN];
ret = CRYPT_EAL_RandbytesEx(libCtx, ikmNew, ikmNewLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ret = HpkeDeriveKeyPair(kemIndex, ikmNew, ikmNewLen, pctx, libCtx, attrName);
BSL_SAL_CleanseData(ikmNew, ikmNewLen);
return ret;
}
int32_t CRYPT_EAL_HpkeExportSecret(CRYPT_EAL_HpkeCtx *ctx, uint8_t *info, uint32_t infoLen, uint8_t *key,
uint32_t keyLen)
{
if (ctx == NULL || key == NULL || keyLen == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->exporterSecret == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if ((info == NULL && infoLen != 0) || (info != NULL && infoLen == 0)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
if (keyLen > 255 * g_hpkeKdfAlgInfo[ctx->kdfIndex].hkdfExtractKeyLen) { // RFC9180 5.3 max L is 255*Nh
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
uint8_t suiteId[HPKE_HPKE_SUITEID_LEN];
HpkeGenerateHpkeSuiteId(ctx->kemIndex, ctx->kdfIndex, ctx->aeadIndex, suiteId, HPKE_HPKE_SUITEID_LEN);
CRYPT_MAC_AlgId macId = g_hpkeKdfAlgInfo[ctx->kdfIndex].macId;
HPKE_LabeledExpandParams params = {macId, ctx->exporterSecret, ctx->exporterSecretLen, (uint8_t *)"sec",
strlen("sec"), info, infoLen, suiteId, HPKE_HPKE_SUITEID_LEN};
return HpkeLabeledExpand(ctx->kdfCtx, ¶ms, key, keyLen);
}
int32_t CRYPT_EAL_HpkeGetSharedSecret(CRYPT_EAL_HpkeCtx *ctx, uint8_t *buff, uint32_t *buffLen)
{
if (ctx == NULL || buffLen == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->sharedSecret == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (buff == NULL) {
*buffLen = ctx->sharedSecretLen;
return CRYPT_SUCCESS;
}
if (*buffLen < ctx->sharedSecretLen) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
(void)memcpy_s(buff, *buffLen, ctx->sharedSecret, ctx->sharedSecretLen);
*buffLen = ctx->sharedSecretLen;
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeSetSharedSecret(CRYPT_EAL_HpkeCtx *ctx, uint8_t *info, uint32_t infoLen,
uint8_t *buff, uint32_t buffLen)
{
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->sharedSecret != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (buff == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if ((info == NULL && infoLen != 0) || (info != NULL && infoLen == 0)) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
if (buffLen != g_hpkeKemAlgInfo[ctx->kemIndex].sharedKeyLen) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return CRYPT_INVALID_ARG;
}
if (ctx->mode == CRYPT_HPKE_MODE_PSK || ctx->mode == CRYPT_HPKE_MODE_AUTH_PSK) {
if (ctx->authInfo == NULL || ctx->authInfo->psk == NULL || ctx->authInfo->pskId == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
}
int32_t ret = HpkeKeySchedule(ctx, buff, buffLen, info, infoLen);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ctx->sharedSecret = BSL_SAL_Dump(buff, buffLen);
if (ctx->sharedSecret == NULL) {
HpkeFreeKeyInfo(ctx);
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ctx->sharedSecretLen = buffLen;
HpkeFreeAuthInfo(ctx); // Derived key successfully, no longer requires authinfo
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeSetPsk(CRYPT_EAL_HpkeCtx *ctx, uint8_t *psk, uint32_t pskLen, uint8_t *pskId, uint32_t pskIdLen)
{
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->mode != CRYPT_HPKE_MODE_PSK && ctx->mode != CRYPT_HPKE_MODE_AUTH_PSK) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->authInfo == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->authInfo->psk != NULL || ctx->authInfo->pskId != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
// psk and pskId must appear together
if (psk == NULL || pskIdLen == 0 || pskId == NULL || pskLen == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
ctx->authInfo->psk = BSL_SAL_Dump(psk, pskLen);
if (ctx->authInfo->psk == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ctx->authInfo->pskLen = pskLen;
ctx->authInfo->pskId = BSL_SAL_Dump(pskId, pskIdLen);
if (ctx->authInfo->pskId == NULL) {
BSL_SAL_ClearFree(ctx->authInfo->psk, ctx->authInfo->pskLen);
ctx->authInfo->psk = NULL;
ctx->authInfo->pskLen = 0;
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return CRYPT_MEM_ALLOC_FAIL;
}
ctx->authInfo->pskIdLen = pskIdLen;
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeSetAuthPriKey(CRYPT_EAL_HpkeCtx *ctx, CRYPT_EAL_PkeyCtx *pkey)
{
if (ctx == NULL || pkey == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->mode != CRYPT_HPKE_MODE_AUTH && ctx->mode != CRYPT_HPKE_MODE_AUTH_PSK) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->role != CRYPT_HPKE_SENDER) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->authInfo == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->authInfo->authPkey != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
CRYPT_EAL_PkeyCtx *skS = NULL;
#ifdef HITLS_CRYPTO_PROVIDER
skS = CRYPT_EAL_ProviderPkeyNewCtx(ctx->libCtx, g_hpkeKemAlgInfo[ctx->kemIndex].pkeyId,
CRYPT_EAL_PKEY_EXCH_OPERATE, ctx->attrName);
#else
skS = CRYPT_EAL_PkeyNewCtx(g_hpkeKemAlgInfo[ctx->kemIndex].pkeyId);
#endif
if (skS == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_FAILED_FETCH_PKEY);
return CRYPT_HPKE_FAILED_FETCH_PKEY;
}
int32_t ret = CRYPT_EAL_PkeyCopyCtx(skS, pkey);
if (ret != CRYPT_SUCCESS) {
CRYPT_EAL_PkeyFreeCtx(skS);
return ret;
}
ctx->authInfo->authPkey = skS;
return CRYPT_SUCCESS;
}
int32_t CRYPT_EAL_HpkeSetAuthPubKey(CRYPT_EAL_HpkeCtx *ctx, uint8_t *pub, uint32_t pubLen)
{
if (ctx == NULL || pub == NULL || pubLen == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (ctx->mode != CRYPT_HPKE_MODE_AUTH && ctx->mode != CRYPT_HPKE_MODE_AUTH_PSK) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->role != CRYPT_HPKE_RECIPIENT) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->authInfo == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
if (ctx->authInfo->authPkey != NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_HPKE_ERR_CALL);
return CRYPT_HPKE_ERR_CALL;
}
CRYPT_EAL_PkeyCtx *pkS = NULL;
int32_t ret = HpkeCreatePubKey(ctx->kemIndex, pub, pubLen, &pkS, ctx->libCtx, ctx->attrName);
if (ret != CRYPT_SUCCESS) {
return ret;
}
ctx->authInfo->authPkey = pkS;
return CRYPT_SUCCESS;
}
#endif // HITLS_CRYPTO_HPKE
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)