引言

Linux 内核 Crypto 子系统是众多基础设施的基石——从磁盘加密(dm-crypt/LUKS)、VPN(WireGuard/IPsec)、安全通信(TLS 内核层)、文件系统加密(fscrypt)到硬件安全模块(HSM)驱动。然而,大多数开发者对这一庞大而精妙的子系统知之甚少。本文将从 Crypto API 架构设计、同步/异步操作模式、核心加密模板、dm-crypt 实战部署、WireGuard 内核加密流水线以及硬件加速(Intel QAT/ARM CE)六个维度,带你全面掌握 Linux 内核加密的实现原理与工程实践。

一、Crypto 子系统架构总览

1.1 分层设计模型

Linux Crypto 采用清晰的四层架构,每层职责明确:

层级说明实例
算法层具体算法实现(明文、驱动层)aes-generic, chacha20-generic
模板层算法组合模式aes-cbc, xts(aes), gcm(aes)
接口层内核模块/用户态入口crypto_alg API, AF_ALG, cryptouser
驱动层硬件加速器驱动qat_c62x, caam, ccree

1.2 核心数据结构关系

struct crypto_alg 是所有算法的基类,通过操作码(cra_blocksize、cra_flags)描述能力。具体操作通过 struct crypto_tfm(转换上下文)展开,其中包含算法专属的 crt_flags 和私有数据。模板算法(如 cbc(aes))通过 crypto_alloc_base 组合底层算法(aes)和模式(cbc)。

1.3 算法三类接口

Linux Crypto 提供了三种不同粒度的操作接口,适用于不同场景:

// 1. 底层 cipher 接口(单次操作,需自行管理 TFM)
struct crypto_cipher *tfm;
crypto_alloc_cipher("aes", 0, 0);

// 2. 高层 skcipher 接口(推荐:支持异步、Scatter-Gather)
struct crypto_skcipher *tfm;
crypto_alloc_skcipher("cbc(aes)", 0, 0);

// 3. ahash/aead 接口(认证加密原子操作)
struct crypto_aead *tfm;
crypto_alloc_aead("gcm(aes)", 0, 0);

二、同步与异步加密操作实战

2.1 同步 Cipher 接口

同步接口在调用线程中完成全部操作,适用于可睡眠上下文中较小的数据块:

#include <linux/crypto.h>
#include <linux/scatterlist.h>

static int aes_cbc_encrypt_sync(const u8 *key, unsigned int keylen,
                                 const u8 *iv, u8 *data, size_t len)
{
    struct crypto_cipher *tfm;
    int ret;
    
    tfm = crypto_alloc_cipher("aes", CRYPTO_ALG_TYPE_CIPHER, 0);
    if (IS_ERR(tfm))
        return PTR_ERR(tfm);
    
    ret = crypto_cipher_setkey(tfm, key, keylen);
    if (ret)
        goto err;
    
    crypto_cipher_crypt_one(tfm, data, iv); // 仅加密一个 block(16B)
    
    // 对长数据需逐 block 调用
    u8 tmpiv[AES_BLOCK_SIZE];
    for (size_t i = 0; i < len; i += AES_BLOCK_SIZE) {
        memcpy(tmpiv, iv, AES_BLOCK_SIZE);
        crypto_cipher_crypt_one(tfm, data + i, tmpiv);
    }

err:
    crypto_free_cipher(tfm);
    return ret;
}

2.2 异步 SKCipher 接口(生产环境首选)

异步接口支持 Scatter-Gather I/O 并通过回调通知完成,适用于高性能场景(存储加密、网络协议栈):

static int aes_gcm_encrypt_async(struct crypto_aead *tfm,
                                  const u8 *iv, struct scatterlist *sg,
                                  unsigned int assoclen)
{
    struct aead_request *req;
    struct scatterlist src[2];
    DECLARE_CRYPTO_WAIT(wait);
    int ret;

    req = aead_request_alloc(tfm, GFP_KERNEL);
    if (!req)
        return -ENOMEM;

    // 构造 AAD(Additional Authenticated Data)+ 数据源
    sg_init_table(src, 2);
    sg_set_buf(&src[0], assoc_data, assoclen);    // AAD(不计入密文)
    sg_set_buf(&src[1], plaintext, plaintext_len); // 待加密数据

    aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
                               crypto_req_done, &wait);
    aead_request_set_crypt(req, src, src, plaintext_len, iv); // in-place
    aead_request_set_ad(req, assoclen);

    ret = crypto_wait_req(crypto_aead_encrypt(req), &wait);
    
    // req->iv 中已生成新 IV / tag 位于 src 尾部
    
    aead_request_free(req);
    return ret;
}

2.3 性能对比:sync vs async

场景推荐接口原因
小数据量(<4KB),可睡眠同步 cipher/api代码简洁,无回调开销
大数据量,有 SG 链sksipher(异步)避免额外内存拷贝,支持硬件 DMA
原子认证加密(AEAD)aead 接口单次调用完成加密+MAC
原子性要求极高(网络包)sksipher + async中断上下文不能睡眠,异步是唯一选择

三、加密模板与算法组合

3.1 常用 Crypto 模板

模板描述了算法组合模式,内核自动选择最优实现(可能有硬件加速版本):

# 查看系统可用加密算法及驱动
cat /proc/crypto

# 典型输出条目:
# name         : gcm(aes)                <-- 模板 + 基础算法
# driver       : gcm-aes-aesni           <-- 实际驱动(AES-NI加速)
# module       : aesni_intel             <-- 硬件加速模块
# priority     : 400                     <-- 优先级(越高越优先选择)
# refcnt       : 5                       <-- 引用计数
# selftest     : passed                  <-- 启动时 KAT 自检状态
# internal     : no
# type         : aead
# async        : yes
# blocksize    : 1
# ivsize       : 12
# maxauthsize  : 16
# geniv        : <none>

# 模式枚举
# ecbc(aes)    - ECB(仅用于教学/测试,勿用于生产)
# cbc(aes)     - CBC 模式(需随机 IV,适合存储)
# xts(aes)     - XTS 模式(扇级加密,dm-crypt 默认)
# gcm(aes)     - GCM AEAD(流加密 + 认证,WireGuard/IPsec)
# chacha20-poly1305 - 流 AEAD(无 AES-NI 时的首选)
# ctr(aes)     - CTR 模式(可并行,适合高速场景)

3.2 算法选择决策树

生产环境算法选型指南:

  • 磁盘扇区加密:首选 xts(aes)(PLAIN64 IV,符合 IEEE 1619),256-bit 密钥
  • 网络包加密(VPN):有 AES-NI → gcm(aes);无 AES-NI → chacha20-poly1305
  • 文件名加密(fscrypt):aes-256-cts(密文窃取避免填充扩展)
  • HMAC/KDF:hmac(sha256) 用于 PBKDF2 密钥派生
  • 随机数生成:drbg_nopr_sha256 用于 /dev/urandom(内核内部)

3.3 self-test 与合规性

内核 Crypto 在启动时自动执行 Known Answer Tests(KAT)确保算法实现正确性,符合 FIPS 140-2/-3 要求。可通过 cat /proc/crypto 检查 selftest: passed 状态。FIPS 模式下(fips=1 内核启动参数),非合规算法(如 MD5用于签名、自实现的 RSA keygen)会自动拒绝。

四、dm-crypt 与 LUKS 实战部署

4.1 dm-crypt 工作原理

dm-crypt 是 device-mapper 的加密目标,将块设备层 I/O 拦截并加密。其核心公式为:密文 = Crypto(明文, key, sector_iv)。XTS 模式为每个扇区 生成 IV:IV = AES_Encrypt(sector_number, tweak_key),避免扇区内部分块重排。

4.2 LUKS2 完整部署流程

# 0. 确认内核支持
$ grep CONFIG_DM_CRYPT /boot/config-$(uname -r)
CONFIG_DDM_CRYPT=m
$ grep CONFIG_CRYPTO_XTS /boot/config-$(uname -r)
CONFIG_CRYPTO_XTS=m

# 1. 安装 cryptsetup
$ sudo apt install cryptsetup

# 2. 创建加密分区(LUKS2 格式,推荐 argon2id KDF)
$ sudo cryptsetup luksFormat     --type luks2     --cipher aes-xts-plain64     --key-size 512     --hash sha256     --pbkdf argon2id     --pbkdf-memory 1048576     --pbkdf-parallel 4     --iter-time 5000     -- sector-size 4096     /dev/nvme0n1p3

# 说明:
# --key-size 512 = 双重 256-bit 密钥(XTS 模式要求)
# --pbkdf-memory 1048576 = Argon2 内存开销 1GB
# --iter-time 5000 = PBKDF2 迭代/Argon2 time cost 5秒
# --sector-size 4096 = 4K 扇区对齐(匹配高级格式化磁盘)

# 3. 打开加密设备
$ sudo cryptsetup luksOpen /dev/nvme0n1p3 secure_data

# 4. 创建文件系统并挂载
$ sudo mkfs.ext4 /dev/mapper/secure_data
$ sudo mount /dev/mapper/secure_data /mnt/secure_data

# 5. 关闭设备
$ sudo umount /mnt/secure_data
$ sudo cryptsetup luksClose secure_data

4.3 LUKS2 密钥槽管理与灾难恢复

# 查看头部信息(不暴露密钥)
$ sudo cryptsetup luksDump /dev/nvme0n1p3
LUKS header information
Version:        2
Epoch:          3
Metadata area:  16384 [bytes]
Keyslots area:  16744448 [bytes]
UUID:           xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Label:          (no label)
Subsystem:      (no subsystem)
Data area:      5**************0 [bytes]

Keyslots:
  0: luks2
        Key:        512 bits
        Priority:   normal
        Cipher:     aes-xts-plain64
        Cipher key: 512 bits
        PBKDF:      argon2id
        Time cost:  7
        Memory:     1048576
        Threads:    4

# 添加备用密码(多用户场景)
$ sudo cryptsetup luksAddKey /dev/nvme0n1p3     --pbkdf argon2id     --iter-time 5000

# 设置密钥槽优先级(用于双因素认证)
$ sudo cryptsetup config /dev/nvme0n1p3 --key-slot 1 --priority prefer

# 备份 LUKS 头部(灾难恢复必备)
$ sudo cryptsetup luksHeaderBackup /dev/nvme0n1p3     --header-backup-file /root/luks_header.bak

# 紧急恢复(头部损坏时)
$ sudo cryptsetup luksHeaderRestore /dev/nvme0n1p3     --header-backup-file /root/luks_header.bak

4.4 性能调优与 AES-NI 利用

# 验证 AES-NI 是否被内核使用
$ modinfo aesni_intel | grep description
driver_desc: "AES-NI + Intel SHA Extensions + AVX/AVX2 AES-GCM"

# 对比:纯软件 vs 硬件加速
# 启用 AES-NI 前后带宽对比(dm-crypt 4K sequential write)
# 禁用(仅 cbc-aes-aesni 被遮盖)~180 MB/s
# 启用 aesni_intel + xts(aes-aesni) ~1500 MB/s(提升 ~8x)

# 检测当前实际使用的算法驱动
$ sudo dmsetup table --showkeys secure_data
0 209714866 crypt aes-xts-plain64 :64:logon:cryptsetup:... 0 259:3 32

# 性能测试
$ cryptsetup benchmark -c aes-xts -s 512
# 结果示例(Intel Xeon w/ AES-NI):
# AES-XTS 512b: 3562.1 MiB/s encrypt, 3548.2 MiB/s decrypt

五、WireGuard 内核加密流水线

5.1 WireGuard 的 Crypto 选择

WireGuard 选用了三个核心原语,全部基于 Linux 内核 Crypto API:

原语算法用途特点
对称数据加密ChaCha20-Poly1305加密所有 VPN 数据包在无 AES-NI CPU 上比 AES更快
密钥协商Curve25519(X25519)握手 ECDH常数时间、抗侧信道
哈希BLAKE2s消息认证、密钥派生比 SHA-256 更快

5.2 数据包加密内核路径

WireGuard 数据包加密的端到端流程:

// WireGuard 数据包发送路径(简化)
// 1. 应用数据到达 wg_xmit()
// 2. 构造 Noise 协议头 + nonce
// 3. 通过 chacha20poly1305_encrypt() 调用内核 Crypto API
//    - struct crypto_aead *tfm = keyp->tfm (每 peer 预分配)
//    - aead_request_set_crypt() 设置 SG IO
//    - 异步回调中交付 sk_buff 给网络栈
// 4. Poly1305 tag 追加在密文尾部
// 5. 发送到 UDP 套接字

// 关键:WireGuard 为每个 peer 预分配 crypto context
// 避免每次加密都分配 TFM(约 2μs 开销)
struct noise_keypair {
    struct kref refcount;
    u8 public_key[NOISE_PUBLIC_KEY_LEN];
    
    struct crypto_aead *tfm;        // ChaCha20-Poly1305
    // 或 struct crypto_sync_shash *blake2s;  // BLAKE2s(同步)
    
    u64 nonce_counter;              // 96-bit nonce(32-bit sender counter + 64-bit replay)
    u64 creating_time;              // 用于 key rotation
};

5.3 性能分析:WireGuard vs IPsec

在相同硬件(Intel Xeon, AES-NI 可用)上进行 VPN 吞吐量对比:

# 测试环境:Linux 6.8, WireGuard 1.0.20230726 vs strongSwan (ChaCha20-Poly1305)

# WireGuard (UDP 封装)
$ iperf3 -c 10.0.0.1 -t 30
[ ID] Interval           Transfer     Bitrate
[  5]  0.00-30.00  sec  2.50 GBytes   716 Mbits/sec

# IPsec (ESP封装,相同原语)
$ iperf3 -c 10.0.0.1 -t 30
[ ID] Interval           Transfer     Bitrate
[  5]  0.00-30.00  sec  1.80 GBytes   515 Mbits/sec

# WireGuard 的优势原因:
# 1. 极简包头(32B vs IPsec ESP's ~56B)
# 2. 无初始 key exchange overhead (预协商)
# 3. 每个 packet 直接 Crypto Op,无状态机转换
# 4. 内核直接处理,无用户态↔内核态切换

六、硬件加速与 QAT 实战

6.1 Intel QAT 硬件加速

Intel QuickAssist Technology (QAT) 将对称加密、认证和压缩卸载到硬件引擎:

# 1. 确认 QAT 设备存在
$ lspci | grep -i qat
3e:00.0 Co-processor: Intel Corporation C6xx/Xeon D QAT

# 2. 加载 QAT 内核驱动
$ sudo modprobe qat_c62x
$ sudo modprobe qat_c6x

# 3. 安装 QAT 引擎(OpenSSL 可选)
$ sudo apt install qatlib-examples qatlib-service

# 4. 检查 Crypto 注册结果(驱动会自动注册到内核 Crypto)
$ grep -i qat /proc/crypto | head -20
name         : gcm(aes)
driver       : qat-gcm-aes           <-- QAT 驱动版本

# 5. 运行 QAT 自带 benchmark
$ sudo /etc/init.d/qat_service start
$ cpa_sample_code signOfIofD sign verify perf level

6.2 ARM CE(Cryptographic Extensions)

ARMv8+ CPU 内置 CE,直接在处理器流水线内完成 AES/SHA1/SHA256:

# 确认 ARM CE 可用
$ cat /proc/cpuinfo | grep -E "aes|sha|pmull"
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid

# 查看算法注册优先级(arm64 CE 驱动通常 priority 300+)
$ grep -E "aes-ce|sha1-ce|sha256-ce|ghash-ce" /proc/crypto
# 示例输出:gcm(aes) driver:gcm-aes-ce  priority:300
#         cbc(aes) driver:cbc-aes-ce  priority:300

# 实测 Raspberry Pi 4 (BCM2711, 含 AES/SHA 扩展)
$ cryptsetup benchmark -c aes-xts -s 256
# ARM CE enabled: AES-XTS 256b ~120 MB/s
# ARM CE disabled: ~45 MB/s (纯 NEON 优化)
# 纯软件: ~22 MB/s

6.3 异步引擎 vs 同步:大数据量场景

极大量数据场景下,充分利用异步 Crypto 实现 DMA+硬件流水线零等待并发:

// 批量提交 + 合并中断(NAPI 风格)
struct cryptd_batch_req {
    struct list_head batch_list;
    atomic_t count;
};

static void batch_callback(struct crypto_async_request *req, int err)
{
    struct cryptd_batch_req *batch = req->data;
    if (atomic_dec_and_test(&batch->count))
        complete(&batch->completion);  
        // 单次唤醒,减少上下文切换
}

// 提交 N 个加密请求,仅等待最后一次回调
void submit_crypto_batch(struct cryptd_batch_req *batch, 
                         struct page **pages, int n)
{
    for (int i = 0; i < n; i++) {
        aead_request_set_callback(req[i], 0, batch_callback, batch);
        crypto_aead_encrypt(req[i]);
    }
}

七、安全最佳实践与常见陷阱

7.1 密码学工程准则

  • IV/Nonce 必须不可预测:CTR/GCM 的 IV 重复会完全破坏加密(使用 get_random_bytes() 而非jiffies)
  • 永远不要 ECB 模式:ECB 不隐藏数据模式,对结构化数据(图片、文本)相当于明文保存
  • AES-256-XTS 是磁盘加密的唯一选择:XTS 扇区级 tweaking 防止跨扇区数据操纵
  • 密钥不在内存中交换到磁盘:使用 GFP_NODE + kvzalloc(),标记为 S_MEMMAP 不要 swap
  • 验证后解密:AEAD 模式下必须先完整验证 MAC,再输出任何解密数据(否则 timing oracle)

7.2 调试与监控常用技巧

# 查看 Crypto 模块使用统计
$ lsmod | grep -E "aes|sha|crypt"
aesni_intel            368640  72
aes_generic            12800   1 aesni_intel
cryptd                 24576   2
dm_crypt               40960   3

# 监控 Crypto 同步等待时间
$ perf probe crypto_alloc_tfm
$ perf stat -e probe:crypto:* -a sleep 5
# 关注 probe:crypto:crypto_wait_req 调用次数(过多说明 crypto 延迟异常)

# 查看 dm-crypt 性能指标
$ dmsetup status secure_data --noflush
0 209714866 crypt aes-xts-plain64 :64:logon:cryptsetup:...
  sector_offset = 32 sectors
  offset        = 0
  # 通过 I/O stat 观察加密开销(通常 < 5% with AES-NI)
$ iostat -xm 1 -p dm-1

# 调试自检失败
$ dmesg | grep -i "crypto.*test"
# [    0.298546] alg: self-tests for gcm(aes) (gcm-aes-aesni) passed
# 如果出现 failed → 立即停止使用该算法(可能硬件故障/驱动错误)

7.3 合规性与 FIPS 140-3 认证

FIPS 140-3 对内核 Crypto 有明确要求:

  • 启动时所有算法必须通过 Known Answer Test(KAT)
  • FIPS 模式下只允许使用 FIPS 批准的算法(AES、SHA-2/3、HMAC、RSA ≥2048bit、ECDSA P-256+)
  • 需要“模块完整性验证”(module signature verification)
  • CentOS/RHEL 9、SLES 15 SP5+ 提供 FIPS 验证的模式(fips=1 启动参数)
  • Ubuntu 22.04+ 提供自动认证的 linux-image-generic-hwe-22.04-fips 包

八、前沿展望:后量子加密在内核中的应用

8.1 PQC 算法蓄势待发

NIST 后量子标准化中选的 CRYSTALS-Kyber(KEM)和 CRYSTALS-Dilithium(签名)正逐步进入内核。目前 WireGuard 已有实验性补丁支持 X25519+Kyber-768 混合密钥交换(X-wing 方案),内核 Crypto 框架已开始集成 kyber 和 dilithium 算法模块:


// 预计会在 6.12+ 进入主线的代码结构
static struct kpp_alg kyber_768_alg = {
    .set_secret = kyber_set_secret,
    .generate_public_key = kyber_generate_public_key,
    .compute_shared_secret = kyber_compute_shared_secret,
    .maxsize = kyber_max_size,
    .base = {
        .cra_name = "kyber-768",
        .cra_driver_name = "kyber-generic",
        .cra_priority = 100,
        .cra_module = THIS_MODULE,
        .cra_ctxsize = sizeof(struct kyber_kmat),
    }
};

结语

Linux 内核 Crypto 子系统是安全基础设施的基石,从块设备加密到 VPN 协议栈再到硬件安全模块驱动,几乎涉及加密的所有场景。掌握其 API 架构、算法选型原则和性能调优方法,对于存储工程师、网络安全工程师和内核开发者都至关重要。随着后量子加密算法即将进入 WireGuard 和 dm-crypt,Crypto 子系统还将迎来新一轮能力扩展。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部