本软件使用Delphi 10.3.3编写和测试, 源码中用到了System.NetEncoding和Generics.Collections两个单元, 因此本程序仅支持Delphi XE及更新的版本.
支持6种加密模式: ECB, CBC, CFB, OFB, PCBC, CTR; 默认为ECB;
支持7种填充模式(ZERO, PKCS5, PKCS7, ISO10126, ANSIX923, OneAndZero); 默认为PKCS7;
SM4要求密码长度的长度为16个字节(128bit), 如果长度不足, 程序就填充0x00来补足
SM4要求初始向量的长度为16个字节(128bit), 如果长度不足, 程序就填充0x00来补足
SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer 说明:
SrcBuffer存放待加密/解密的数据,
KeyBuffer存放密码数据,
IVBuffer存放初始向量数据,
DestBuffer存放已加密/解密的数据
TBuffer本身带有各种数据转换函数, 数据转换非常方便,不需要另外再写代码,例如:
function ToString(Encoding: TEncoding): String;
function ToHexString: String;
function ToDelimitedHexString(Prefix: String; Delimitor: String): String;
function ToBase64String: String;
procedure ToBytes(var OutBytes: TBytes; ByteLen: Integer);
procedure FromString(const Str: String);
procedure FromString(const Str: String; Encoding: TEncoding);
procedure FromHexString(const Str: String);
procedure FromDelimitedHexString(HexStr: String; Prefix: String; Delimitor: String');
procedure FromBase64String(const Str: String);
procedure FromBytes(const InBytes: TBytes; ByteLen: Integer);
最简单的TSM4使用示范代码:
//最简单的使用示范
procedure TMainForm.Test;
varS1, S2: String;
beginwith TSM4.Create(cmCBC, pmPKCS5) dobeginSrcBuffer.FromString('先学着让自己值钱');KeyBuffer.FromBase64String('MTIzNDU2Nzg5MDEyMzQ1Ng=='); //1234567890123456
//或KeyBuffer.FromString('1234567890123456');IVBuffer.FromHexString('6162636465666768696A6B6C6D6E6F70'); //abcdefghijklmnopEncrypt; //加密S1 := DestBuffer.ToHexString; //6C19FEC30147DBDE9539DC1DF0F3ACF09B6E6210F0F220D3D923F200DC5A44C4SrcBuffer.FromHexString(S1);Decrypt; //解密S2 := DestBuffer.ToString; //'先学着让自己值钱'Free;end;
end;
完整的TSM4代码(包括测试代码)
unit uMain;interfaceuses
{$IF CompilerVersion <= 22}Windows, Messages, Generics.Collections, SysUtils,Variants, Classes, Graphics, Controls, Forms,Dialogs, NetEncoding, StdCtrls, uSM4;
{$ELSE}Winapi.Windows, Winapi.Messages, Generics.Collections, System.SysUtils,System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,Vcl.Dialogs, System.NetEncoding, Vcl.StdCtrls, uSM4;
{$ENDIF}typeTMainForm = class(TForm)LabelBlockMode: TLabel;LabelPaddingmode: TLabel;ComboBoxCipherMode: TComboBox;ComboBoxPaddingmode: TComboBox;LabelStr: TLabel;LabelIV: TLabel;LabelEnc: TLabel;LabelDec: TLabel;EditKey: TEdit;EditIV : TEdit;EditDec: TEdit;EditEnc: TEdit;EditStr: TEdit;LabelKey: TLabel;ButtonEncrypt: TButton;ButtonDecrypt: TButton;ComboBoxStr: TComboBox;ComboBoxKey: TComboBox;ComboBoxIV: TComboBox;ComboBoxEnc: TComboBox;ComboBoxDec: TComboBox;LabelStrCnt: TLabel;LabelKeyCnt: TLabel;LabelIVCnt : TLabel;LabelEncCnt: TLabel;LabelDecCnt: TLabel;EditSrc: TEdit;ButtonToHex: TButton;ButtonToBase64: TButton;EditDest: TEdit;RzEdit1: TEdit;Label1: TLabel;ButtonFromHex: TButton;ButtonFramBase64: TButton;Label2: TLabel;Label3: TLabel;procedure FormCreate(Sender: TObject);procedure EditChange(Sender: TObject);procedure ButtonClick(Sender: TObject);procedure FormDestroy(Sender: TObject);procedure ButtonConvertClick(Sender: TObject);procedure ButtonFromClick(Sender: TObject);private typeTProc = procedure(const S: String) of object;Tfunc = function(): String of object;TFromDict = TDictionary;TToDict = TDictionary;TCountDict = TDictionary;privateSM4: TSM4;DIctCm: TDictionary;DIctPm: TDictionary;DictFromStr, DictFromKey, DictFromIV, DictFromEnc: TFromDict;DictToEnc, DictToDec: TToDict;DictCount: TCountDict;procedure FillDicts;public{ Public declarations }end;varMainForm: TMainForm;implementation{$R *.dfm}uses uBuffer;procedure TMainForm.FormCreate(Sender: TObject);
beginReportMemoryLeaksOnShutDown := True;SM4 := TSM4.Create;DIctCm := TDictionary.Create;DIctPm := TDictionary.Create;DictFromStr := TFromDict.Create;DictFromKey := TFromDict.Create;DictFromIV := TFromDict.Create;DictFromEnc := TFromDict.Create;DictToEnc := TToDict.Create;DictToDec := TToDict.Create;DictCount := TCountDict.Create;FillDicts;EditChange(nil);
end;procedure TMainForm.FormDestroy(Sender: TObject);
beginDIctCm.Free;DIctPm.Free;DictFromStr.Free;DictFromKey.Free;DictFromIV.Free;DictFromEnc.Free;DictToEnc.Free;DictToDec.Free;DictCount.Free;SM4.Free;
end;procedure TMainForm.FillDicts;
varSrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer;
beginSrcBuffer := SM4.SrcBuffer;KeyBuffer := SM4.KeyBuffer;IVBuffer := SM4.IVBuffer;DestBuffer:= SM4.DestBuffer;with DIctCm dobeginAdd('ECB' , cmECB);Add('CBC' , cmCBC);Add('CFB' , cmCFB);Add('OFB' , cmOFB);Add('PCBC', cmPCBC);Add('CTR' , cmCTR);end;with DIctPm dobeginAdd('ZERO' , pmZERO);Add('PKCS5' , pmPKCS5);Add('PKCS7' , pmPKCS7);Add('ISO10126' , pmISO10126);Add('ANSIX923' , pmANSIX923);Add('OneAndZero', pmOneAndZero);end;with DictFromStr dobeginAdd('String', SrcBuffer.FromString);Add('Hex' , SrcBuffer.FromHexString);Add('Base64', SrcBuffer.FromBase64String);end;with DictFromKey dobeginAdd('String', KeyBuffer.FromString);Add('Hex' , KeyBuffer.FromHexString);Add('Base64', KeyBuffer.FromBase64String);end;with DictFromIV dobeginAdd('String', IVBuffer.FromString);Add('Hex' , IVBuffer.FromHexString);Add('Base64', IVBuffer.FromBase64String);end;with DictFromEnc dobeginAdd('Hex' , SrcBuffer.FromHexString);Add('Base64', SrcBuffer.FromBase64String);end;with DictToEnc dobeginAdd('Hex' , DestBuffer.ToHexString);Add('Base64', DestBuffer.ToBase64String);end;with DictToDec dobeginAdd('String', DestBuffer.ToString);Add('Hex' , DestBuffer.ToHexString);Add('Base64', DestBuffer.ToBase64String);end;with DictCount dobeginAdd(EditStr, LabelStrCnt);Add(EditKey, LabelKeyCnt);Add(EditIV , LabelIVCnt );Add(EditEnc, LabelEncCnt);Add(EditDec, LabelDecCnt);end;
end;procedure TMainForm.ButtonConvertClick(Sender: TObject);
beginwith TBuffer.Create dobeginFromString(EditSrc.Text);if Sender = ButtonToHex thenEditDest.Text := ToHexStringelseEditDest.Text := ToBase64String;Free;end;
end;procedure TMainForm.ButtonFromClick(Sender: TObject);
beginwith TBuffer.Create dobeginif Sender = ButtonFromHex thenFromHexString(EditDest.Text)elseFromBase64String(EditDest.Text);EditSrc.Text := ToString;Free;end;
end;procedure TMainForm.ButtonClick(Sender: TObject);
beginSM4.CipherMode := DIctCm.Items[ComboBoxCipherMode.Text];SM4.PaddingMode := DIctPm.Items[ComboBoxPaddingMode.Text];DictFromKey.Items[ComboBoxKey.Text](EditKey.Text);DictFromIV.Items[ComboBoxIV.Text](EditIV.Text);if Sender = ButtonEncrypt thenbeginDictFromStr.Items[ComboBoxStr.Text](EditStr.Text);SM4.Encrypt;EditEnc.Text := DictToEnc.Items[ComboBoxEnc.Text]();endelsebeginDictFromEnc.Items[ComboBoxEnc.Text](EditEnc.Text);SM4.Decrypt;EditDec.Text := DictToDec.Items[ComboBoxDec.Text]();end;
end;procedure TMainForm.EditChange(Sender: TObject);
varEdit: TEdit;
beginif Sender <> nil thenbeginEdit := TEdit(Sender);DictCount.Items[Edit].Caption := Length(Edit.Text).ToString;endelsebeginfor Edit in DictCount.Keys dobeginDictCount.Items[Edit].Caption := Length(Edit.Text).ToString;end;end;
end;end.
object MainForm: TMainFormLeft = 0Top = 0Caption = 'SM4'#21152#23494#35299#23494#27979#35797ClientHeight = 452ClientWidth = 788Color = clBtnFaceFont.Charset = DEFAULT_CHARSETFont.Color = clWindowTextFont.Height = -11Font.Name = 'Tahoma'Font.Style = []OldCreateOrder = FalsePosition = poScreenCenterOnCreate = FormCreateOnDestroy = FormDestroyDesignSize = (788452)PixelsPerInch = 96TextHeight = 13object LabelBlockMode: TLabelLeft = 5Top = 108Width = 48Height = 13Caption = #21152#23494#27169#24335endobject LabelPaddingmode: TLabelLeft = 132Top = 108Width = 48Height = 13Caption = #22635#20805#27169#24335endobject LabelStr: TLabelLeft = 28Top = 149Width = 24Height = 13Caption = #26126#25991endobject LabelIV: TLabelLeft = 4Top = 234Width = 48Height = 13Caption = #21021#22987#21521#37327endobject LabelEnc: TLabelLeft = 26Top = 314Width = 24Height = 13Caption = #23494#25991endobject LabelDec: TLabelLeft = 26Top = 402Width = 24Height = 13Caption = #26126#25991endobject LabelKey: TLabelLeft = 26Top = 193Width = 24Height = 13Caption = #23494#30721endobject LabelStrCnt: TLabelLeft = 758Top = 150Width = 24Height = 13Anchors = [akTop, akRight]Caption = #23383#25968endobject LabelKeyCnt: TLabelLeft = 758Top = 192Width = 24Height = 13Anchors = [akTop, akRight]Caption = #23383#25968endobject LabelIVCnt: TLabelLeft = 758Top = 234Width = 24Height = 13Anchors = [akTop, akRight]Caption = #23383#25968endobject LabelEncCnt: TLabelLeft = 758Top = 314Width = 24Height = 13Anchors = [akTop, akRight]Caption = #23383#25968endobject LabelDecCnt: TLabelLeft = 758Top = 400Width = 24Height = 13Anchors = [akTop, akRight]Caption = #23383#25968endobject Label1: TLabelLeft = 5Top = 35Width = 48Height = 13Caption = #23545#27604#32467#26524endobject Label2: TLabelLeft = 512Top = 1Width = 28Height = 13Caption = 'String'endobject Label3: TLabelLeft = 494Top = 64Width = 89Height = 13Caption = 'Hex/Base64 [utf8]'endobject ComboBoxCipherMode: TComboBoxLeft = 59Top = 105Width = 54Height = 21TabOrder = 0Text = 'ECB'Items.Strings = ('ECB''CBC''PCBC''CFB''OFB''CTR')endobject ComboBoxPaddingmode: TComboBoxLeft = 185Top = 105Width = 68Height = 21TabOrder = 1Text = 'PKCS7'Items.Strings = ('PKCS5''PKCS7''ANSIX923''OneAndZero''ISO10126''ZERO''')endobject EditKey: TEditLeft = 119Top = 188Width = 633Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 2Text = '1234567890123456'OnChange = EditChangeendobject EditIV: TEditLeft = 119Top = 230Width = 633Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 3Text = 'abcdefghijklmnop'OnChange = EditChangeendobject EditDec: TEditLeft = 119Top = 396Width = 633Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 4OnChange = EditChangeendobject EditEnc: TEditLeft = 119Top = 310Width = 633Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 5OnChange = EditChangeendobject EditStr: TEditLeft = 119Top = 146Width = 633Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 6Text = #20808#23398#30528#35753#33258#24049#20540#38065OnChange = EditChangeendobject ButtonEncrypt: TButtonLeft = 329Top = 268Width = 137Height = 24Caption = #21152#23494TabOrder = 7OnClick = ButtonClickendobject ButtonDecrypt: TButtonLeft = 329Top = 351Width = 137Height = 24Caption = #35299#23494TabOrder = 8OnClick = ButtonClickendobject ComboBoxStr: TComboBoxLeft = 57Top = 146Width = 58Height = 21TabOrder = 9Text = 'String'Items.Strings = ('String''Hex''Base64')endobject ComboBoxKey: TComboBoxLeft = 57Top = 188Width = 58Height = 21TabOrder = 10Text = 'String'Items.Strings = ('String''Hex''Base64')endobject ComboBoxIV: TComboBoxLeft = 57Top = 230Width = 58Height = 21TabOrder = 11Text = 'String'Items.Strings = ('String''Hex''Base64')endobject ComboBoxEnc: TComboBoxLeft = 57Top = 310Width = 58Height = 21TabOrder = 12Text = 'Hex'Items.Strings = ('Hex''Base64')endobject ComboBoxDec: TComboBoxLeft = 57Top = 396Width = 58Height = 21TabOrder = 13Text = 'String'Items.Strings = ('String''Hex''Base64')endobject EditSrc: TEditLeft = 361Top = 16Width = 345Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 14Text = #20808#23398#30528#35753#33258#24049#20540#38065endobject ButtonToHex: TButtonLeft = 293Top = 41Width = 62Height = 22Caption = 'ToHex'TabOrder = 15OnClick = ButtonConvertClickendobject ButtonToBase64: TButtonLeft = 713Top = 41Width = 65Height = 22Anchors = [akTop, akRight]Caption = 'ToBase64'TabOrder = 16OnClick = ButtonConvertClickendobject EditDest: TEditLeft = 361Top = 41Width = 345Height = 21Anchors = [akLeft, akTop, akRight]TabOrder = 17endobject RzEdit1: TEditLeft = 57Top = 32Width = 196Height = 21TabOrder = 18Text = 'https://javalang.cn/crypto/sm4.html'endobject ButtonFromHex: TButtonLeft = 294Top = 16Width = 61Height = 22Caption = 'FromHex'TabOrder = 19OnClick = ButtonFromClickendobject ButtonFramBase64: TButtonLeft = 713Top = 15Width = 65Height = 22Anchors = [akTop, akRight]Caption = 'FromBase64'TabOrder = 20OnClick = ButtonFromClickend
end
unit uSM4;interfaceuses
{$IF CompilerVersion <= 22}Forms, Classes, Windows, SysUtils, NetEncoding,
{$ELSE}Vcl.Forms, System.Classes, Winapi.Windows, System.SysUtils, System.NetEncoding,
{$ENDIF}uConst, uBuffer;typeTCipherMode = (cmECB, cmCBC, cmCFB, cmOFB, cmPCBC, cmCTR);TPaddingMode = (pmZERO, pmPKCS5, pmPKCS7, pmISO10126, pmANSIX923, pmOneAndZero);TSM4 = class(TObject)private typeTBlock = array[0..4 -1] of UInt32;TRoundKey = array[0..32-1] of UInt32;TCryptType = (ctEncrypt, ctDecrypt);TProc = procedure (var B: TBlock) of object;privateRK: TRoundKey;MK, DataBlock, IVBlock: TBlock;BlockSize, BlockLen, RKLen: Integer;function SBoxMap(A: UInt32): UInt32;function ROTL(A: UInt32; N: UInt16): UInt32;procedure CryptBlock(const CryptType: TCryptType; var A: TBlock);procedure ExpandKey;procedure ReverseEndian(var A: TBlock);procedure AddPadding;procedure RemovePadding;procedure CheckKeyBufferAndIVBuffer;procedure XorBlock(var A: TBlock; const B: TBlock); overload;procedure XorBlock(var A: TBlock; const B, C: TBlock); overload;procedure IncBlock(Var A: TBlock);procedure Crypt(CryptType: TCryptType);publicCipherMode : TCipherMode;PaddingMode: TPaddingMode;SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer;procedure Encrypt;procedure Decrypt;constructor Create; overload;constructor Create(aCipherMode: TCipherMode; aPaddingMode: TPaddingMode); overload;destructor Destroy; override;end;implementationconstructor TSM4.Create;
begininherited;SrcBuffer := TBuffer.Create;KeyBuffer := TBuffer.Create;IVBuffer := TBuffer.Create;DestBuffer := TBuffer.Create;CipherMode := cmECB;PaddingMode:= pmPKCS7;BlockSize := SizeOf(TBlock); //16BlockLen := SizeOf(TBlock) div Sizeof(UInt32); //4RKLen := SizeOf(TRoundKey) div Sizeof(UInt32); //32
end;constructor TSM4.Create(aCipherMode: TCipherMode; aPaddingMode: TPaddingMode);
beginCreate;CipherMode := aCipherMode;PaddingMode := aPaddingMode;
end;destructor TSM4.Destroy;
beginSrcBuffer.Free;KeyBuffer.Free;IVBuffer.Free;DestBuffer.Free;inherited;
end;procedure TSM4.Encrypt;
beginAddPadding;Crypt(ctEncrypt);
end;procedure TSM4.Decrypt;
beginCrypt(ctDecrypt);RemovePadding;
end;procedure TSM4.Crypt(CryptType: TCryptType);
varInBlock: TBlock;DataLen, OffSet: Integer;
beginCheckKeyBufferAndIVBuffer;DataLen := SrcBuffer.Length;DestBuffer.Length := DataLen;KeyBuffer.ToBytes(MK, BlockSize);if CipherMode <> cmECB thenbeginIVBuffer.ToBytes(IVBlock, BlockSize);end;ExpandKey;OffSet := 0;while OffSet < DataLen dobeginSrcBuffer.OffsetToBytes(OffSet, DataBlock, BlockSize);case CipherMode ofcmECB: //Electronic Codebook (ECB)beginCryptBlock(CryptType, DataBlock);end;cmCBC: //Cipher Block Chaining (CBC)beginif CryptType = ctEncrypt thenbeginXorBlock(DataBlock, IVBlock);CryptBlock(CryptType, DataBlock);IVBlock := DataBlock;endelsebeginInBlock := DataBlock;CryptBlock(CryptType, DataBlock);XorBlock(DataBlock, IVBlock);IVBlock := InBlock;end;end;cmPCBC: //Propagating Cipher Block Chaining (PCBC)beginInBlock := DataBlock;if CryptType = ctEncrypt thenbeginXorBlock(DataBlock, IVBlock);CryptBlock(CryptType, DataBlock);endelsebeginCryptBlock(CryptType, DataBlock);XorBlock(DataBlock, IVBlock);end;XorBlock(IVBlock, DataBlock, InBlock);end;cmCFB: //Cipher Feedback (CFB)beginInBlock := DataBlock;CryptBlock(ctEncrypt, IVBlock); //不管是加密还是解密,这里都是用EncryptBlock对IV进行操作XorBlock(DataBlock, IVBlock);if CryptType = ctEncrypt thenIVBlock := DataBlockelseIVBlock := InBlock;end;cmOFB: //Output Feedback (OFB)beginCryptBlock(ctEncrypt, IVBlock); //不管是加密还是解密,这里都是用EncryptBlock对IV进行操作XorBlock(DataBlock, IVBlock);end;cmCTR: //Counter (CTR)beginInBlock := IVBlock;CryptBlock(ctEncrypt, InBlock); //不管是加密还是解密,这里都是用EncryptBlock对IV进行操作XorBlock(DataBlock, InBlock);IncBlock(IVBlock);end;end;DestBuffer.OffsetFromBytes(OffSet, DataBlock, BlockSize);Inc(OffSet, BlockSize);end;
end;procedure TSM4.AddPadding;
varI, M, N, Len, NewLen: Integer;
beginLen := Length(SrcBuffer.Data);M := (Len mod BlockSize);if (M = 0) and (PaddingMode = pmZero) thenN := 0elseN := BlockSize - M;NewLen := Len + N;SetLength(SrcBuffer.Data, NewLen);case PaddingMode ofpmPKCS5, pmPKCS7:beginfor I := Len to NewLen-1 do SrcBuffer.Data[I] := N;end;pmANSIX923:beginfor I := Len to NewLen-1 dobeginif I < NewLen-1 thenSrcBuffer.Data[I] := 0elseSrcBuffer.Data[I] := N;end;end;pmISO10126:beginRandomize;for I := Len to NewLen-1 dobeginif I < NewLen-1 thenSrcBuffer.Data[I] := Random(255)elseSrcBuffer.Data[I] := N;end;end;pmZERO:beginfor I := Len to NewLen-1 do SrcBuffer.Data[I] := 0;end;pmOneAndZero:beginfor I := Len to NewLen-1 dobeginif I = Len thenSrcBuffer.Data[I] := $80elseSrcBuffer.Data[I] := 0;end;end;end;
end;procedure TSM4.RemovePadding;
varI, M, Len: Integer;
beginLen := Length(DestBuffer.Data);case PaddingMode ofpmPKCS5, pmPKCS7, pmANSIX923, pmISO10126:beginM := DestBuffer.Data[Len-1];SetLength(DestBuffer.Data, Len-M);end;pmZERO:beginfor I := Len-1 downto 0 dobeginif DestBuffer.Data[I] = 0 thenDec(Len)elseBreak;end;SetLength(DestBuffer.Data, Len);end;pmOneAndZero:beginfor I := Len-1 downto 0 dobeginif DestBuffer.Data[I] <> $80 thenDec(Len)elseBreak;end;SetLength(DestBuffer.Data, Len-1);end;end;
end;procedure TSM4.CheckKeyBufferAndIVBuffer;
varI, Len: Integer;
beginLen := Length(KeyBuffer.Data);SetLength(KeyBuffer.Data, BlockSize);for I := Len to BlockSize - 1 dobeginKeyBuffer.Data[I] := 0;end;if CipherMode <> cmECB thenbeginLen := Length(IVBuffer.Data);SetLength(IVBuffer.Data, BlockSize);for I := Len to BlockSize - 1 dobeginIVBuffer.Data[I] := 0;end;end;
end;procedure TSM4.ExpandKey;function Transform(A: UInt32): UInt32;beginA := SBoxMap(A); //合成置换Result := A xor ROTL(A, 13) xor ROTL(A, 23); //线性变换end;
varI, M0, M1, M2, M3: Integer;K: TBlock;
beginReverseEndian(MK); //32整数位大小端转换for I := 0 to BlockLen-1 dobeginK[I] := MK[I] xor FK[I];end;for I := 0 to RKLen-1 do //32次迭代运算beginM0 := (I+0) mod 4;M1 := (I+1) mod 4;M2 := (I+2) mod 4;M3 := (I+3) mod 4;K[M0] := K[M0] xor Transform(K[M1] xor K[M2] xor K[M3] xor CK[I]); //迭代运算RK[I] := K[M0];end;
end;procedure TSM4.CryptBlock(const CryptType: TCryptType; var A: TBlock);function TransForm(A: UInt32): UInt32;beginA := SBoxMap(A); //合成置换Result := A xor ROTL(A, 2) xor ROTL(A, 10) xor ROTL(A, 18) xor ROTL(A, 24); //线性变换end;
varT: TBlock;I, R, M0, M1, M2, M3: UInt32;
beginReverseEndian(A); //32位整数大小端转换for I := 0 to RKLen-1 do //32次迭代运算beginif CryptType = ctEncrypt thenR := IelseR := 31 - I; //解密时使用反序的RKM0 := (I ) mod 4;M1 := (I+1) mod 4;M2 := (I+2) mod 4;M3 := (I+3) mod 4;A[M0] := A[M0] xor TransForm(A[M1] xor A[M2] xor A[M3] xor RK[R]); //迭代运算end;ReverseEndian(A); //32整数位大小端转换T := A;for I := 0 to 3 do //Block内部的4个32位整数的位置做反序变换beginA[I] := T[3 - I];end;
end;function TSM4.SBoxMap(A: UInt32): UInt32; //S盒非线性变换(Byte to Byte的固定映射}
varI: Integer;B: array[0..3] of Byte absolute A;R: array[0..3] of Byte absolute Result;
beginfor I := 0 to 3 dobeginR[I] := SBox[B[I]];end;
end;procedure TSM4.ReverseEndian(var A: TBlock); //32位大小端变换
varI: Integer;
beginfor I := 0 to BlockLen-1 dobeginA[I] := ((A[I] and $FF000000) shr 24) or((A[I] and $00FF0000) shr 8 ) or((A[I] and $0000FF00) shl 8 ) or((A[I] and $000000FF) shl 24);end;
end;function TSM4.ROTL(A: UInt32; N: UInt16): UInt32; //32位循环左移N位
varI: UInt16;
beginfor I := 0 to N - 1 dobeginA := A shl 1 or (A shr 31);end;Result := A;
end;procedure TSM4.XorBlock(var A: TBlock; const B: TBlock); //两个Block异或
varI: Integer;
beginfor I := 0 to BlockLen-1 dobeginA[I] := A[I] xor B[I];end;
end;procedure TSM4.XorBlock(var A: TBlock; const B, C: TBlock); //两个Block异或
varI: Integer;
beginfor I := 0 to BlockLen-1 dobeginA[I] := B[I] xor C[I];end;
end;procedure TSM4.IncBlock(var A: TBlock); //Block值加1
varI: Integer;
beginfor I := 0 to BlockLen-1 dobeginif I > 0 thenbeginA[I-1] := 0;end;if (I = BlockLen-1) or (A[I] < $FFFFFFFF) thenbeginInc(A[0]);Exit;end;end;
end;end.
unit uConst;interfaceconst
// S盒Sbox: array[0..255] of Byte = ($d6,$90,$e9,$fe,$cc,$e1,$3d,$b7,$16,$b6,$14,$c2,$28,$fb,$2c,$05,$2b,$67,$9a,$76,$2a,$be,$04,$c3,$aa,$44,$13,$26,$49,$86,$06,$99,$9c,$42,$50,$f4,$91,$ef,$98,$7a,$33,$54,$0b,$43,$ed,$cf,$ac,$62,$e4,$b3,$1c,$a9,$c9,$08,$e8,$95,$80,$df,$94,$fa,$75,$8f,$3f,$a6,$47,$07,$a7,$fc,$f3,$73,$17,$ba,$83,$59,$3c,$19,$e6,$85,$4f,$a8,$68,$6b,$81,$b2,$71,$64,$da,$8b,$f8,$eb,$0f,$4b,$70,$56,$9d,$35,$1e,$24,$0e,$5e,$63,$58,$d1,$a2,$25,$22,$7c,$3b,$01,$21,$78,$87,$d4,$00,$46,$57,$9f,$d3,$27,$52,$4c,$36,$02,$e7,$a0,$c4,$c8,$9e,$ea,$bf,$8a,$d2,$40,$c7,$38,$b5,$a3,$f7,$f2,$ce,$f9,$61,$15,$a1,$e0,$ae,$5d,$a4,$9b,$34,$1a,$55,$ad,$93,$32,$30,$f5,$8c,$b1,$e3,$1d,$f6,$e2,$2e,$82,$66,$ca,$60,$c0,$29,$23,$ab,$0d,$53,$4e,$6f,$d5,$db,$37,$45,$de,$fd,$8e,$2f,$03,$ff,$6a,$72,$6d,$6c,$5b,$51,$8d,$1b,$af,$92,$bb,$dd,$bc,$7f,$11,$d9,$5c,$41,$1f,$10,$5a,$d8,$0a,$c1,$31,$88,$a5,$cd,$7b,$bd,$2d,$74,$d0,$12,$b8,$e5,$b4,$b0,$89,$69,$97,$4a,$0c,$96,$77,$7e,$65,$b9,$f1,$09,$c5,$6e,$c6,$84,$18,$f0,$7d,$ec,$3a,$dc,$4d,$20,$79,$ee,$5f,$3e,$d7,$cb,$39,$48);// 密钥扩展算法的常数FKFK: array[0..3] of UInt32 = ($a3b1bac6, $56aa3350, $677d9197, $b27022dc);// 密钥扩展算法的固定参数CKCK: array[0..31] of UInt32 = ($00070e15, $1c232a31, $383f464d, $545b6269,$70777e85, $8c939aa1, $a8afb6bd, $c4cbd2d9,$e0e7eef5, $fc030a11, $181f262d, $343b4249,$50575e65, $6c737a81, $888f969d, $a4abb2b9,$c0c7ced5, $dce3eaf1, $f8ff060d, $141b2229,$30373e45, $4c535a61, $686f767d, $848b9299,$a0a7aeb5, $bcc3cad1, $d8dfe6ed, $f4fb0209,$10171e25, $2c333a41, $484f565d, $646b7279
);implementationend.
unit uBuffer;interfaceuses
{$IF CompilerVersion <= 22}Forms, Classes, Windows, SysUtils, NetEncoding;
{$ELSE}Vcl.Forms, System.Classes, Winapi.Windows, System.SysUtils, System.NetEncoding;
{$ENDIF}typeTBuffer = classprivatefunction GetDataLength: Integer; inline;procedure SetDataLength(Len: Integer); inline;function GetItem(Index: Integer): Byte; inline;procedure SetItem(Index: Integer; Value: Byte); inline;publicData: TBytes; //TBytes = array of Byte;procedure FromString(const Str: String); overload; //默认为utf8procedure FromString(const Str: String; Encoding: TEncoding); overload;procedure FromHexString(const Str: String);procedure FromDelimitedHexString(HexStr: String; Prefix: String = '$'; Delimitor: String = ',');procedure FromBase64String(const Str: String);procedure FromBytes(const InBytes: TBytes; ByteLen: Integer = -1); overload;procedure FromBytes(const InBytes: array of Byte; ByteLen: Integer = -1); overload;procedure FromBytes(const Ints: array of UInt32; ByteLen: Integer); overload;procedure OffsetFromBytes(Offset: Integer; const Ints: array of UInt32; ByteLen: Integer);procedure FromStream(const Stream: TStream; ByteLen: Integer = -1);procedure FromFile(const FileName: String);function ToString: String; reintroduce; overload; //默认为utf8function ToString(Encoding: TEncoding): String; reintroduce; overload;function ToHexString: String;function ToDelimitedHexString(Prefix: String = '$'; Delimitor: String = ', '): String;function ToBase64String: String;procedure ToBytes(var OutBytes: TBytes; ByteLen: Integer = -1); overload;procedure ToBytes(var OutBytes: array of Byte; ByteLen: Integer = -1); overload;procedure ToBytes(var Ints: array of UInt32; ByteLen: Integer = 1); overload;procedure OffsetToBytes(Offset: Integer; var Ints: array of UInt32; ByteLen: Integer);procedure ToStream(const Stream: TStream);procedure ToFile(const FileName: String; Warning: Boolean = True);property Length: Integer read GetDataLength write SetDataLength;property Bytes[Index: Integer]: Byte read GetItem write SetItem; default;end;resourcestringSInvalidBufSize = 'Invalid buffer size for ouput';implementationfunction TBuffer.GetDataLength: Integer;
beginResult := System.Length(Data);
end;procedure TBuffer.SetDataLength(Len: Integer);
beginSystem.SetLength(Data, Len);
end;function TBuffer.GetItem(Index: Integer): Byte;
beginResult := Data[Index];
end;
procedure TBuffer.SetItem(Index: Integer; Value: Byte);
beginData[Index] := Value;
end;procedure TBuffer.FromString(const Str: String);
beginData := TEncoding.UTF8.GetBytes(Str);
end;procedure TBuffer.FromString(const Str: String; Encoding: TEncoding);
beginData := Encoding.GetBytes(Str);
end;procedure TBuffer.FromHexString(const Str: String);
varLen: Integer;
beginLen := System.Length(Str) div 2;SetLength(Data, Len);HexToBin(PChar(Str), @Data[0], Len)
end;procedure TBuffer.FromDelimitedHexString(HexStr: String; Prefix: String; Delimitor: String);
varLen: Integer;
beginHexStr := HexStr.Replace(Prefix , '');HexStr := HexStr.Replace(Delimitor , '');HexStr := HexStr.Replace(' ' , '');Len := System.Length(HexStr) div 2;SetLength(Data, Len);HexToBin(PChar(HexStr), @Data[0], Len)
end;procedure TBuffer.FromBase64String(const Str: String);
varBase64Encoding: TBase64Encoding;
begin
//Base64Encoding := TBase64Encoding.Create; //含换行符Base64Encoding := TBase64Encoding.Create(0); //不含换行符Data := Base64Encoding.DecodeStringToBytes(Str);Base64Encoding.Free;
end;procedure TBuffer.FromBytes(const InBytes: array of Byte; ByteLen: Integer);
beginif (ByteLen = -1) then ByteLen := System.Length(InBytes);SetLength(Data, ByteLen);Move(InBytes[0], Data[0], ByteLen);
end;procedure TBuffer.FromBytes(const InBytes: TBytes; ByteLen: Integer);
beginif (ByteLen = -1) then ByteLen := System.Length(InBytes);SetLength(Data, ByteLen);Move(InBytes[0], Data[0], ByteLen);
end;procedure TBuffer.FromBytes(const Ints: array of UInt32; ByteLen: Integer);
beginSetLength(Data, ByteLen);Move(Ints[0], Data[0], ByteLen);
end;procedure TBuffer.OffsetFromBytes(Offset: Integer; const Ints: array of UInt32; ByteLen: Integer);
beginMove(Ints[0], Data[Offset], ByteLen);
end;procedure TBuffer.FromStream(const Stream: TStream; ByteLen: Integer);
beginif (ByteLen = -1) then ByteLen := Stream.Size;SetLength(Data, ByteLen);Stream.Read(Data, ByteLen);
end;procedure TBuffer.FromFile(const FileName: String);
varStream: TFileStream;
beginStream := TFileStream.Create(FileName, fmOpenRead);SetLength(Data, Stream.Size);Stream.Read(Data, Stream.Size);Stream.Free;
end;function TBuffer.ToString: String;
beginResult := TEncoding.UTF8.GetString(Data);
end;function TBuffer.ToString(Encoding: TEncoding): String;
beginResult := Encoding.GetString(Data);
end;function TBuffer.ToHexString: String;
varLen: Integer;
beginLen := System.Length(Data);SetLength(Result, 2*Len);BinToHex(@Data[0], PChar(Result), Len);
end;function TBuffer.ToDelimitedHexString(Prefix: String; Delimitor: String): String;
varI, Len: Integer;
beginResult := '';Len := System.Length(Data);for I := 0 to Len-1 dobeginResult := Result + Prefix + IntToHex(Data[I], 2);if I < Len-1 thenResult := Result + Delimitor;end;
end;function TBuffer.ToBase64String: String;
varBase64Encoding: TBase64Encoding;
begin
//Base64Encoding := TBase64Encoding.Create; //含换行符Base64Encoding := TBase64Encoding.Create(0); //不含换行符Result := Base64Encoding.EncodeBytesToString(Data);Base64Encoding.Free;
end;procedure TBuffer.ToBytes(var OutBytes: array of Byte; ByteLen: Integer);
beginif (ByteLen = -1) then ByteLen := System.Length(Data);if (ByteLen > System.Length(OutBytes)) thenraise Exception.Create(SInvalidBufSize);Move(Data[0], OutBytes[0], ByteLen);
end;procedure TBuffer.ToBytes(var OutBytes: TBytes; ByteLen: Integer);
beginif ByteLen = -1 then ByteLen := System.Length(Data);SetLength(OutBytes, ByteLen);Move(Data[0], OutBytes[0], ByteLen);
end;procedure TBuffer.ToBytes(var Ints: array of UInt32; ByteLen: Integer);
beginif ByteLen = -1 then ByteLen := System.Length(Data);Move(Data[0], Ints[0], ByteLen);
end;procedure TBuffer.OffsetToBytes(Offset: Integer; var Ints: array of UInt32; ByteLen: Integer);
beginMove(Data[Offset], Ints[0], ByteLen);
end;procedure TBuffer.ToStream(const Stream: TStream);
beginStream.Write(Data, System.Length(Data));
end;procedure TBuffer.ToFile(const FileName: String; Warning: Boolean);
varStream: TFileStream;
beginif Warning and FileExists(FileName) and(Application.MessageBox(PChar('File ' + FileName + ' Exists, Overwrite It?'),'Warning: File Exists', MB_YESNO) = IDNO) then Exit;Stream := TFileStream.Create(FileName, fmCreate);Stream.Write(Data, System.Length(Data));Stream.Free;
end;end.