Linux 内核加密 API

作者:James Morris

本文简要概述了 Linux 内核的新加密 API。它面向对 Linux 感兴趣的技术人员,例如系统管理员,以及其他好奇的人,他们希望深入了解 API 的设计、实现和应用。对内核内部原理的一些了解是有用的,但对于广泛理解 API 概念来说并非必不可少。

这个 API 的历史很短。在 2002 年万圣节内核功能冻结之前不久,Dave Miller 和 Alexey Kuznetzov 开发的 IPSec 实现被计划包含到 2.6 内核中。IPSec 需要内核内的加密支持,这以及对内核加密日益增长的普遍需求,促使了新的加密 API 的开发。

设计

虽然最初旨在支持 IPSec,但该 API 被设计为通用设施,潜在应用包括加密文件、加密文件系统、强大的文件系统完整性、随机字符设备 (/dev/random)、网络文件系统安全(例如,CIFS)以及其他需要加密的内核网络服务。

一个具体的设计要求是 API 直接在页面向量上就地工作。页面是内核管理的主要内存单元。基于页面向量的 API 允许与内核子结构(例如 VFS 和网络堆栈)以及分散-聚集操作进行深度集成。在 IPSec 的情况下,加密转换可以直接应用于与网络数据包关联的不连续内存页面。

简单性是一个重要的设计目标,这通常是一个好主意,对于内核和安全代码尤其重要。

部署灵活性是另一个目标。例如,API 对算法采取灵活的策略;它们可以作为内核模块动态加载,而 API 无需事先了解任何关于它们的信息。

未来的设计目标包括

  • 对加密加速卡和具有 IPSec 卸载功能的网卡提供硬件支持。

  • 当有多种实现可用时,支持指定算法偏好,例如,优化的汇编器版本和各种硬件实现。

  • 非对称加密支持 (RSA),内核可能需要它来支持多播 IPSec 和内核模块签名验证。这可能是一个有争议的问题,因为非对称加密通常很慢且很复杂——这两者都是将其排除在内核之外的充分理由。

  • 一个统一的 API,供希望利用可用加密硬件的用户空间应用程序使用,例如 SSL、IPSec 密钥交换、安全路由协议和 DNSSec。

  • 进一步优化 API 内存占用,以满足嵌入式系统场景的需求。

算法

API 当前支持三种类型的算法

1) 摘要(单向哈希函数)——这些函数接受任意消息并生成短的、固定长度的消息摘要。为了成为单向的,哈希函数必须设计成易于生成哈希,但难以从哈希计算原始消息。出于加密目的,哈希函数需要抗碰撞,以便难以使两条消息哈希到相同的值。应用包括确保数据完整性和为网络协议生成消息认证码。摘要算法的示例包括 MD5 和 SHA1。

API 中包含一个名为 HMAC (RFC2104) 的消息认证方案,它将对任何标准摘要算法进行操作。这目前用于为 IPSec 数据包生成身份验证数据。

2) 密码——这些算法实现对称密钥加密,其中使用密钥加密明文消息以生成密文。通常,使用相同的密钥将密文解密回原始明文。使用密钥(必须保密)加密和解密消息应该很容易,但在没有密钥的情况下这样做很困难。应用包括加密数据以确保隐私和生成消息认证码。密码算法的示例包括 Triple DES、Blowfish 和 AES。

密码有两种类型:分组密码对固定长度的数据块(例如,16 字节)进行操作,而流密码使用密钥流一次操作少量至一位的数据。

密码也可以在各种模式下运行,例如电子密码本 (ECB),其中每个明文块都只是用密钥加密,以及密码块链接 (CBC),其中先前加密的块被馈送到下一个块的加密中。

3) 压缩——这通常与加密结合使用,以便更难以利用与原始明文相关的弱点,并加快加密速度(即,压缩后的明文更短)。根据定义,加密数据应该难以压缩,但这会对通常利用压缩的链路上的性能产生不利影响。在许多情况下,在加密之前压缩数据有助于减少这种性能损失。压缩算法的示例包括 LZS 和 Deflate。

到目前为止,来自知名来源的算法实现已被改编用于 API,因为它们更有可能经过审查和广泛测试。为了包含到主线内核中,算法通常必须是无专利的(例如,IDEA 在 2011 年左右之前不会成为包含的候选者),基于开放的、公认的标准,并提交一组测试向量。

页面向量

在讨论 API 结构之前,让我们简要了解一下内存页面和页面向量。如前所述,页面是内核管理的基本内存单元(在 i386 上,页面大小为 4KB)。考虑一个包含例如 1,460 字节用户空间数据的缓冲区。它属于内核中的特定页面,从页面开头偏移一定量,长度为 1,460 字节。此缓冲区可以表示为基于页面的元组

{ page, offset, length }

诸如直接使用页面的加密 API 之类的接口需要处理此元组或页面向量。使用一个现有的内核数据结构,称为 scatterlist,它包含一个页面向量,通常用于分散-聚集 DMA 操作。

加密 API 使用 scatterlist 来操作不连续页面向量的数组。内核中分散-聚集的主要目的是避免不必要的数据复制。它似乎也使代码更简洁。许多读者将熟悉分散-聚集 I/O,形式为 readv() 和 writev() 系统调用。内核加密 API 使用相同的通用概念,但操作的是页面而不是普通内存缓冲区。

API 结构

API 处理两个主要对象

  • 算法实现——包含底层算法代码的内核模块。

  • 转换——实例化算法、管理内部状态和处理通用实现逻辑的对象。转换由 crypto_alloc_tfm() 和 crypto_free_tfm() 管理。提供了一组 API 包装器来简化转换的使用,并允许查询转换的底层算法的属性。

以下伪代码演示了转换接口的典型用法,其中一些内核代码需要使用电子密码本 (ECB) 模式下的 Blowfish 密码加密数据

tfm = crypto_alloc_tfm("blowfish",
                       CRYPTO_TFM_MODE_ECB);
crypto_cipher_setkey(tfm, key, keylength);
crypto_cipher_encrypt(tfm, &scatterlist,
                      numlists);
crypto_free_tfm(tfm);

如图 1 所示,API 是分层的,以便核心逻辑对加密用户和算法实现者隐藏。此核心逻辑包括通用转换管理、scatterlist 操作和底层算法的抽象。更进一步,处理每个算法类型的逻辑,例如密码处理模式和利用摘要生成消息认证码。

The Linux Kernel Cryptographic API

图 1. 内核加密 API 的结构

算法管理层包含用于定位、加载和引用计数算法实现的逻辑。后者是必需的,以防止尝试卸载仍在使用的算法模块时发生糟糕的事情。

提供了一个算法运行时查询接口,以便调用代码可以确定系统上可用的算法。这主要用于密钥协商协议,例如 ISAKMP/IKE。

最后,算法注册接口允许模块注册一个或多个算法,指定各种属性,例如算法的名称、其块大小以及最小和最大密钥大小。当前注册的算法及其属性列表可以在 /proc/crypto 中查看。

结论

这仍然是一个年轻的 API,很可能会有所发展,特别是如果此处列出的一些未来设计目标得以实现。

就 API 用户而言,IPSec 工作正常且性能良好,特别是对于没有性能优化的第一个版本。需要加密的现有内核组件预计会随着时间的推移转换为新的 API,并且希望会因此开发出很酷的新项目。

致谢

非常感谢 David Miller 和 Nancy Chan 审阅了本文。

资源

电子邮件:jmorris@intercode.com.au

James Morris 是一位软件开发人员,参与 Netfilter、LSM、SELinux 和 Linux 内核加密 API 项目。他在澳大利亚悉尼担任独立顾问。

加载 Disqus 评论