本人写了个TEA算法,有没有哪位朋友有研究过的,看看我写得如何?是否有不好的地方。大家探讨探讨。源码如下
unit CTeaUnit;interface
uses
Windows, Messages, SysUtils, Classes,dialogs;type
TByteArray = array of byte;
CTea = class
private
function encipher(inB: TByteArray): TByteArray;
function decipher(inB: TByteArray; offset: Integer): TByteArray; overload;
function decipher(inB: TByteArray): TByteArray; overload;
procedure encrypt8Bytes();
function decrypt8Bytes(inB: TByteArray; offset, len: integer): boolean;
protected
public
constructor Create;
destructor Destroy; override;
function decrypt(inB: TByteArray; offset: Integer; len: Integer; k: TByteArray): TByteArray; overload;
function decrypt(inB: TByteArray; k: TByteArray): TByteArray; overload;
function encrypt(inB: TByteArray; offset: Integer; len: Integer; k: TByteArray): TByteArray; overload;
function encrypt(inB: TByteArray; k: TByteArray): TByteArray; overload; published
end;implementation
var
// 指向当前的明文块
plain: TByteArray;
// 这指向前面一个明文块
prePlain: TByteArray;
// 输出的密文或者明文
outB: TByteArray;
// 当前加密的密文位置和上一次加密的密文块位置,他们相差8
crypt, preCrypt: Integer;
// 当前处理的加密解密块的位置
pos: Integer;
// 填充数
padding: Integer;
// 密钥
key: TByteArray;
// 用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的
// 但是最开始的8个字节没有反馈可用,所有需要标明这种情况
header: boolean;
// 这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时
// 后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错
contextStart: Integer;constructor CTea.Create;
begin
header := true;
Randomize;
end;{ destructor - just as a placeholder if cleanup will be necessary }destructor CTea.Destroy;
begin
inherited Destroy;
end;
unit CTeaUnit;interface
uses
Windows, Messages, SysUtils, Classes,dialogs;type
TByteArray = array of byte;
CTea = class
private
function encipher(inB: TByteArray): TByteArray;
function decipher(inB: TByteArray; offset: Integer): TByteArray; overload;
function decipher(inB: TByteArray): TByteArray; overload;
procedure encrypt8Bytes();
function decrypt8Bytes(inB: TByteArray; offset, len: integer): boolean;
protected
public
constructor Create;
destructor Destroy; override;
function decrypt(inB: TByteArray; offset: Integer; len: Integer; k: TByteArray): TByteArray; overload;
function decrypt(inB: TByteArray; k: TByteArray): TByteArray; overload;
function encrypt(inB: TByteArray; offset: Integer; len: Integer; k: TByteArray): TByteArray; overload;
function encrypt(inB: TByteArray; k: TByteArray): TByteArray; overload; published
end;implementation
var
// 指向当前的明文块
plain: TByteArray;
// 这指向前面一个明文块
prePlain: TByteArray;
// 输出的密文或者明文
outB: TByteArray;
// 当前加密的密文位置和上一次加密的密文块位置,他们相差8
crypt, preCrypt: Integer;
// 当前处理的加密解密块的位置
pos: Integer;
// 填充数
padding: Integer;
// 密钥
key: TByteArray;
// 用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的
// 但是最开始的8个字节没有反馈可用,所有需要标明这种情况
header: boolean;
// 这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时
// 后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错
contextStart: Integer;constructor CTea.Create;
begin
header := true;
Randomize;
end;{ destructor - just as a placeholder if cleanup will be necessary }destructor CTea.Destroy;
begin
inherited Destroy;
end;
解决方案 »
- 春节后结贴-200
- combo如何获得itemindex中对应的某个值?
- 一个截取字符串的问题
- 如何在程序运行期动态建立一个sql2000的数据库?up者有分。
- !!!!!!!有关socket服务器端故障重启后重新刷新客户端用户名单的问题?????
- 问一个字符串处理的问题
- 急急急!怎么样来类里定义全局变量
- 请教一个简单的排序算法
- 请教TDBChart的用法,40分必送上.
- 哪位大侠知道Windows删除文件或访问文件时发送什么消息?
- stringgrid 的scrollbars 已经设为ssBoth ,运行时怎样判断滚动条是否出现了,用代码,不告诉我用肉眼
- 100分寻求答案!Select * from [;Database=D:\hacc\Data\db1.mdb;pwd=123456].table1 此语句粘帖到Access查询中执行没问题,但是在程序中
{/**
* 解密
* @param in 密文
* @param offset 密文开始的位置
* @param len 密文长度
* @param k 密钥
* @return 明文
*/}
var
count, i: integer;
m: TByteArray;
begin
crypt := 0;
preCrypt := 0;
key := k;
SetLength(m, offset + 8);
// 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况
if ((len mod 8 <> 0) or (len < 16)) then begin
Result := nil;
Exit;
end;
// 得到消息的头部,关键是得到真正明文开始的位置,这个信息存在第一个字节里面,所以其用解密得到的第一个字节与7做与
prePlain := decipher(inB, offset);
pos := prePlain[0] and $7;
// 得到真正明文的长度
count := len - pos - 10;
// 如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回
if (count < 0) then begin
Result := nil;
Exit;
end;
//这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时
//第一个8字节块也没有preCrypt,所有这里建一个全0的
for i := offset to Length(m) - 1 do
m[i] := 0;
// 通过了上面的代码,密文应该是没有问题了,我们分配输出缓冲区
SetLength(outB, count);
// 设置preCrypt的位置等于0,注意目前的preCrypt位置是指向m的,因为java没有指针,所以我们在后面要控制当前密文buf的引用
preCrypt := 0;
// 当前的密文位置,为什么是8不是0呢?注意前面我们已经解密了头部信息了,现在当然该8了
crypt := 8;
// 自然这个也是8
contextStart := 8;
// 加1,和加密算法是对应的
Inc(pos);
// 开始跳过头部,如果在这个过程中满了8字节,则解密下一块
// 因为是解密下一块,所以我们有一个语句 m = in,下一块当然有preCrypt了,我们不再用m了
// 但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来
// 所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充
padding := 1;
while padding <= 2 do
begin
if (pos < 8) then begin
Inc(pos);
Inc(padding);
end;
if (pos = 8) then begin
m := inB;
if (not decrypt8Bytes(inB, offset, len)) then begin
Result := nil;
Exit;
end;
end;
end;
// 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密
// 注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是in了
i := 0;
while (count <> 0) do begin
if (pos < 8) then begin
outB[i] := byte(m[offset + preCrypt + pos] xor prePlain[pos]);
Inc(i);
Dec(count);
Inc(pos);
end;
if (pos = 8) then begin
m := inB;
preCrypt := crypt - 8;
if (not decrypt8Bytes(inB, offset, len)) then begin
Result := nil;
Exit;
end;
end;
end;
// 最后的解密部分,上面一个while已经把明文都解出来了,到了这里还剩下什么?对了,还剩下尾部的填充,应该全是0
// 所以这里有检查是否解密了之后是0,如果不是的话那肯定出错了,所以返回null
for padding := 1 to 7 do begin
if (pos < 8) then begin
if ((m[offset + preCrypt + pos] xor prePlain[pos]) <> 0) then begin
Result := nil;
Exit;
end;
inc(pos);
end;
if (pos = 8) then begin
m := inB;
preCrypt := crypt;
if (not decrypt8Bytes(inB, offset, len)) then begin
Result := nil;
Exit;
end;
end;
end; //Result := outB;
SetLength(Result, Length(outB));
CopyMemory(@Result[0], @outB[0], Length(outB));
end;
{
/**
* @param in
* 需要被解密的密文
* @param inLen
* 密文长度
* @param k
* 密钥
* @return Message 已解密的消息
*/
}
begin
Result := decrypt(inB, 0, Length(inB), k);
end;function CTea.encrypt(inB: TByteArray; offset: Integer; len: Integer; k: TByteArray): TByteArray;
{
/**
* 加密
* @param in 明文字节数组
* @param offset 开始加密的偏移
* @param len 加密长度
* @param k 密钥
* @return 密文字节数组
*/
}
var
i: Integer;
begin
SetLength(plain, 8);
SetLength(prePlain, 8);
pos := 1;
padding := 0;
crypt := 0;
preCrypt := 0;
key := k;
header := true; // 计算头部填充字节数
pos := (len + $A) mod 8;
if (pos <> 0) then
pos := 8 - pos;
// 计算输出的密文长度
SetLength(outB, len + pos + 10);
// 这里的操作把pos存到了plain的第一个字节里面
// 0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置
plain[0] := byte((random(256) and $F8) or pos); // 这里用随机产生的数填充plain[1]到plain[pos]之间的内容
for i := 1 to pos do
plain[i] := byte(random(256) and $FF);
Inc(pos);
// 这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块
for i := 1 to 7 do
prePlain[i] := $0; // 继续填充2个字节的随机数,这个过程中如果满了8字节就加密之
padding := 1;
while (padding <= 2) do begin
if (pos < 8) then begin
plain[pos] := byte(random(246) and $FF);
Inc(pos);
Inc(padding);
end;
if (pos = 8) then
encrypt8Bytes();
end; // 头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完
i := offset;
while (len > 0) do begin
if (pos < 8) then begin
plain[pos] := inB[i];
Inc(pos);
Inc(i);
Dec(len);
end;
if (pos = 8) then
encrypt8Bytes();
end; // 最后填上0,以保证是8字节的倍数
padding := 1;
while (padding <= 7) do begin
if (pos < 8) then begin
plain[pos] := $0;
Inc(pos);
Inc(padding); end;
if (pos = 8) then
encrypt8Bytes();
end; //Result:=outB;
SetLength(Result, Length(outB));
CopyMemory(@Result[0], @outB[0], Length(outB));end;function CTea.encrypt(inB: TByteArray; k: TByteArray): TByteArray;
{
/**
* @param in
* 需要加密的明文
* @param inLen
* 明文长度
* @param k
* 密钥
* @return Message 密文
*/
}
begin
Result := encrypt(inB, 0, Length(inB), k);
end;function CTea.encipher(inB: TByteArray): TByteArray;
var
loop: Integer;
y, z, a, b, c, d, sum, delta: Cardinal;
tmpArray, tmpOut: array[0..7] of byte;begin
try
// 迭代次数,16次
loop := $10;
// 得到明文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
// 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
// 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
CopyMemory(@y, @inB[0], 4);
CopyMemory(@z, @inB[4], 4);
CopyMemory(@a, @key[0], 4);
CopyMemory(@b, @key[4], 4);
CopyMemory(@c, @key[8], 4);
CopyMemory(@d, @key[12], 4);
// 这是算法的一些控制变量,为什么delta是0x9E3779B9呢?
// 这个数是TEA算法的delta,实际是就是sqr(5)-1 * 2^31
sum := 0;
delta := $9E3779B9; // 开始迭代了,乱七八糟的,我也看不懂,反正和DES之类的差不多,都是这样倒来倒去
while (loop > 0) do begin
sum := sum + delta;
y := y + ((z shl 4) + a) xor (z + sum) xor ((z shr 5) + b);
z := z + ((y shl 4) + c) xor (y + sum) xor ((y shr 5) + d);
Dec(loop);
end; // 最后,我们输出密文,因为我用的long,所以需要强制转换一下变成int
CopyMemory(@tmpArray[0], @y, 4);
CopyMemory(@tmpArray[4], @z, 4);
tmpOut[0] := tmpArray[3];
tmpOut[1] := tmpArray[2];
tmpOut[2] := tmpArray[1];
tmpOut[3] := tmpArray[0];
tmpOut[4] := tmpArray[7];
tmpOut[5] := tmpArray[6];
tmpOut[6] := tmpArray[5];
tmpOut[7] := tmpArray[4]; SetLength(Result, Length(tmpOut));
CopyMemory(@Result[0], @tmpOut[0], Length(tmpOut));
except
Result := nil;
end;
end;function CTea.decipher(inB: TByteArray; offset: Integer): TByteArray;
var
loop: Integer;
y, z, a, b, c, d, sum, delta: Cardinal;
tmpArray, tmpOut: array[0..7] of byte;
begin
try
// 迭代次数,16次
loop := $10;
// 得到密文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
// 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
// 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
CopyMemory(@y, @inB[offset], 4);
CopyMemory(@z, @inB[offset + 4], 4);
CopyMemory(@a, @key[0], 4);
CopyMemory(@b, @key[4], 4);
CopyMemory(@c, @key[8], 4);
CopyMemory(@d, @key[12], 4);
// 算法的一些控制变量,为什么sum在这里也有数了呢,这个sum嘛就是和迭代次数有关系了
// 因为delta是这么多,所以sum如果是这么多的话,迭代的时候减减减,减16次,最后
// 得到什么? Yeah,得到0。反正这就是为了得到和加密时相反顺序的控制变量,这样
// 才能解密呀~~
sum := $E3779B90;
delta := $9E3779B9;
// 迭代开始了, #_#
while (loop > 0) do begin
z := z - ((y shl 4) + c) xor (y + sum) xor ((y shr 5) + d);
y := y - ((z shl 4) + a) xor (z + sum) xor ((z shr 5) + b);
sum := sum - delta;
Dec(loop);
end; // 输出明文,注意要转成int
CopyMemory(@tmpArray[0], @y, 4);
CopyMemory(@tmpArray[4], @z, 4);
tmpOut[0] := tmpArray[3];
tmpOut[1] := tmpArray[2];
tmpOut[2] := tmpArray[1];
tmpOut[3] := tmpArray[0];
tmpOut[4] := tmpArray[7];
tmpOut[5] := tmpArray[6];
tmpOut[6] := tmpArray[5];
tmpOut[7] := tmpArray[4]; SetLength(Result, Length(tmpOut));
CopyMemory(@Result[0], @tmpOut[0], Length(tmpOut));
except
Result := nil;
end;
end;function CTea.decipher(inB: TByteArray): TByteArray;
begin
Result := decipher(inB, 0);
end;procedure CTea.encrypt8Bytes();
{
/**
* 加密8字节
*/
}
var
crypted: TByteArray;
i: integer;
begin
// 这部分完成我上面所说的 plain ^ preCrypt,注意这里判断了是不是第一个8字节块,如果是的话,那个prePlain就当作preCrypt用
for pos := 0 to 7 do begin
if (header) then
plain[pos] := plain[pos] xor prePlain[pos]
else
plain[pos] := plain[pos] xor outB[preCrypt + pos];
end;
// 这个完成到了我上面说的 f(plain ^ preCrypt)
crypted := encipher(plain);
// 这个没什么,就是拷贝一下,delphi不像c,所以我只好这么干,c就不用这一步了
for i := 0 to 7 do
begin
outB[crypt + i] := crypted[i];
end; // 这个就是完成到了 f(plain ^ preCrypt) ^ prePlain,ok,完成了,下面拷贝一下就行了
for pos := 0 to 7 do
outB[crypt + pos] := outB[crypt + pos] xor prePlain[pos]; prePlain := plain;
// 完成了加密,现在是调整crypt,preCrypt等等东西的时候了
preCrypt := crypt;
crypt := crypt + 8;
pos := 0;
header := false;
end;function CTea.decrypt8Bytes(inB: TByteArray; offset, len: integer): boolean;
begin
// 这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^ prePlain
for pos := 0 to 7 do begin
if (contextStart + pos >= len) then begin
Result := true;
Exit;
end;
prePlain[pos] := prePlain[pos] xor inB[offset + crypt + pos];
end; // 好,这里执行到了 d(crypt ^ prePlain)
prePlain := decipher(prePlain);
if (prePlain = nil) then begin
Result := false;
Exit;
end; // 解密完成,wait,没完成哦,最后一步没做哦?
// 这里最后一步放到decrypt里面去做了,因为解密的步骤毕竟还是不太一样嘛
// 调整这些变量的值先
contextStart := contextStart + 8;
crypt := crypt + 8;
pos := 0;
Result := true;
end;
end.