近日,华为谢尔德实验室正式向openHiTLS社区贡献FrodoKEM后量子密钥封装算法的高质量实现代码。作为全球首批进入ISO/IEC国际标准草案的后量子密码算法之一,FrodoKEM算法基于无结构格路线,其安全性得到业界广泛认可,是应对量子计算威胁的关键算法之一。此次贡献不仅标志着谢尔德实验室在后量子密码领域的深厚积累与技术自信,更进一步丰富了openHiTLS社区的后量子密码算法矩阵,为我国乃至全球构建安全、开放、多元的抗量子密码生态注入了新动能。
后量子威胁迫在眉睫:传统密码体系面临“量子崩塌”
随着量子计算技术的飞速发展,传统密码安全体系正面临前所未有的挑战。目前广泛使用的RSA、ECC等密码算法等,在量子计算机面前将不再安全,甚至存在被完全破解的潜在威胁。为应对这一挑战,全球密码学界加速推进后量子密码(Post-Quantum Cryptography, PQC)迁移。其中,密钥封装机制(Key Encapsulation Mechanism, KEM)作为安全通信的基石,亟需具备抗量子能力的替代方案。FrodoKEM正是在此背景下脱颖而出——它基于学习带误差问题Learning With Errors(LWE),这一被广泛认可的格密码难题,即使面对量子攻击,仍能保障密钥协商的安全性,为TLS、IPSec、TLCP协议等关键场景提供“量子安全”的密钥交换能力。
FrodoKEM:稳健、简洁、国际认可的格基KEM方案
FrodoKEM的核心是Learning With Errors(LWE)问题:给定矩阵A和向量b=A·s+e(e为随机误差),在噪声干扰下恢复秘密向量s的难度极高。其安全性可归约至格上的最短向量问题(SVP),目前无已知经典或量子算法能高效解决。其核心优势在于:
- 安全性坚实:基于标准LWE问题,具备可证明安全性,且参数选择保守,安全边界清晰;
- 结构简洁:算法逻辑清晰,无复杂代数结构,易于形式化验证与安全审计;
- 性能均衡:在密钥尺寸、计算开销与带宽之间取得良好平衡,适用于云、端、网多类场景;
- 标准化领先:目前已进入ISO/IEC 18033-2 AMD2国际标准草案阶段,并被纳入ETSI、IETF等多个国际组织的PQC评估清单,曾参与NIST后量子密码标准化第三轮评估,是国际密码学界公认的抗量子备选方案。
openHiTLS社区:构建开放、多元、可信的后量子密码生态
openHiTLS作为由西安电子科技大学、山东大学、上海交通大学、华为等15家产学研机构联合发起的密码开源社区,始终致力于打造“开放、创新、安全”的密码技术底座。在后量子密码领域,社区已实现多项里程碑:
- 全面支持NIST标准化算法:ML-KEM、ML-DSA、SLH-DSA、XMSS均已开源并完成交叉验证;
- 推动国产算法创新:Scloud+等国产PQC算法通过“密码创新仓(PQCP)”加速落地;
- 强化实现质量保障:通过向量测试、压力测试、FUZZ测试、形式化验证等多维手段,确保算法实现安全可靠;
- 完善算法矩阵:此次FrodoKEM的加入,填补了社区在ISO/IEC后量子密码算法方向的空白,形成“国产创新+NIST+ISO/IEC”三位一体的后量子密码能力体系。
展望未来,openHiTLS还将持续推进更多国产创新、NIST及ISO/IEC后量子密码算法的开源实现,推动后量子算法与传统密码体系的融合演进。
结语:共迎后量子时代,安全始于开源
量子计算的浪潮不可逆转,密码迁移的窗口正在收窄。华为谢尔德实验室向openHiTLS开源社区贡献FrodoKEM,不仅是一次技术开源,更是一份对全球数字安全的责任担当。
我们诚邀更多开发者、研究机构与企业加入openHiTLS社区,共同参与后量子密码的实现、验证与应用,为构建一个智能、安全、可靠的数字世界贡献力量。
立即体验
访问[openHiTLS PQCP密码创新仓库],获取FrodoKEM源码及文档,加入开发者社区共筑密码技术生态!
获取地址:https://gitcode.com/openHiTLS/pqcp
开源核心代码:
https://gitcode.com/openHiTLS/pqcp/blob/main/src/frodokem/src/frodokem.c
/*
* 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 <stdlib.h>
#include <string.h>
#include "frodo_local.h"
#include "internal/frodo_params.h"
#include "bsl_params.h"
#include "frodokem.h"
#include "securec.h"
#include "pqcp_err.h"
#include "crypt_eal_rand.h"
#include "securec.h"
int32_t FrodoKemRandombytes(uint8_t* buffer, size_t len)
{
return CRYPT_EAL_Randbytes(buffer, len);
}
int FrodoKemKeypairInternal(const uint8_t* rnd, const FrodoKemParams* params, uint8_t* pk, uint8_t* sk, size_t lenSk)
{
const uint16_t n = params->n;
const uint16_t nbar = params->nBar;
const size_t SnB = (size_t)n * nbar * sizeof(uint16_t);
const uint8_t* s = rnd;
const uint8_t* seedSE = rnd + params->ss;
const uint8_t* z = rnd + params->ss + params->lenSeedSE;
// alloc memory
uint16_t* sTranspose = (uint16_t*)malloc(SnB);
if (!sTranspose) {
goto clean;
}
uint8_t seedA[FRODOKEM_LEN_A];
if (n == 640) {
FrodoKemShake128(seedA, FRODOKEM_LEN_A, z, FRODOKEM_LEN_A);
} else {
FrodoKemShake256(seedA, FRODOKEM_LEN_A, z, FRODOKEM_LEN_A);
}
if (FrodoPkeKeygenSeeded(params, pk, sTranspose, seedA, seedSE) != 0) {
goto clean;
}
uint8_t* sk_s = sk;
uint8_t* sk_pk = sk + params->ss;
uint8_t* sk_S = sk_pk + params->pkSize;
uint8_t* sk_pkh = sk_S + SnB;
memcpy_s(sk_s, lenSk, s, params->ss);
memcpy_s(sk_pk, lenSk - params->ss, pk, params->pkSize);
memcpy_s(sk_S, lenSk - params->ss - params->pkSize, (uint8_t*)sTranspose, SnB);
if (n == 640) {
FrodoKemShake128(sk_pkh, params->lenPkHash, pk, params->pkSize);
} else {
FrodoKemShake256(sk_pkh, params->lenPkHash, pk, params->pkSize);
}
clean:
free(sTranspose);
return 0;
}
int FrodoKemKeypair(const FrodoKemParams* params, uint8_t* pk, uint8_t* sk, size_t lenSk)
{
const size_t need = (size_t)params->ss + params->lenSeedSE + params->lenSeedA;
uint8_t rnd[112] = {0};
int32_t ret = FrodoKemRandombytes(rnd, need);
if (ret != PQCP_SUCCESS) {
return ret;
}
ret = FrodoKemKeypairInternal(rnd, params, pk, sk, lenSk);
BSL_SAL_CleanseData(rnd, need);
return ret;
}
int FrodoKemEncapsInternal(const uint8_t* mu, const FrodoKemParams* params, uint8_t* ct, uint8_t* ss, const uint8_t* pk)
{
uint8_t pkh[32];
if (params->lenPkHash > sizeof(pkh)) {
return PQCP_FRODOKEM_INVALID_ARG;
}
if (params->n == 640) {
FrodoKemShake128(pkh, params->lenPkHash, pk, params->pkSize);
} else {
FrodoKemShake256(pkh, params->lenPkHash, pk, params->pkSize);
}
const size_t seedk_len = params->lenSeedSE + params->ss;
uint8_t* seedk = (uint8_t*)malloc(seedk_len);
if (!seedk) {
return PQCP_MEM_ALLOC_FAIL;
}
const size_t in_len = params->lenPkHash + params->lenMu + params->lenSalt;
uint8_t* in = (uint8_t*)malloc(in_len);
if (!in) {
free(seedk);
return PQCP_MEM_ALLOC_FAIL;
}
memcpy_s(in, in_len, pkh, params->lenPkHash);
memcpy_s(in + params->lenPkHash, in_len - params->lenPkHash, mu, params->lenMu + params->lenSalt);
if (params->n == 640) {
FrodoKemShake128(seedk, seedk_len, in, in_len);
} else {
FrodoKemShake256(seedk, seedk_len, in, in_len);
}
free(in);
uint8_t* seedSEp = seedk;
uint8_t* k = seedk + params->lenSeedSE;
if (FrodoPkeEncrypt(params, pk, mu, seedSEp, ct) != 0) {
free(seedk);
return PQCP_FRODOKEM_ENCRYPT_FAIL;
}
for (int i = 0; i < params->lenSalt; i++) {
ct[params->ctxSize - params->lenSalt + i] = mu[params->lenMu + i];
}
size_t ct_k_len = params->ctxSize + params->ss;
uint8_t* ct_k = (uint8_t*)malloc(ct_k_len);
if (!ct_k) {
free(seedk);
return PQCP_MEM_ALLOC_FAIL;
}
memcpy_s(ct_k, ct_k_len, ct, params->ctxSize);
memcpy_s(ct_k + params->ctxSize, ct_k_len - params->ctxSize, k, params->ss);
if (params->n == 640) {
FrodoKemShake128(ss, params->ss, ct_k, ct_k_len);
} else {
FrodoKemShake256(ss, params->ss, ct_k, ct_k_len);
}
free(ct_k);
free(seedk);
return 0;
}
int FrodoKemEncaps(const FrodoKemParams* params, uint8_t* ct, uint8_t* ss, const uint8_t* pk)
{
uint8_t mu[32 + 64];
int32_t ret = FrodoKemRandombytes(mu, params->lenMu + params->lenSalt);
if (ret != PQCP_SUCCESS) {
return ret;
}
ret = FrodoKemEncapsInternal(mu, params, ct, ss, pk);
BSL_SAL_CleanseData(mu, params->lenMu + params->lenSalt);
return ret;
}
int FrodoKemDecaps(const FrodoKemParams* params, uint8_t* ss, const uint8_t* ct, const uint8_t* sk)
{
const uint8_t* sk_s = sk;
const uint8_t* sk_pk = sk + params->ss;
const uint8_t* sk_S = sk_pk + params->pkSize;
const uint8_t* sk_pkh = sk_S + (params->n * params->nBar * sizeof(uint16_t));
uint8_t mu_prime[params->lenMu];
int ret = FrodoPkeDecrypt(params, sk_S, ct, mu_prime);
if (ret != 0) {
return ret;
}
size_t seed_k_len = params->lenSeedSE + params->ss;
uint8_t* seed_k_bytes_prime = (uint8_t*)malloc(seed_k_len);
if (!seed_k_bytes_prime) {
return PQCP_MEM_ALLOC_FAIL;
}
size_t pkh_mu_len = params->lenPkHash + params->lenMu + params->lenSalt;
uint8_t* pkh_mu_bytes_prime = (uint8_t*)malloc(pkh_mu_len);
if (!pkh_mu_bytes_prime) {
free(seed_k_bytes_prime);
return PQCP_MEM_ALLOC_FAIL;
}
memcpy_s(pkh_mu_bytes_prime, pkh_mu_len, sk_pkh, params->lenPkHash);
memcpy_s(pkh_mu_bytes_prime + params->lenPkHash, pkh_mu_len - params->lenPkHash, mu_prime, params->lenMu);
memcpy_s(pkh_mu_bytes_prime + params->lenPkHash + params->lenMu, pkh_mu_len - params->lenPkHash - params->lenMu,
ct + params->ctxSize - params->lenSalt,
params->lenSalt);
if (params->n == 640) {
FrodoKemShake128(seed_k_bytes_prime, seed_k_len, pkh_mu_bytes_prime, pkh_mu_len);
} else {
FrodoKemShake256(seed_k_bytes_prime, seed_k_len, pkh_mu_bytes_prime, pkh_mu_len);
}
uint8_t* seedSE_prime = seed_k_bytes_prime;
uint8_t* k_prime = seed_k_bytes_prime + params->lenSeedSE;
uint8_t* ct_prime = (uint8_t*)malloc(params->ctxSize);
if (!ct_prime) {
free(seed_k_bytes_prime);
free(pkh_mu_bytes_prime);
return PQCP_MEM_ALLOC_FAIL;
}
ret = FrodoPkeEncrypt(params, sk_pk, mu_prime, seedSE_prime, ct_prime);
if (ret != 0) {
free(seed_k_bytes_prime);
free(pkh_mu_bytes_prime);
free(ct_prime);
return ret;
}
int8_t selector = FrodoCommonCtVerify((uint16_t*)ct, (uint16_t*)ct_prime,
(params->ctxSize - params->lenSalt) / sizeof(uint16_t));
uint8_t final_k[params->ss];
FrodoCommonCtSelect(final_k, k_prime, sk_s, params->ss, selector);
size_t ct_k_len = params->ctxSize + params->ss;
uint8_t* ct_k_bytes = (uint8_t*)malloc(ct_k_len);
if (!ct_k_bytes) {
free(seed_k_bytes_prime);
free(pkh_mu_bytes_prime);
free(ct_prime);
return PQCP_MEM_ALLOC_FAIL;
}
memcpy_s(ct_k_bytes, ct_k_len, ct, params->ctxSize);
memcpy_s(ct_k_bytes + params->ctxSize, ct_k_len - params->ctxSize, final_k, params->ss);
if (params->n == 640) {
FrodoKemShake128(ss, params->ss, ct_k_bytes, ct_k_len);
} else {
FrodoKemShake256(ss, params->ss, ct_k_bytes, ct_k_len);
}
free(seed_k_bytes_prime);
free(pkh_mu_bytes_prime);
free(ct_prime);
free(ct_k_bytes);
return 0;
}
1560

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



