Thema: Delphi RC4-Verschlüsselung

Einzelnen Beitrag anzeigen

CalganX

Registriert seit: 21. Jul 2002
Ort: Bonn
5.403 Beiträge
 
Turbo Delphi für Win32
 

Re: RC4-Verschlüsselung

  Alt 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
Angehängte Dateien
Dateityp: pas rcx_159.pas (5,6 KB, 632x aufgerufen)
  Mit Zitat antworten Zitat