
1、前言
近日,华为谢尔德实验室再次向openHiTLS社区贡献Classic McEliece后量子密钥封装算法(KEM)的高质量实现代码。作为全球首批同时进入NIST后量子密码标准化最终候选名单与ISO/IEC 18033-2 AMD2国际标准草案阶段的基于编码理论的算法,Classic McEliece算法基于Niederreiter密码体制与线性码技术,其安全性经过数十年密码学界验证,是应对量子计算威胁的核心KEM方案之一。此次贡献不仅标志着谢尔德实验室在后量子密码多技术路线(基于编码、基于格等)上的全面积累与技术自信,更进一步丰富了openHiTLS社区的后量子密码算法矩阵,为我国乃至全球构建安全、开放、多元的抗量子密码生态注入了新动能。
2、后量子威胁迫在眉睫:传统密码体系面临“量子崩塌”
随着量子计算技术的飞速发展,传统密码安全体系正面临前所未有的挑战。目前广泛使用的RSA、ECC等密码算法,在量子计算机面前将不再安全,甚至存在被完全破解的潜在威胁。为应对这一挑战,全球密码学界加速推进后量子密码(Post-Quantum Cryptography, PQC)迁移。其中,密钥封装机制(Key Encapsulation Mechanism, KEM)作为安全通信的基石,亟需具备抗量子能力的替代方案。
Classic McEliece正是在此背景下脱颖而出——它基于二元Goppa码的Niederreiter对偶型加密,这一被密码学界公认的NP困难问题(非确定性多项式时间困难问题)。即使在量子计算模型下,也无已知多项式时间算法能高效解决该问题,因此能保障密钥协商的长期安全性,为TLS、IPSec、TLCP协议及物联网设备通信等关键场景提供“量子安全”的密钥交换能力。
3、Classic McEliece:稳健、高效、国际认可的基于编码KEM方案
Classic McEliece是基于二元Goppa码的Niederreiter对偶型加密:其私钥主要包含二元Goppa码的生成多项式,而公钥为其混淆后的矩阵;密钥封装时,计算随机向量的伴随子(Syndrome)作为封装密文,并生成对应的封装密钥;解封装时,利用特定的高效解码算法(如针对特定线性码的快速译码器)从伴随子中恢复随机向量,从而提取封装密钥。其安全性基于校验子译码问题(Syndrome Decoding Problem),这是一个NP-困难问题。
其核心优势在于:
- 安全性根基深厚:基于数十年成熟的编码理论,攻击手段(如信息集译码、快速Walsh变换等)已被充分探索,无已知量子加速攻击方法,安全边界清晰且能保障长期可靠性;
- 运算效率优异:加密/解密过程以简单的线性代数运算(异或、二进制矩阵乘法)为主,无基于格类算法的复杂模运算或约减操作,解密速度远超基于格KEM(如ML-KEM),可适用于物联网终端、嵌入式设备等资源受限场景;
- 参数灵活适配:支持多套线性码参数组合(如` classicmceliece348864`、` classicmceliece460896`等),可在“安全强度”“密钥尺寸”“密文带宽”之间灵活权衡(小型参数适合带宽敏感场景,大型参数满足金融级高安全需求);
- 标准化地位明确:除已进入ISO/IEC 18033-2 AMD2国际标准草案阶段外,还作为NIST后量子密码标准化项目(PQC Standardization)KEM类别的最终候选算法,通过三轮严格的安全性与性能评估,同时被ETSI(欧洲电信标准协会)、IETF(互联网工程任务组)等国际组织纳入PQC技术清单,是国际密码学界公认的抗量子备选方案。
4、openHiTLS社区:构建开放、多元、可信的后量子密码生态
openHiTLS作为由西安电子科技大学、山东大学、上海交通大学、华为等15家产学研机构联合发起的密码开源社区,始终致力于打造“开放、创新、安全”的密码技术底座。在后量子密码领域,社区已实现多项里程碑:
- 全面支持NIST标准化算法:ML-KEM、ML-DSA、SLH-DSA、XMSS均已开源并完成交叉验证;
- 推动国产算法创新:搭建“密码创新仓(PQCP)”特色平台,既为Scloud+等国产PQC算法提供从理论到实践的转化通道,也通过社区协作反哺算法优化,加速国产密码技术产业化进程;
- 强化实现质量保障:通过向量测试、压力测试、FUZZ测试、形式化验证等多维手段,确保算法实现安全可靠;
- 完善算法矩阵:社区此前已引入基于格路线且同样进入ISO/IEC 18033-2 AMD2标准草案的FrodoKEM,此次Classic McEliece的加入,进一步填补了“基于编码理论的后量子KEM算法”方向的空白,与FrodoKEM形成“基于格+基于编码”的ISO/IEC标准算法双支撑。
展望未来,openHiTLS还将持续推进更多国产创新、NIST及ISO/IEC后量子密码算法的开源实现,推动后量子算法与传统密码体系的融合演进。
5、结语:共迎后量子时代,安全始于开源
量子计算的浪潮不可逆转,密码迁移的窗口正在收窄。华为谢尔德实验室向openHiTLS开源社区贡献Classic McEliece,不仅是一次技术开源,更是对全球数字安全“多技术路线备份”理念的践行——通过补充基于编码算法,与既有基于格算法形成互补,为抗量子密码生态增加“双保险”。
诚邀更多开发者、研究机构与企业加入openHiTLS社区,共同参与后量子密码的实现、验证与应用,为构建一个智能、安全、可靠的数字世界贡献力量。
附、查看Classic McEiliece源码
详见:https://gitcode.com/openHiTLS/pqcp/tree/main/src/classic_mceliece/src
/*
* 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 "mceliece_kem.h"
static CRYPT_ERROR GenDeltaFromSeed(uint8_t *delta)
{
/* McElieceRandombytesInit copies entropyInput into seedMaterial and optionally x-ors a personalization string.
* The copied seedMaterial is then fed to McElieceAES256CTRDrbgUpdate to initialise the global DRBG state
* (Key, V, reseed_counter). Hence entropyInput is the raw entropy source that bootstraps the whole DRBG. */
uint8_t entropyInput[MCELIECE_SEED_BYTES];
if (CRYPT_EAL_Randbytes(entropyInput, MCELIECE_SEED_BYTES) != PQCP_SUCCESS)
{
return PQCP_MCELIECE_KEYGEN_FAIL;
}
uint8_t kgSeed[33]; // Total buffer length for key-generation seed: 1-byte length prefix + 32-byte random
CRYPT_ERROR ret = McElieceRandomBytesInit((uint8_t *)entropyInput, NULL, 256); // Security strength (in bits) requested from the DRBG during seed generation
if (ret != PQCP_SUCCESS)
{
return ret;
}
/* kgSeed[0] is the length byte that Classic McEliece hard-codes to 64 (0x40) so that the later Expand-And-Split step
* produces the correct number of field elements for the public key generation; any other value would break the
* deterministic key schedule */
kgSeed[0] = 64; // the value of first element must be 64
ret = McElieceRandomBytes(kgSeed + 1, MCELIECE_L_BYTES);
if (ret != PQCP_SUCCESS)
{
return ret;
}
memcpy_s(delta, MCELIECE_L_BYTES, kgSeed + 1, MCELIECE_L_BYTES);
return PQCP_SUCCESS;
}
// KeyGen
CRYPT_ERROR McElieceKeygen(CMPublicKey *pk, CMPrivateKey *sk, const McelieceParams *params)
{
if (pk == NULL || sk == NULL)
{
return PQCP_NULL_INPUT;
}
uint8_t delta[MCELIECE_L_BYTES];
CRYPT_ERROR ret = GenDeltaFromSeed(delta);
if (ret != PQCP_SUCCESS)
{
return ret;
}
return SeededKeyGen(delta, pk, sk, params);
}
CRYPT_ERROR McElieceKeygenSemi(CMPublicKey *pk, CMPrivateKey *sk, const McelieceParams *params)
{
if (pk == NULL || sk == NULL)
{
return PQCP_NULL_INPUT;
}
uint8_t delta[MCELIECE_L_BYTES];
CRYPT_ERROR ret = GenDeltaFromSeed(delta);
if (ret != PQCP_SUCCESS)
{
return ret;
}
return SeededKeyGenSemi(delta, pk, sk, params);
}
// gen e & encode
static CRYPT_ERROR GenVectorE(uint8_t *c, uint8_t *e, const CMPublicKey *pk, const McelieceParams *params)
{
CRYPT_ERROR ret = FixedWeightVector(e, params);
if (ret != PQCP_SUCCESS)
{
return PQCP_MCELIECE_KEYGEN_FAIL;
}
memset_s(c, params->mtBytes, 0, params->mtBytes);
EncodeVector(e, &pk->matT, c, params);
return PQCP_SUCCESS;
}
// K = Hash(1, e, C)
static void ComputeSessionKey(uint8_t *sessionKey, const uint8_t *e, const uint8_t *c, const McelieceParams *params)
{
size_t inLen = 1 + params->nBytes + params->cipherBytes;
uint8_t hashIn[inLen];
hashIn[0] = 1;
memcpy_s(hashIn + 1, params->nBytes, e, params->nBytes);
memcpy_s(hashIn + 1 + params->nBytes, params->cipherBytes, c, params->cipherBytes);
McElieceShake256(sessionKey, MCELIECE_L_BYTES, hashIn, inLen);
}
// Encap algorithm (non-pc parameter sets)
CRYPT_ERROR McElieceEncaps(uint8_t *ciphertext, const CMPublicKey *pk, uint8_t *sessionKey, const McelieceParams *params)
{
if (pk == NULL || ciphertext == NULL || sessionKey == NULL)
{
return PQCP_NULL_INPUT;
}
uint8_t *e = (uint8_t *)BSL_SAL_Malloc(params->nBytes);
if (e == NULL)
{
return PQCP_MALLOC_FAIL;
}
CRYPT_ERROR ret = GenVectorE(ciphertext, e, pk, params);
if (ret != PQCP_SUCCESS)
{
return ret;
}
ComputeSessionKey(sessionKey, e, ciphertext, params);
BSL_SAL_CleanseData(e, params->nBytes);
BSL_SAL_FREE(e);
return PQCP_SUCCESS;
}
CRYPT_ERROR McElieceEncapsPC(uint8_t *ciphertext, const CMPublicKey *pk, uint8_t *sessionKey, const McelieceParams *params)
{
if (pk == NULL || ciphertext == NULL || sessionKey == NULL)
{
return PQCP_NULL_INPUT;
}
uint8_t *c0 = ciphertext;
uint8_t *c1 = ciphertext + params->cipherBytes;
uint8_t *e = (uint8_t *)BSL_SAL_Malloc(params->nBytes);
if (e == NULL)
{
return PQCP_MALLOC_FAIL;
}
CRYPT_ERROR ret = GenVectorE(c0, e, pk, params);
if (ret != PQCP_SUCCESS)
{
return ret;
}
// PC only: C1 = H(2, e)
uint8_t hashIn[1 + MCELIECE_L_BYTES];
hashIn[0] = 2;
memcpy_s(hashIn + 1, MCELIECE_L_BYTES, e, MCELIECE_L_BYTES);
McElieceShake256(c1, MCELIECE_L_BYTES, hashIn, sizeof(hashIn));
ComputeSessionKey(sessionKey, e, c0, params);
BSL_SAL_CleanseData(e, params->nBytes);
BSL_SAL_FREE(e);
return PQCP_SUCCESS;
}
static CRYPT_ERROR BuildVectorAndDecoding(uint8_t *e, const uint8_t *c0, const CMPrivateKey *sk, const McelieceParams *params)
{
uint8_t *v = BSL_SAL_Calloc(params->nBytes, 1);
if (v == NULL)
{
return PQCP_MALLOC_FAIL;
}
for (uint32_t i = 0; i < params->mt; i++)
{
uint32_t bit = VectorGetBit(c0, i);
VectorSetBit(v, i, bit);
}
if (sk->controlbits == NULL)
{
BSL_SAL_FREE(v);
return PQCP_MCELIECE_INVALID_ARG;
}
GFElement *gfL = (GFElement *)BSL_SAL_Malloc(sizeof(GFElement) * params->n);
if (gfL == NULL)
{
BSL_SAL_FREE(v);
return PQCP_MALLOC_FAIL;
}
SupportFromCbits(gfL, sk->controlbits, params->m, params->n);
int32_t decodeSuccess;
CRYPT_ERROR ret = DecodeGoppa(v, &sk->g, gfL, e, params->nBytes, &decodeSuccess, params);
BSL_SAL_FREE(gfL);
BSL_SAL_FREE(v);
if (ret != PQCP_SUCCESS)
{
return ret;
}
if (decodeSuccess == 0)
{
memcpy_s(e, params->nBytes, sk->s, params->nBytes);
}
return PQCP_SUCCESS;
}
// K = Hash(b, e, C)
static void ComputeDecapSessionKey(uint8_t *sessionKey, uint8_t b, const uint8_t *e, const uint8_t *c, const McelieceParams *params)
{
size_t inLen = 1 + params->nBytes + params->cipherBytes;
uint8_t hashIn[inLen];
hashIn[0] = b;
memcpy_s(hashIn + 1, params->nBytes, e, params->nBytes);
memcpy_s(hashIn + 1 + params->nBytes, params->cipherBytes, c, params->cipherBytes);
McElieceShake256(sessionKey, MCELIECE_L_BYTES, hashIn, inLen);
}
// Decap algorithm (non-pc parameter sets)
CRYPT_ERROR McElieceDecaps(const uint8_t *ciphertext, const CMPrivateKey *sk, uint8_t *sessionKey, const McelieceParams *params)
{
if (ciphertext == NULL || sk == NULL || sessionKey == NULL)
{
return PQCP_NULL_INPUT;
}
uint8_t *e = (uint8_t *)BSL_SAL_Malloc(params->nBytes);
if (e == NULL)
{
return PQCP_MALLOC_FAIL;
}
CRYPT_ERROR ret = BuildVectorAndDecoding(e, ciphertext, sk, params);
uint8_t b = (ret == PQCP_SUCCESS) ? 1 : 0; // If e = ⊥, set b <- 0
ComputeDecapSessionKey(sessionKey, b, e, ciphertext, params);
BSL_SAL_CleanseData(e, params->nBytes);
BSL_SAL_FREE(e);
return PQCP_SUCCESS;
}
CRYPT_ERROR McElieceDecapPC(const uint8_t *ciphertext, const CMPrivateKey *sk, uint8_t *sessionKey, const McelieceParams *params)
{
if (ciphertext == NULL || sk == NULL || sessionKey == NULL)
{
return PQCP_NULL_INPUT;
}
const uint8_t *c0 = ciphertext;
const uint8_t *c1 = ciphertext + params->cipherBytes;
uint8_t *e = (uint8_t *)BSL_SAL_Malloc(params->nBytes);
if (e == NULL)
{
return PQCP_MALLOC_FAIL;
}
CRYPT_ERROR ret = BuildVectorAndDecoding(e, c0, sk, params);
// PC only: verify C1
uint8_t hashIn[1 + MCELIECE_L_BYTES];
hashIn[0] = 2;
memcpy_s(hashIn + 1, MCELIECE_L_BYTES, e, MCELIECE_L_BYTES);
uint8_t c1Prime[MCELIECE_L_BYTES];
McElieceShake256(c1Prime, MCELIECE_L_BYTES, hashIn, sizeof(hashIn));
uint8_t b = (ret == PQCP_SUCCESS) && (memcmp(c1Prime, c1, MCELIECE_L_BYTES) == 0) ? 1 : 0; // If e = ⊥ or C' != C1, set b <- 0
ComputeDecapSessionKey(sessionKey, b, e, ciphertext, params);
BSL_SAL_CleanseData(e, params->nBytes);
BSL_SAL_FREE(e);
return PQCP_SUCCESS;
}

60

被折叠的 条评论
为什么被折叠?



