Registriert seit: 21. Jul 2002
Ort: Bonn
5.403 Beiträge
Turbo Delphi für Win32
|
Re: RC4-Verschlüsselung
18. Apr 2005, 16:27
negaH hat diesen Algorithmus erweitert von RC4 auf RCx:
(Die objektorientierte Kapselung der Funktionen inklusive Stromdekorier von maximov findet man hier)
Ok, hier meine RCx Abwandlung von RC4
Delphi-Quellcode:
{
Copyright: 2002 Hagen Reddmann
Author: Hagen Reddmann, HaReddmann bei T-Online punkt de
Remarks: All rights reserved
Version: open source, developed on D5
Description: derivate of RC5 stream cipher with internal cipher feedback and stronger keysetup
includes secure one way pseudo random number generator
}
unit RCx;
{$A+,B-,C-,D-,E-,F-,G+,H+,I-,J+,K-,L-,M-,N+,O+,P+,Q-,R-,S-,T-,U+,V+,W-,X+,Y-,Z1}
interface
type
TRCxContext = record
D: array[Byte] of Byte;
I,J,F: Byte;
end;
procedure RCxInit( var RCx: TRCxContext; const Key; KeySize: Integer); overload;
procedure RCxInit( var RCx: TRCxContext; const Key: String); overload;
procedure RCxEncode( var RCx: TRCxContext; const Source; var Dest; Count: Integer); overload;
function RCxEncode( var RCx: TRCxContext; const Value: String): String; overload;
procedure RCxDecode( var RCx: TRCxContext; const Source; var Dest; Count: Integer); overload;
function RCxDecode( var RCx: TRCxContext; const Value: String): String; overload;
procedure RCxDone( var RCx: TRCxContext); overload;
// all in one encode/decode
function RCxEncode( const Value, Password: String): String; overload;
function RCxDecode( const Value, Password: String): String; overload;
// random number generator based on RCx
procedure RCxSeed( const Seed; SeedSize: Integer); overload;
procedure RCxSeed( const Seed: String); overload;
procedure RCxRandomize; overload;
function RCxRandom(Range: Cardinal = 0): Cardinal; overload;
function RCxRandomString(Length: Integer): String; overload;
implementation
type
PByteArray = ^TByteArray;
TByteArray = array[0..MaxInt -1] of Byte;
procedure RCxInit( var RCx: TRCxContext; const Key; KeySize: Integer);
var
R,S,T,K: Byte;
L: Integer;
M: array[Byte] of Byte;
begin
with RCx do
try
L := 0;
for S := 0 to 255 do
begin
D[S] := S;
M[S] := TByteArray(Key)[S mod KeySize] xor L;
L := (L + M[S] * 257) mod MaxInt +1;
end;
I := 0;
J := 0;
R := L;
F := L shr 8;
for S := 0 to 255 do
begin
Inc(R, D[S] + M[S]);
T := D[S];
D[S] := D[R];
D[R] := T;
end;
finally
R := 0;
S := 0;
T := 0;
L := 0;
FillChar(M, SizeOf(M), 0);
end;
end;
procedure RCxInit( var RCx: TRCxContext; const Key: String);
begin
RCxInit(RCx, Pointer(Key)^, Length(Key));
end;
procedure RCxDone( var RCx: TRCxContext);
begin
FillChar(RCx, SizeOf(RCx), 0);
end;
procedure RCxEncode( var RCx: TRCxContext; const Source; var Dest; Count: Integer);
var
S: TByteArray absolute Source;
O: TByteArray absolute Dest;
C: Integer;
T,K: Byte;
begin
with RCx do
for C := 0 to Count -1 do
begin
Inc(I);
T := D[I];
Inc(J, T);
D[I] := D[J] xor F;
D[J] := T - F;
Inc(T, D[I]);
K := S[C];
O[C] := K xor D[T];
F := F xor K;
end;
end;
procedure RCxDecode( var RCx: TRCxContext; const Source; var Dest; Count: Integer);
var
S: TByteArray absolute Source;
O: TByteArray absolute Dest;
C: Integer;
T,K: Byte;
begin
with RCx do
for C := 0 to Count -1 do
begin
Inc(I);
T := D[I];
Inc(J, T);
D[I] := D[J] xor F;
D[J] := T - F;
Inc(T, D[I]);
K := S[C] xor D[T];
O[C] := K;
F := F xor K;
end;
end;
function RCxEncode( var RCx: TRCxContext; const Value: String): String;
var
Count: Integer;
begin
Count := Length(Value);
SetLength(Result, Count);
RCxEncode(RCx, Value[1], Result[1], Count);
end;
function RCxDecode( var RCx: TRCxContext; const Value: String): String;
var
Count: Integer;
begin
Count := Length(Value);
SetLength(Result, Count);
RCxDecode(RCx, Value[1], Result[1], Count);
end;
function RCxEncode( const Value, Password: String): String;
var
RCx: TRCxContext;
begin
RCxInit(RCx, Password);
try
Result := RCxEncode(RCx, Value);
finally
RCxDone(RCx);
end;
end;
function RCxDecode( const Value, Password: String): String;
var
RCx: TRCxContext;
begin
RCxInit(RCx, Password);
try
Result := RCxDecode(RCx, Value);
finally
RCxDone(RCx);
end;
end;
var
FRCxRegister: TRCxContext;
procedure RCxSeed( const Seed; SeedSize: Integer);
begin
RCxInit(FRCxRegister, Seed, SeedSize);
end;
procedure RCxSeed( const Seed: String);
begin
RCxSeed(Pointer(Seed)^, Length(Seed));
end;
procedure RCxRandomize;
var
Tick: Cardinal;
begin
Tick := GetTickCount;
FRCxRegister.F := Tick;
FRCxRegister.I := Tick shr 8;
FRCxRegister.J := Tick shr 16;
RCxEncode(FRCxRegister, FRCxRegister.D, FRCxRegister.D, SizeOf(FRCxRegister.D));
end;
function RCxRandom(Range: Cardinal): Cardinal;
type
PCardinal = ^Cardinal;
begin
RCxEncode(FRCxRegister, FRCxRegister.D, FRCxRegister.D, SizeOf(FRCxRegister.D));
Result := PCardinal(@FRCxRegister.D)^;
if Range > 1 then Result := Result mod Range;
end;
function RCxRandomString(Length: Integer): String;
var
I: Integer;
begin
SetLength(Result, Length);
for I := 1 to Length do
begin
RCxEncode(FRCxRegister, FRCxRegister.D, FRCxRegister.D, SizeOf(FRCxRegister.D));
Result[I] := Char(FRCxRegister.D[0]);
end;
end;
const
FRCxSeed: TGUID = ' {F4D35205-2B59-42B0-8B8F-239855B6DD2B}';
initialization
RCxSeed(FRCxSeed, SizeOf(FRCxSeed));
finalization
end.
Um die Unterschiede zu sehen mal eine Testfunktion
Delphi-Quellcode:
procedure TestRCx;
const
Msg = 'Test Nachricht';
Key = 'Test';
var
Salt,RCx_C,RCx_D,RC4_C,RC4_D: String;
begin
Salt := RCxRandomString(1);
RCx_C := RCxEncode(Salt + Msg, Key);
RCx_D := RCxDecode(RCx_C , Key);
RC4_C := RC4Code (Salt + Msg, Key);
RC4_D := RC4Code (RC4_D , Key);
Delete(RCx_D, 1, Length(Salt));
Delete(RC4_D, 1, Length(Salt));
WriteLn('Salt: ', HexString(Salt), ' RCx_C: ', HexString(RCx_C), ' RC4_C: ', HexString(RC4_C));
end;
Dabei wird nur das erste Zeichen der zu verschlüsselten Nachricht um ein Zufallsbyte erweitert. Und nachfolgend der mehrfache Aufruf dieser Testfunktion:
Delphi-Quellcode:
Salt: D1 RCx_C: 7DCAB77ACA0C74FA2F8DDBFC3E67A6 RC4_C: AB3A283B361B402B17DFC96130F3EF
Salt: 8A RCx_C: 2634AFC68997688D5952A17D5A13FF RC4_C: F03A283B361B402B17DFC96130F3EF
Salt: FA RCx_C: 569858AC2C3BF3099957176055BD5C RC4_C: 803A283B361B402B17DFC96130F3EF
Salt: F1 RCx_C: 5D9A9E1FE91539F2AD9ADBB93D36A0 RC4_C: 8B3A283B361B402B17DFC96130F3EF
Salt: 3A RCx_C: 968344C42342DD6DEADE1735000B06 RC4_C: 403A283B361B402B17DFC96130F3EF
Salt: 7A RCx_C: D60C9A59B8959E0E8468175AB6DEE4 RC4_C: 003A283B361B402B17DFC96130F3EF
Was nun ganz deutlich auffällt ist das der CipherText des RC4 sich wirklich nur im ersten Zufallszeichen unterscheidet. Alle anderen Bytes der gleichen Nachricht werden auch immer gleich verschlüsselt bei gleichem Passwort. Mein RCx wiederrum hat dieses Merkmal nicht weil es eben die Nachrichtenbytes auch indirekt in den internen Status des RCxContext einrechnet. Dies nennt man Lawineneffekt und erhöht die Sicherheit enorm, denn nur leicht geänderte Nachricht bei gleichem Passwort erzeugt vollständig anderen Ciphertext. Alledings hat es auch einen Nachteil !! denn es wird die "Selbstsynchronisation" bei fehlerhaften Datenübertragungen zerstört. D.h. wird nur 1 Bit im Datenstrom falsch übertragen so wird der komplette Rest der verschlüsselten Nachricht eben auch falsch entschlüsselt. Das kann bei RC4 nicht passieren.
Wie man aber erkennt reicht eine Nachrichtenexpansion um Zufallsbytes bei RC4 nicht aus. Auch ein zufälliger IV ist nur eine virtuelle äußere Nachrichtenexpansion. Man muß also wirklich den RC4 Algorithmus abändern um ähnliche Effekte und Sicherheiten wie bei Blockciphern üblich zu erhalten.
Auch die Expansion des Passwortes durch einen Salt beim RC4 würde dieses Problem NICHT beseitigen !
Einen zweiten "Nachteil" erkennt man auf den ersten Blick: Statt nur einer Ver- und Entschlüsselungsfunktion wie beim RC4 benötigt man nun zwei leicht unterschiedliche Funktonen für Ver- und Entschlüsselung separat.
Am besten ist es nun vor dem Verschlüsseln der Nachrichten immer einen 16 Bytes langen Zufallsstring zu verschlüsseln und in den Ciphertext einfließen zulassen. Man expandiert also jede Nachricht amAnfang um 16 ZUfallsbytes. Bei der Entschlüsselung wird der nun zusammengesetzte CipheText in einem Rutsch entschlüsselt und die ersten 16 Bytes einfach entfernt.
Etwa so:
Delphi-Quellcode:
begin
CipherText := RCxEncode(RCxRandomString(16) + 'Nachricht', 'Passwort');
PlainText := RCxDecode(CipherText, 'Passwort');
Delete(PlainText, 1, 16);
end;
Diese Vorgehensweise bettet den Zufallswert IN die Verschlüsselung ein. Das ist eingewaltiger Unterschied zu den Init Vektoren bei normalen Blockverschlüsselungen. Diese IV's sind nämlich in der CipherText Nachricht 1 zu 1 lesebar gespeichert, also nur äußere Merkmale.
Gruß Hagen
|