我正在浏览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.
我很好奇其他人对此的看法?其他人可以确认这个错误吗?
答案 0 :(得分:3)
作为VeraCrypt的主要开发人员,由于VeraCrypt基于TrueCrypt源,因此用户已将我重定向到此帖子。
在研究了你提出的问题之后,我可以确认这确实是代码中的错误,并且对serpent_set_key的调用应该传递32而不是256作为参数。
幸运,这个错误对程序执行过程中的正确性或安全性没有影响,这就是没有人在你之前发现它的原因。因此,我们不能将此视为错误。
让我用三点来解释:
serpent_set_key
的Serpent算法实现:参数keylen
仅用于将用户密钥复制到保证<{1}}缓冲区中/ strong>最小尺寸 560 (查看crypt.h中的ks
定义)。因此,即使SERPENT_KS
是256而不是32,我们也永远不会超出分配的keylen
内存。ks
的引导,我们会注意到,除了自动测试的情况之外,所有调用都使用256字节缓冲区作为userKey参数,即使它是第一个填充32个字节(在crypto.h中查看serpent_set_key
)。因此,在运行时,我们永远不会超出分配缓冲区空间。它仍然是自动测试的例子(在Tests.c中为ei或在Dlgcode.c中为MASTER_KEYDATA_SIZE
),其中32字节缓冲区用于userKey:这里我们将读取超出分配的空间但实际上它没有&#39 ; t会造成任何伤害,因为此缓冲区周围的内存是可读的。我希望这可以澄清为什么这个错误是无害的。话虽如此,它必须得到纠正,这是我们在VeraCrypt中所做的事情。
对于记录,似乎这个错误是由CipherTestDialogProc
和twofish_set_key
之间的混淆引起的:两个函数的声明具有相同类型的参数,但serpent_set_key
期望用户密钥长度(以位为单位),而twofish_set_key
则以字节为单位!显然,我们应该对密钥的大小采用相同的约定。