Blowfish是一种对称密钥分组密码,Bruce Schneier在1993年设计并公布。它没有专利,任何人均可自由使用。
在介绍Blowfish之前先来搞清楚Feistel Structure(下图来自Wikipedia):
上述4个步骤是一轮循环的工作过程,Feistel Structure是进行了16轮循环方才完成一次加密。需要说明一点,在最后一轮循环中左右数据不对调。解密过程是加密过程的反向,具体可参照上图,不再赘述。
##0x03 Source Code
笔者分析的源码是Paul Kocher版本,并没有使用Bruce Schneier版本。Schneier版本中的子密钥来源是BLOWFISH.DAT
文件,而Kocher版本是直接在源文件中定义子密钥,分析起来较为直观。如需其他版本可到此网站下载
在blowfish.h
头文件中有如下定义:
为结构体定义别名,结构体中的两数组即P-Box与S-Box。
ORIG_P
与ORIG_S
取自圆周率的小数位,每4个字节赋值给其中的一个元素。
该函数用来初始化S-Box与P-Box。传递参数中的key
即密钥,keyLen
是密钥长度。
datal=0x00000000;
datar=0x00000000;
将上面经过变换后的ctx
,datal
与datar
传递给Blowfish_Encrypt
步骤5、8中提到的赋值过程是这样的(以步骤5来举例):
第一次 P[0]=datal
,P[1]=datar
第二次 P[2]=datal
,P[3]=datar
......
该函数是Blowfish的加密部分。传递参数中的ctx
应该已经初始化过S-Box与P-Box,xl
指向原数据的左半部分,xr
指向原数据的右半部分。
上述过程即一次完整的加密过程,可参照下图来理解(来自Wikipedia,其中轮函数F的工作过程见0x03.6
):
解密过程是加密过程的反向,如有困惑,可参照源码理解,不再赘述。
轮函数工作过程:
此Demo来自Paul Kocher版本根目录下的blowfish_test.c
:
需要说明的一点是Paul Kocher这个版本并没有考虑到小端序的情况,它均按大端序来处理,所以如果在Linux平台运行此Demo会像下图所示:
可以看到加密结果并非源码中给出的结果,而在Windows平台下一切正常:
typedef struct {
unsigned long P[16 + 2];
unsigned long S[4][256];
} BLOWFISH_CTX;
#include <stdio.h>
#include "blowfish.h"
void main(void) {
unsigned long L = 1, R = 2;
BLOWFISH_CTX ctx;
Blowfish_Init (&ctx, (unsigned char*)"TESTKEY", 7);
Blowfish_Encrypt(&ctx, &L, &R);
printf("%08lX %08lX\n", L, R);
if (L == 0xDF333FD2L && R == 0x30A71BB4L)
printf("Test encryption OK.\n");
else
printf("Test encryption failed.\n");
Blowfish_Decrypt(&ctx, &L, &R);
if (L == 1 && R == 2)
printf("Test decryption OK.\n");
else
printf("Test decryption failed.\n");
}
笔者分析的源码是Paul Kocher版本,并没有使用Bruce Schneier版本。Schneier版本中的子密钥来源是BLOWFISH.DAT
文件,而Kocher版本是直接在源文件中定义子密钥,分析起来较为直观。如需其他版本可到此网站下载
步骤5、8中提到的赋值过程是这样的(以步骤5来举例):
第一次 P[0]=datal
,P[1]=datar
第二次 P[2]=datal
,P[3]=datar
......
- 密钥长度:32-448位
- 分组长度:64位
- 16轮循环的Feistel结构
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)