我在TrueCrypt(7.1a)中浏览Serpent实现,发现有些地方看起来不对劲!这是这个算法的接口:
void serpent_set_key(const unsigned __int8 userKey[], int keylen, unsigned __int8 *ks);
void serpent_encrypt(const unsigned __int8 *inBlock, unsigned __int8 *outBlock, unsigned __int8 *ks);
void serpent_decrypt(const unsigned __int8 *inBlock, unsigned __int8 *outBlock, unsigned __int8 *ks);
这里感兴趣的函数是serpent_set_key
。用户密钥长度为32字节,keylen
应为其大小,ks
是用于加密/解密的输出密钥。问题在于执行。以下是开头的相关片段:
unsigned __int32 a,b,c,d,e;
unsigned __int32 *k = (unsigned __int32 *)ks;
unsigned __int32 t;
int i;
for (i = 0; i < keylen / (int)sizeof(__int32); i++)
k[i] = LE32(((unsigned __int32*)userKey)[i]);
for循环实际上是将数据从用户密钥复制到实现密钥。这是通过将数据"视为"4字节整数来完成的。现在,如果密钥len以字节形式发送(32是正确的值),那么一切都可以,但是。。。
在trueCrypt的所有实现中,这在两个地方被调用。这是第一个:在CipherInit中这样调用:
case SERPENT:
serpent_set_key (key, CipherGetKeySize(SERPENT) * 8, ks);
break;
CipherGetKeySize(SERPENT)
将返回32(字节),因此传入的参数的值为256!就密钥的长度而言,这是正确的,但不适用于此实现!这将导致"serpent_set_key"中的缓冲区溢出,因为for循环将运行64次,而不是仅运行8次!另一个被调用的地方是这样的EAInit:
serpent_set_key(key,32*8,ks);
这里很明显,传入的参数将是256。
我很好奇其他人对此有什么看法?其他人能确认这个错误吗?
作为VeraCrypt的主要开发者,一位用户将我重定向到了这篇文章,因为VeraCrypto是基于TrueCrypt源代码的。
在研究了您提出的问题后,我可以确认这确实是代码中的一个错误,并且对serpent_set_key的调用应该传递32而不是256作为参数。
幸运的是,这个错误不会影响程序执行过程中的正确性或安全性,这就是为什么在您之前没有人发现它的原因。因此,我们不能将其定性为bug。
让我用三点来解释这一点:
- 让我们来看看
serpent_set_key
的Serpent算法实现:参数keylen
仅用于将用户密钥复制到ks
缓冲区,该缓冲区被保证具有560的最小大小(看看crypt.h中定义的SERPENT_KS
)。因此,即使keylen
是256而不是32,我们也永远不会写超出ks
的分配内存 - 此循环之后的内部密钥扩展将使用仅userKey的前32个字节构建扩展的Serpent密钥,如Serpent算法规范中所述。因此,在前32个字节之后的所有字节都将被丢弃,并且它们将永远不会被使用。这解释了为什么即使将256字节而不是预期的32字节传递给计算结果,计算结果也是正确的
- 如果我们列出所有导致
serpent_set_key
的运行时调用,我们会注意到,除了自动测试的情况外,所有调用都为userKey参数使用256字节的缓冲区,即使它的前32个字节已经填充(看看crypto.h中的MASTER_KEYDATA_SIZE
)。因此,在运行时,我们永远不会读取超出分配的缓冲区空间的内容。它仍然是自动测试的情况(例如,在tests.c或Dlgcode.c中的CipherTestDialogProc
),其中32字节的缓冲区用于userKey:在这里,我们将读取超出分配的空间的内容,但在实践中,它不会造成任何伤害,因为该缓冲区周围的内存是可读的
我希望这能澄清为什么这个错误是无害的。话虽如此,它必须被纠正,这就是我们在VeraCrypt中要做的。
记录在案,这个错误似乎是由twofish_set_key
和serpent_set_key
之间的混淆引起的:这两个函数的声明具有相同类型的参数,但twofish_set_key
期望用户密钥长度以位为单位,而serpent_set_key
期望用户密钥的长度以字节为单位!显然,对于钥匙的大小,我们应该有相同的约定。