这是BCrypt文件加密实用程序源代码的一部分。不变,除了我添加的一些评论。
uLong BFEncrypt(char **input, char *key, uLong sz, BCoptions *options) {
uInt32 L, R;
uLong i;
BLOWFISH_CTX ctx;
int j;
unsigned char *myEndian = NULL;
j = sizeof(uInt32);
getEndian(&myEndian);
// makes space 2 bytes
memmove(*input+2, *input, sz);
// add endian and compresssion option
memcpy(*input, myEndian, 1);
memcpy(*input+1, &options->compression, 1);
sz += 2; /* add room for endian and compress flags */ // total size increased
Blowfish_Init (&ctx, key, MAXKEYBYTES); // initialize
// encrypt the file
for (i = 2; i < sz; i+=(j*2)) { /* start just after tags */
memcpy(&L, *input+i, j);// copy j bytes from input to L
memcpy(&R, *input+i+j, j); // copy second j byte to R
Blowfish_Encrypt(&ctx, &L, &R); // encrypt
memcpy(*input+i, &L, j); // copy everything back
memcpy(*input+i+j, &R, j);
}
if (options->compression == 1) {
if ((*input = realloc(*input, sz + j + 1)) == NULL)
memerror();
memset(*input+sz, 0, j + 1);
memcpy(*input+sz, &options->origsize, j);
sz += j; /* make room for the original size */
}
free(myEndian);
return(sz);
}
在循环中,我们首先将文件缓冲区逐字节复制到新变量,然后应用河豚加密。然后再次将字节复制到缓冲区。为什么不能将字节直接传递给加密函数?为什么memcpy()
甚至需要?
为什么不能将字节直接传递给加密函数?
有两个反对或至少不支持它的规则。
第一个是如果对的对齐方式不正确,则将char
指针转换为指向的指针int
具有未定义的行为int
,并且即使对齐方式正确,结果的值也未完全定义。有关此规则,请参见C 2018 6.3.2.3,其中涉及指针转换。
通常,诸如之类的对象int
必须位于四个字节的倍数处。这是因为计算机内存和数据总线的组织方式;所涉及的各种“电线”被设置为以一定大小和排列的组来传输事物。当此类系统的编译器生成用于处理int
对象的指令时,它会生成加载对齐单词的指令。如果您使用char
未对齐的指针并将其转换为的指针int
,则当加载对齐字指令尝试使用未对齐的地址时,某些处理器将生成陷阱。其他处理器可能会忽略地址的低位,并从其他地址加载对齐的字。
即使地址对齐,C标准也不能保证将a转换char *
为int *
实际指向与原始位置相同的位置的结果。这是因为在某些系统中(现在大多是过时的),以不同的方式表示了指向不同类型的指针。有些系统仅以多个字节为单位访问内存,因此,要实现该功能char *
,编译器必须合成与硬件地址不同的地址,而对于而言int *
,编译器可能会直接使用硬件地址。
第二条规则是,指定用于一种类型(例如的数组)的内存char
不能随意地用作另一种类型(例如)的内存int
。此规则在C 2018 6.5 7中。它具有允许的特定情况,例如,可以将任何类型(例如int
或)float
作为进行访问char
,反之亦然。该规则的目的是使例程可以通过anint *i
和afloat *f
在这样的代码中知道这一点:
for (int j = 0; j < 1024; ++j)
f[j] += *i;
在f[j]
总访问float
,从不访问的int
,所以这个循环体永远不会改变的价值*i
。这意味着编译器可以优化代码以:
int t = *i;
for (int j = 0; j < 1024; ++j)
f[j] += t;
*i
由于临时对象t
可以保存在处理器寄存器中,因此可以节省从内存重复加载的工作。(最重要的是,编译器实际上可以使用float t = *i;
,保存*i
从内存的重复加载和向的重复转换,以节省float
添加的时间。)
您可能会查看该动机,然后查看Blowfish_Encrypt
并看到它BlowFish_Encrypt
永远不会从这种潜在的优化中受益,可能是因为它从未与受此规则影响的混合类型一起运行。但是,随着编译器在转换中变得越来越先进和积极,编译器优化的复杂性变得越来越难以看到,因此很容易错过编译器从将一种类型别名为另一种类型的规则中将获得的某些优势。无论如何,由于存在该规则,因此您无法保证程序会在违反该规则的情况下运行。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句