我目前正在为需要 AES 加密的文件系统目的进行一些内核开发,并且遇到以下限制:
- 我必须能够加密任何长度的明文
- 填充(如果有)不明显
- 填充字节可能导致的开销是不可接受的
在纸上,有一个简单的方法可以解决这个问题:使用 CTR 加密模式!
有了这个绝妙的想法(嗯……),我很高兴地深入研究 Linux 内核的加密 API 源来学习如何开始。
在这一点上,我注意到加密功能涉及杂项功能的使用:
- crypt_inplace函数。其目的是处理用户想要将密文存储在与给定明文相同的内存区域中的情况。(与crypt_segment相同,但有内存限制)
- crypt_segment函数。它是标准的加密函数。它对整个数据块(AES 为 16 个字节)进行加密。
- crypt_final函数。当给定明文的长度L不是底层分组密码 blocksize 的倍数时,此函数对剩余字节执行加密。
因此,使用我们的L字节长明文和 AES,前L/16块根据请求使用crypt_segment或crypt_inplace进行处理。然后使用crypt_final对剩余的L mod 16个字节进行加密。
内部crypt_segment函数定义如下:(crypt_inplace非常相似)
static int crypto_ctr_crypt_segment(struct blkcipher_walk *walk,
struct crypto_cipher *tfm)
{
void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
crypto_cipher_alg(tfm)->cia_encrypt;
unsigned int bsize = crypto_cipher_blocksize(tfm);
u8 *ctrblk = walk->iv;
u8 *src = walk->src.virt.addr;
u8 *dst = walk->dst.virt.addr;
unsigned int nbytes = walk->nbytes;
do {
/* create keystream */
fn(crypto_cipher_tfm(tfm), dst, ctrblk);
crypto_xor(dst, src, bsize);
/* increment counter in counterblock */
crypto_inc(ctrblk, bsize);
src += bsize;
dst += bsize;
} while ((nbytes -= bsize) >= bsize);
return nbytes;
}
如您所见,计数器以块大小为基础递增。在大多数用例中,这不是问题,但请考虑这种情况:
- 在给定位置p写入请求(例如,这可以是硬盘驱动器上的(扇区、偏移)特定值):11 字节长的明文
- 在p+3处读取 8 个字节的请求:这里,我们希望从写入的密文中取回初始明文的最后 8 个字节。
第一步将通过在加密序列中调用crypt_final来完成。然后,将 11 个加密字节写入正确的位置。但是当我们想要检索这块数据的最后 8 个字节时,由于“块范围”加密,我们需要存储在p、p+1和p+2中的前 3 个字节来执行解密操作。
显然,从文件系统的角度来看,当请求读取时,如果内核不做出一些与硬件相关的假设,就没有办法知道这种事情。
因此,这是我的问题:有没有办法将 CTR 模式设置为始终以字节为基础执行(解密)操作,或者我应该创建自己的 CTR 模式实现来强制执行此操作?(我在 ctr 源代码中没有找到任何入口点来做这个配置操作,我可能漏掉了什么)
在此先感谢,我希望我没有用这个大帖子惊呆你!
PS:这篇文章中的代码片段可以在 Linux Kernel 源代码树的 crypto 目录下的 ctr.c 文件中找到。显示的版本来自 3.8-rc3 内核版本。
编辑 :
实际上,CTR 模式旨在处理任意长度的数据。我会回忆起 ISO/IEC 10116 规范中的描述。
假设我们的明文P被分成等长的块( P i ) 0 < i < n ( j位)。
令K为加密提供的密钥,IV为计数器的初始化向量。
密文C将像明文P一样被分成块( C i ) 0 < i < n。
CTR 模式引入了一个计数器,该计数器在每个已处理的块处走动。也就是说,我们将调用用于加密(或解密)块P i(或C i
) CTR i
最后,让CTR 1 = IV
使用这些符号,计算可能如下所示:
FOR i 从 1 到 n DO
- Y = AES_encrypt( CTR i , K ) <- Y 是 16 字节长
- E = Truncate(Y, j) <- 只保留 Y 的最左边的j位
- C i = P i XOR E
- CTR i+1 = ComputeNextCTR( CTR i ) <- 通常ComputeNextCTR是一个简单的增量。
完毕
在 Linux 内核中的 CTR 版本中,此行为是通过j采用底层块密码块大小(128 位,AES)的值来强制执行的,除了最后一步,如果给定的明文没有合适的长度,则会发生截断.
我的问题是:有没有办法告诉加密 API 应用我想要的j参数?对我来说,答案似乎是“不”,所以我必须“重新发明”轮子并重新实现 CTR 模式才能获得这个额外的功能。由于我可能遗漏了一些东西,我将感谢能清楚说明这一点的人。
奖励一:如果答案是“否”,我们将非常欢迎快速了解 Crypto API的algapi如何工作(我目前正在研究这个)。
再次提前感谢。