我浏览了TrueCrypt (7.1a)中的蛇形实现,发现有些地方看起来不太对劲!下面是这个算法的接口:
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的所有实现中,这在两个地方被调用。下面是第一个: In 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。
我很好奇其他人是怎么想的?其他人能确认这个bug吗?
发布于 2014-09-27 15:05:39
作为VeraCrypt的主要开发者,一位用户将我重定向到这篇文章,因为VeraCrypt是基于TrueCrypt源代码的。
在研究了您提出的问题之后,我可以确认这确实是代码中的一个错误,对serpent_set_key的调用应该传递32而不是256作为参数。
幸运的是,,这个错误不会影响程序执行过程中的正确性或安全性,这就是为什么在你之前没有人发现它的原因。因此,我们不能将其限定为bug。
让我用三点来解释:
serpent_set_key的蛇算法实现:参数keylen仅用于将用户密钥复制到ks缓冲区,该缓冲区是保证的,其最小大小为560 (请查看在crypt.h中定义的SERPENT_KS )。因此,即使keylen是256而不是32,我们也永远不会超出分配的ks.serpent_set_key的运行时调用,我们会注意到,除了自动测试的情况外,所有调用都使用256个字节的缓冲区来存储userKey参数,即使它的前32个字节已被填满(请查看MASTER_KEYDATA_SIZE in crypto.h)。因此,在运行时,我们永远不会读取超出分配缓冲区空间的内容。它仍然是自动测试的情况(即,在Tests.c或Dlgcode.c)中,其中32字节的缓冲区用于userKey :这里我们将读取分配的空间之外的内容,但实际上它不会造成任何损害,因为此缓冲区周围的内存是可读的。我希望这能澄清为什么这个错误是无害的。这就是说,它必须被纠正,这就是我们在VeraCrypt中要做的。
根据记录,这个错误似乎是由twofish_set_key和serpent_set_key之间的混淆造成的:这两个函数的声明具有相同类型的参数,但twofish_set_key期望用户密钥长度以位为单位,而serpent_set_key期望它以字节为单位!显然,对于键的大小,我们应该有相同的约定。
https://stackoverflow.com/questions/22122509
复制相似问题