关于 Kyber:抗量子密码算法 Kyber 详解

一、基本概念

后量子密码学(PQC)
│
├──> 是一个领域(研究如何在“量子时代”保护数据安全)
│
└──> Kyber 是这个领域中设计出来的一个“抗量子密码算法”
        └──> Kyber 是用于加密密钥交换的算法(叫 KEM)

>后量子密码学(Post-Quantum Cryptography, PQC)

这是一个“研究领域/学科”,目标是:设计在“未来量子计算机”也无法破解的密码算法。

因为像 RSA、ECC(椭圆曲线加密)这类传统算法,如果未来有量子计算机,会被很快破解(因为 Shor 算法可以高效分解大数)。

所以现在提前研究“后量子密码学”,防止将来量子计算一来,所有通信瞬间不安全。

>抗量子密码算法(Post-Quantum Algorithms)

是后量子密码学这个领域研究出来的“成果”之一,也叫:

  • 抗量子密码算法

  • 后量子密码算法

  • 有时简称“PQC算法”

它是具体的算法,用来实现:

  • 密钥交换

  • 数字签名

  • 加密传输

而这些算法的设计目标就是:在量子计算机出现后依然安全。

常见的有:

算法类型算法名称
密钥交换 / 加密Kyber, NTRU, FrodoKEM
签名算法Dilithium, Falcon, SPHINCS+

>Kyber 是什么?

Kyber 属于后量子密码算法,一种基于格(lattice)密码学的密钥封装机制(KEM),由 NIST 选为后量子密码标准,用于安全密钥交换

你可以把它当成是“后量子密码学”这个领域里的一种技术实现,目标是:

  • 在量子时代 安全地协商出对称密钥(例如 HTTPS 用的 AES 密钥)

Kyber 有以下几个等级:

名称安全级别(对抗强度)
Kyber512轻量级安全(类比 AES-128)
Kyber768中等安全
Kyber1024高强度安全(类比 AES-256)
  • 不是加密消息,而是用于共享对称密钥(适合 TLS、VPN、SSH 等应用)

  • 安全性基于数学难题:学习带噪声问题(LWE) 的模块变种

  • 实现快速、高效、参数可扩展


二、Kyber 使用流程(API)

Kyber 的典型使用涉及三个 API:

int crypto_kem_keypair(unsigned char *pk, unsigned char *sk);
int crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk);
int crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);
步骤函数说明
1crypto_kem_keypair()生成公钥 + 私钥
2crypto_kem_enc()封装密钥:用公钥生成密文 ct + 对称密钥 ss
3crypto_kem_dec()解封装密钥:用私钥解密 ct 得到密钥 ss

三、数学原理

Kyber 的加密基于以下数学结构:

1)模数与多项式

  • 所有计算都在有限环中进行:
    $Z_q[X]/(X^n + 1)$

  • 常用参数:$q = 3329$,$n = 256$,即多项式系数范围 [0, 3328]

2)模式(Module)LWE

Kyber 不是普通 LWE,而是模块化版本:Module-LWE(模块学习带噪声)

  • 模块结构:A 是 $k \times k$ 多项式矩阵,s 是多项式向量

  • 密文结构:一对多项式向量(u, v)


四、核心结构与流程详解

我们用 Kyber768 为例。

1)crypto_kem_keypair

步骤:

  1. 随机生成种子 seed

  2. 生成公共矩阵 $A$,秘密向量 $s$ 和错误向量 $e$

  3. 计算公钥:$pk = A·s + e$

  4. 私钥保留原始 $s$,并包含 $pk$、哈希(pk)、随机值 z

对应代码:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define KYBER_K 2       // 向量维度(Kyber512=2,Kyber768=3,Kyber1024=4)
#define N 256           // 多项式长度(固定为 256)
#define Q 3329          // Kyber 模数

// --------- 多项式与多项式向量结构定义 ---------
typedef struct {
    int16_t coeffs[N]; // 多项式系数
} poly;

typedef struct {
    poly vec[KYBER_K]; // 多项式向量
} polyvec;

// --------- 简化的随机数与噪声生成 ---------
void get_noise(poly *r) {
    for (int i = 0; i < N; i++) {
        r->coeffs[i] = rand() % 10 - 5; // 简化为 [-5, 4] 区间的噪声
    }
}

void polyvec_getnoise(polyvec *v) {
    for (int i = 0; i < KYBER_K; i++) {
        get_noise(&v->vec[i]);
    }
}

// --------- 简化的矩阵 A 生成 ---------
void gen_matrix(polyvec A[KYBER_K], const uint8_t *seed) {
    srand(seed[0]); // 简化用种子第一个字节设置随机种子
    for (int i = 0; i < KYBER_K; i++) {
        for (int j = 0; j < KYBER_K; j++) {
            get_noise(&A[i].vec[j]); // 用噪声函数代替真实伪随机生成
        }
    }
}

// --------- 多项式乘加运算:N = A * s + e ---------
void poly_muladd(polyvec *res, polyvec A[KYBER_K], polyvec *s, polyvec *e) {
    for (int i = 0; i < KYBER_K; i++) {
        for (int j = 0; j < N; j++) {
            res->vec[i].coeffs[j] = e->vec[i].coeffs[j];
            for (int k = 0; k < KYBER_K; k++) {
                res->vec[i].coeffs[j] += A[i].vec[k].coeffs[j] * s->vec[k].coeffs[j];
                res->vec[i].coeffs[j] %= Q;
            }
        }
    }
}

// --------- 打包函数(这里只做打印模拟) ---------
void pack(polyvec *pk) {
    printf("Public Key:\n");
    for (int i = 0; i < KYBER_K; i++) {
        for (int j = 0; j < 8; j++) { // 只打印前8个系数
            printf("%d ", pk->vec[i].coeffs[j]);
        }
        printf("...\n");
    }
}

// --------- 存储函数(这里也做打印) ---------
void store(polyvec *s, const char *name) {
    printf("Stored %s (first 8 coeffs):\n", name);
    for (int j = 0; j < 8; j++) {
        printf("%d ", s->vec[0].coeffs[j]);
    }
    printf("...\n");
}

// --------- 主函数:执行密钥生成 ---------
int main() {
    polyvec s, e, pk;
    polyvec A[KYBER_K];

    uint8_t seed[32] = {42}; // 模拟种子值

    gen_matrix(A, seed);             // 生成公共矩阵 A
    polyvec_getnoise(&s);            // 私钥 s 加噪声
    polyvec_getnoise(&e);            // 误差项 e 加噪声
    poly_muladd(&pk, A, &s, &e);     // pk = A * s + e
    pack(&pk);                       // 打包公钥(这里只是打印)
    store(&s, "sk");                 // 存储私钥(这里只是打印)

    return 0;
}

2)crypto_kem_enc

  • 输入公钥 pk

  • 生成临时随机密钥 m

  • 哈希 m 与 pk 得种子

  • 生成 ephemerals:sp, e1, e2

  • 计算密文:

    • $u = A^T · sp + e1$

    • $v = pk^T · sp + e2 + m·(q/2)$

  • 输出密文 + 哈希(m)

crypto_kem_enc 核心实现(对应 Kyber 规范):

#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "params.h"
#include "poly.h"
#include "polyvec.h"
#include "indcpa.h"
#include "symmetric.h"
#include "randombytes.h"
#include "hash.h"

// 输出:ct = 密文,ss = 会话密钥,输入 pk
void crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
    uint8_t buf[2 * KYBER_SYMB];   // 存放 m || H(pk)
    uint8_t kr[2 * KYBER_SYMB];    // 存放 H(m||pk) → kr[0:32]=K, kr[32:64]=coins
    polyvec sp, pkpv, e1, at[KYBER_K];
    poly v, e2, m;
    uint8_t seed[KYBER_SYMB];

    // 1)生成临时密钥 m
    randombytes(buf, KYBER_SYMB);               // 生成随机 m(长度32字节)
    hash_h(buf, buf, KYBER_SYMB);               // m ← H(m) 做 domain separation

    // 2)拼接 m || H(pk),用于生成加密种子 coins
    hash_h(buf + KYBER_SYMB, pk, KYBER_INDCPA_PUBLICKEYBYTES); 
    hash_g(kr, buf, 2 * KYBER_SYMB);            // kr ← H(m || H(pk))

    // 3)解包公钥(提取多项式向量 pkpv)
    unpack_pk(&pkpv, seed, pk);                 // pk ← 多项式 pkpv + seed

    // 4)生成矩阵 A^T
    gen_matrix(at, seed, 1);                    // A^T ← transpose(A)

    // 5)用 kr[32:64] 生成临时私钥 sp 与误差项 e1, e2
    polyvec_getnoise_eta1(&sp, kr + KYBER_SYMB, 0);
    polyvec_getnoise_eta1(&e1, kr + KYBER_SYMB, KYBER_K);
    get_noise_eta2(&e2, kr + KYBER_SYMB, 2 * KYBER_K);

    // NTT 变换
    polyvec_ntt(&sp);
    polyvec_ntt(&pkpv);

    // 6)计算 u = A^T · sp + e1
    polyvec_pointwise_acc_montgomery(&v, &pkpv, &sp); // v = pk^T * sp
    polyvec_frommont(&e1);
    polyvec_add(&e1, &e1, &at[0]); // 实际实现是循环内完成 A^T·sp + e1
    polyvec_reduce(&e1); // 模 Q 归约
    pack_ciphertext(ct, &e1, &v);  // u 部分

    // 7)v = pk^T·sp + e2 + m*(q/2)
    poly_frommsg(&m, buf);                // m → 多项式 m(位乘 q/2)
    poly_add(&v, &v, &e2);                // v += e2
    poly_add(&v, &v, &m);                 // v += m·(q/2)
    poly_reduce(&v);
    pack_ciphertext(ct, &e1, &v);         // v 部分合并进 ct

    // 8)会话密钥 ss = H(K || H(ciphertext))
    hash_h(kr + KYBER_SYMB, ct, KYBER_CIPHERTEXTBYTES); // kr[32:64] = H(ct)
    kdf(ss, kr, 2 * KYBER_SYMB);                        // ss = KDF(K || H(ct))
}

3)crypto_kem_dec

  • 输入密文 + sk,恢复 s

  • 解密计算:

    • 计算 $m' = v - u·s$

  • 从 $m'$ 派生共享密钥 ss'

crypto_kem_dec() 的核心解密代码:

void crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
    uint8_t buf[2 * KYBER_SYMB];   // 临时缓冲区,存储 m' || H(c)
    uint8_t kr[2 * KYBER_SYMB];    // kr = K || H(c)
    polyvec sp, bp, skpv;
    poly v, mp;
    const uint8_t *pk = sk + KYBER_INDCPA_SECRETKEYBYTES; // 从 sk 中提取出 pk
    const uint8_t *hpk = pk + KYBER_INDCPA_PUBLICKEYBYTES;

    // 1)从 sk 解包私钥 s 向量
    unpack_sk(&skpv, sk); // 恢复 polyvec s ← sk

    // 2)解包密文 ct 得到 u、v
    unpack_ciphertext(&bp, &v, ct); // u = bp, v = v

    // 3)NTT 变换
    polyvec_ntt(&bp);

    // 4)m' = v - (u ⋅ s)
    polyvec_pointwise_acc_montgomery(&mp, &skpv, &bp); // mp = u ⋅ s
    poly_invntt_tomont(&mp);                           // 逆NTT
    poly_sub(&mp, &v, &mp);                            // m' = v - u·s
    poly_reduce(&mp);                                  // 模Q规约
    poly_tomsg(buf, &mp);                              // 提取消息 m' → buf[0:32]

    // 5)拼接 m' || H(pk)
    memcpy(buf + KYBER_SYMB, hpk, KYBER_SYMB);         // 拼接 H(pk)

    // 6)哈希派生 kr = H(m' || H(pk))
    hash_g(kr, buf, 2 * KYBER_SYMB);                   // kr = K || coins

    // 7)计算 H(ct) 加入 kr 中
    hash_h(kr + KYBER_SYMB, ct, KYBER_CIPHERTEXTBYTES); // kr[32:64] = H(c)

    // 8)派生共享密钥 ss = KDF(kr)
    kdf(ss, kr, 2 * KYBER_SYMB);                       // ss ← H(K || H(c))
}

五、源码结构分析(以 PQClean 为例)

路径

PQClean/crypto_kem/kyber768/clean/
├── api.h                // 接口声明
├── kem.c                // 封装主流程(keypair, enc, dec)
├── indcpa.c / .h        // CPA-secure加密流程(核心)
├── poly.c / .h          // 多项式运算实现
├── ntt.c                // 快速傅里叶变换(NTT)
├── randombytes.c        // 随机数生成
├── verify.c             // 常量时间比较等

六、逆向分析建议

目标识别

项目逆向技巧
crypto_kem_keypairGhidra 查找调用伪随机函数,识别 spk 生成
crypto_kem_enc重点分析 polyvec 结构、模运算、hash 种子生成
crypto_kem_dec跟踪 v - u·s 运算,是否有解密中间态泄露
随机数源Hook randombytes,可伪造 predictable key

Frida 示例:Hook 密钥生成

Interceptor.attach(Module.findExportByName(null, "crypto_kem_keypair"), { // Hook 到导出函数 crypto_kem_keypair 的地址
    onEnter(args) { // 函数执行前触发,args 是参数数组
        console.log("keypair called"); // 输出日志,表示函数开始执行
    },
    onLeave(retval) { // 函数执行结束后触发,retval 是返回值
        console.log("keypair done"); // 输出日志,表示函数执行完毕
    }
});

IDA/Ghidra 静态分析:

  • 搜索 CRYPTO_PUBLICKEYBYTES,定位缓冲区分配

  • 查找 NTT 函数(多使用查表或优化运算)


七、实现中可能存在的风险点

风险类型描述
随机数问题伪随机数不安全可被预测
缓冲区未擦除私钥残留在内存中
错误信息可侧信道攻击不同错误路径返回不同信息量
非常量时间操作对输入密文不同响应时间,导致 side-channel 攻击
缓存攻击模式化访存、FFT变换泄露私钥结构

八、总结重点

解释
算法分类Kyber 是 lattice-based,加密目标是密钥,不是数据
模数操作所有乘法/加法在模 q = 3329 上进行
多项式向量核心运算单位是 polyvec,即多个多项式组成的向量
安全来源基于 Module-LWE 难题,不怕量子算法(Shor/Grover)
实战方向TLS握手、Frida hook、NTT逆向、种子分析、内存提取
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值