【附源码】华为谢尔德实验室再献Classic McEliece后量子密码算法!openHiTLS同时布局基于编码/基于格的后量子KEM算法

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-困难问题。

其核心优势在于:

  1. 安全性根基深厚:基于数十年成熟的编码理论,攻击手段(如信息集译码、快速Walsh变换等)已被充分探索,无已知量子加速攻击方法,安全边界清晰且能保障长期可靠性;
  2. 运算效率优异:加密/解密过程以简单的线性代数运算(异或、二进制矩阵乘法)为主,无基于格类算法的复杂模运算或约减操作,解密速度远超基于格KEM(如ML-KEM),可适用于物联网终端、嵌入式设备等资源受限场景;
  3. 参数灵活适配:支持多套线性码参数组合(如` classicmceliece348864`、` classicmceliece460896`等),可在“安全强度”“密钥尺寸”“密文带宽”之间灵活权衡(小型参数适合带宽敏感场景,大型参数满足金融级高安全需求);
  4. 标准化地位明确:除已进入ISO/IEC 18033-2 AMD2国际标准草案阶段外,还作为NIST后量子密码标准化项目(PQC Standardization)KEM类别的最终候选算法,通过三轮严格的安全性与性能评估,同时被ETSI(欧洲电信标准协会)、IETF(互联网工程任务组)等国际组织纳入PQC技术清单,是国际密码学界公认的抗量子备选方案。

4、openHiTLS社区:构建开放、多元、可信的后量子密码生态

openHiTLS作为由西安电子科技大学、山东大学、上海交通大学、华为等15家产学研机构联合发起的密码开源社区,始终致力于打造“开放、创新、安全”的密码技术底座。在后量子密码领域,社区已实现多项里程碑:

  1. 全面支持NIST标准化算法:ML-KEM、ML-DSA、SLH-DSA、XMSS均已开源并完成交叉验证;
  2. 推动国产算法创新:搭建“密码创新仓(PQCP)”特色平台,既为Scloud+等国产PQC算法提供从理论到实践的转化通道,也通过社区协作反哺算法优化,加速国产密码技术产业化进程;
  3. 强化实现质量保障:通过向量测试、压力测试、FUZZ测试、形式化验证等多维手段,确保算法实现安全可靠;
  4. 完善算法矩阵:社区此前已引入基于格路线且同样进入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;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值