《常见密码算法总结--(1)分组对称密码》见
http://bbs.pediy.com/showthread.php?t=113921
http://blog.csdn.net/NJZhuJinhua/arc...7/5629455.aspx
《常见密码算法总结--(2)分组密码加密模式》见
http://bbs.pediy.com/showthread.php?t=114169
http://blog.csdn.net/NJZhuJinhua/arc...0/5635313.aspx
NJZhuJinhua@csdn May.30, 2010
http://bbs.pediy.com/showthread.php?t=114170
http://blog.csdn.net/NJZhuJinhua/arc...0/5635343.aspx
转载请注明出处。
(三)加密模式的openssl代码分析
openssl(本文所用openssl代码为1.0.0版本,2010.03.29发布。http://www.openssl.org 本文所用的内容最近几年应该未有更新,因而版本似乎也没多少关系)代码中crypto\modes目录下提供了cbc128,cfb128,ctr128,cts128,ofb128模式的实现。modes.h提供接口声明,其余诸文件分别提供了各种模式。
代码:
typedef void (*block128_f)(const unsigned char in[16],
unsigned char out[16],
const void *key);
typedef void (*cbc128_f)(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], int enc);
//cbc128的加密与解密
void CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], block128_f block);
void CRYPTO_cbc128_decrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], block128_f block);
//ctr128的加密与解密
void CRYPTO_ctr128_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], unsigned char ecount_buf[16],
unsigned int *num, block128_f block);
//ofb128的加密与解密
void CRYPTO_ofb128_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], int *num,
block128_f block);
//cfb128的加密与解密
void CRYPTO_cfb128_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], int *num,
int enc, block128_f block);
void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const void *key,
unsigned char ivec[16], int *num,
int enc, block128_f block);
void CRYPTO_cfb128_1_encrypt(const unsigned char *in, unsigned char *out,
size_t bits, const void *key,
unsigned char ivec[16], int *num,
int enc, block128_f block);
//cts128模式
size_t CRYPTO_cts128_encrypt_block(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], block128_f block);
size_t CRYPTO_cts128_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], cbc128_f cbc);
size_t CRYPTO_cts128_decrypt_block(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], block128_f block);
size_t CRYPTO_cts128_decrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], cbc128_f cbc);
我们按照cbc,cfb,ofb的顺序进行。
(1)CRYPTO_cbc128_encrypt CRYPTO_cbc128_decrypt
该函数在libeay32的实现中被
seed_cbc.c(SEED_cbc_encrypt())
cts128.c(CRYPTO_cts128_encrypt_block())
cmll_cbc.c(Camellia_cbc_encrypt())
aes_cbc.c(AES_cbc_encrypt())
所使用。
例如aes_cbc.c中:
代码:
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const AES_KEY *key,
unsigned char *ivec, const int enc) {
if (enc)
CRYPTO_cbc128_encrypt(in,out,len,key,ivec,(block128_f)AES_encrypt);
else
CRYPTO_cbc128_decrypt(in,out,len,key,ivec,(block128_f)AES_decrypt);
}
AES的加解密公用一个函数,具体是加密还是解密通过参数enc指示。AES_cbc_encrypt通过将参数原封不动粗韩给CRYPTO_cbc128_encrypt或者CRYPTO_cbc128_encrypt完成加解密。最后一个回调函数AES_encrypt和AES_descrypt即使用该模式的加密算法,用于加密一个分组。
其他模式也基本同这例子,后面的模式不再详细说明其怎么跟加密算法的结合。
代码:
void CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], block128_f block)
{
size_t n;
const unsigned char *iv = ivec;
//确保明文,密文,密钥及初始向量指针的有效性
assert(in && out && key && ivec);
#if !defined(OPENSSL_SMALL_FOOTPRINT)
if (STRICT_ALIGNMENT &&
((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0)
{
//cpu对其,但in out及ivec中有一个不是sizeof(size_t)字节对齐的,如地址为0x08000001,sizeof(size_t)==4情况下
while (len>=16)
{
for(n=0; n<16; ++n)
out[n] = in[n] ^ iv[n];【A】
第一次循环时iv为入参给的ivec,之后的循环已经在【C】处被设为新产生的密文了
(*block)(out, out, key);【B】
调用具体的回调函数,第一个参数为【A】处算出的明文分组与上一个密文分组的异或分组。第二个参数out为出参,意思是加密之后的值还写到out所在的内存。key为提供的密钥。
iv = out;【C】
更新密文分组,用于下一分组的明文进行异或。
len -= 16;
in += 16;
out += 16;
移动指针等,准备处理下一分组。当剩余的len小于16时,或压根输入就小于16时进行后面【D】的流程
}
}
else
{
这个与上一个目的一样,不过对已经对齐的情形进行了优化,一次处理了sizeof(size_t)个字节的异或运算而已。
while (len>=16)
{
for(n=0; n<16; n+=sizeof(size_t))
*(size_t*)(out+n) =
*(size_t*)(in+n) ^ *(size_t*)(iv+n);
(*block)(out, out, key);
iv = out;
len -= 16;
in += 16;
out += 16;
}
}
#endif
while (len) 【D】
如果定义了OPENSSL_SMALL_FOOTPRINT 则上述并未执行,len仍是入参的len。否则这里的len经上面处理后应该是小于16的了。下面处理总体来说逻辑跟上面差不多,多了len<16的情形的处理。
{
for(n=0; n<16 && n<len; ++n)
out[n] = in[n] ^ iv[n];【E】
如果实际剩余长度len小于16则对len字节进行异或运算,否则对分组长16字节进行运算。
for(; n<16; ++n)
out[n] = iv[n];【F】
如果上述len小于16的情形,需要执行【F】,及最后一个分组的明文的最后字节用上一个分组的密文对应字节填充(在明文总长度就小于16时则是用初始向量填充了)
(*block)(out, out, key);
对分组加密
iv = out;
if (len<=16) break;
退出
len -= 16;
in += 16;
out += 16;
}
memcpy(ivec,iv,16);
}
//cbc128模式的解密函数
void CRYPTO_cbc128_decrypt(const unsigned char *in, unsigned char *out,
size_t len, const void *key,
unsigned char ivec[16], block128_f block)
{
size_t n;
union { size_t align; unsigned char c[16]; } tmp;
assert(in && out && key && ivec);
#if !defined(OPENSSL_SMALL_FOOTPRINT)
if (in != out) 解密后的明文是否放在密文的内存中
{
const unsigned char *iv = ivec;
if (STRICT_ALIGNMENT &&
((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0)
{
while (len>=16)
{
(*block)(in, out, key);解密一个分组
for(n=0; n<16; ++n) 到这里out为该分组对应的明文分组与前一个密文分组的异或。
out[n] ^= iv[n];
iv = in;到这里后out为明文分组了
len -= 16;
in += 16;
out += 16;
}
}
else
{
while (len>=16)
{
(*block)(in, out, key);与上同理
for(n=0; n<16; n+=sizeof(size_t))
*(size_t *)(out+n) ^= *(size_t *)(iv+n);
iv = in;
len -= 16;
in += 16;
out += 16;
}
}
memcpy(ivec,iv,16);
}
else
{
如果解密后的明文放到原密文的地方,则执行此部分。除了使用临时变量tmp中转一下外与上一样,略
if (STRICT_ALIGNMENT &&
((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0)
{
unsigned char c;
while (len>=16)
{
(*block)(in, tmp.c, key);
for(n=0; n<16; ++n)
{
c = in[n];
out[n] = tmp.c[n] ^ ivec[n];
ivec[n] = c;
}
len -= 16;
in += 16;
out += 16;
}
}
else
{
//字节对齐时的优化操作
size_t c;
while (len>=16) {
(*block)(in, tmp.c, key);
for(n=0; n<16; n+=sizeof(size_t)) {
c = *(size_t *)(in+n);
*(size_t *)(out+n) =
*(size_t *)(tmp.c+n) ^ *(size_t *)(ivec+n);
*(size_t *)(ivec+n) = c;
}
len -= 16;
in += 16;
out += 16;
}
}
}
#endif
while (len)
{
//解密最后一个分组,或openssl定义了OPENSSL_SMALL_FOOTPRINT的话这里解密所有分组。与加密时基本一样,略
unsigned char c;
(*block)(in, tmp.c, key);
for(n=0; n<16 && n<len; ++n) {
c = in[n];
out[n] = tmp.c[n] ^ ivec[n];
ivec[n] = c;
}
if (len<=16) {
for (; n<16; ++n)
ivec[n] = in[n];
break;
}
len -= 16;
in += 16;
out += 16;
}
}
(2)CRYPTO_cfb128_encrypt CRYPTO_cfb128_1_encrypt CRYPTO_cfb128_8_encrypt
这三个实现了cfb模式的三种制式。其函数声明见上文描述。
这三种均被seed_cfb.c cmll_cfb.c aes_cfb.c引用。
在aes_cfb.c内使用情况如下:
代码:
void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc) {
CRYPTO_cfb128_encrypt(in,out,length,key,ivec,num,enc,(block128_f)AES_encrypt);
}
/* N.B. This expects the input to be packed, MS bit first */
void AES_cfb1_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc)
{
CRYPTO_cfb128_1_encrypt(in,out,length,key,ivec,num,enc,(block128_f)AES_encrypt);
}
void AES_cfb8_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc)
{
CRYPTO_cfb128_8_encrypt(in,out,length,key,ivec,num,enc,(block128_f)AES_encrypt);
}