{ $Project$ $Workfile$ $Revision$ $DateUTC$ $Id$ This file is part of the Indy (Internet Direct) project, and is offered under the dual-licensing agreement described on the Indy website. (http://www.indyproject.org/) Copyright: (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. } { $Log$ } { Rev 1.4 5/12/2003 12:30:58 AM GGrieve Get compiling again with DotNet Changes Rev 1.3 10/12/2003 1:49:26 PM BGooijen Changed comment of last checkin Rev 1.2 10/12/2003 1:43:24 PM BGooijen Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc Rev 1.0 11/14/2002 02:13:56 PM JPMugaas } unit IdBlockCipherIntercept; { UnitName: IdBlockCipherIntercept Author: Andrew P.Rybin [magicode@mail.ru] Creation: 27.02.2002 Version: 0.9.0b Purpose: Secure communications } interface {$i IdCompilerDefines.inc} uses Classes, IdGlobal, IdException, IdResourceStringsProtocols, IdIntercept; const IdBlockCipherBlockSizeDefault = 16; IdBlockCipherBlockSizeMax = 256; // why 256? not any block ciphers that can - or should - be used beyond this // length. You can extend this if you like. But the longer it is, the // more network traffic is wasted //256, as currently the last byte of the block is used to store the block size type TIdBlockCipherIntercept = class; // OnSend and OnRecieve Events will always be called with a blockSize Data TIdBlockCipherIntercept = class(TIdConnectionIntercept) protected FBlockSize: Integer; FIncoming : TIdBytes; procedure Decrypt (var VData : TIdBytes); virtual; procedure Encrypt (var VData : TIdBytes); virtual; procedure SetBlockSize(const Value: Integer); procedure InitComponent; override; public procedure Receive(var VBuffer: TIdBytes); override; //Decrypt procedure Send(var VBuffer: TIdBytes); override; //Encrypt procedure CopySettingsFrom (ASrcBlockCipherIntercept: TIdBlockCipherIntercept); // warning: copies Data too published property BlockSize: Integer read FBlockSize write SetBlockSize default IdBlockCipherBlockSizeDefault; end; TIdServerBlockCipherIntercept = class(TIdServerIntercept) protected FBlockSize: Integer; procedure InitComponent; override; public procedure Init; override; function Accept(AConnection: TComponent): TIdConnectionIntercept; override; published property BlockSize: Integer read FBlockSize write FBlockSize default IdBlockCipherBlockSizeDefault; end; EIdBlockCipherInterceptException = EIdException; {block length} implementation uses IdResourceStrings, SysUtils; { TIdBlockCipherIntercept } //const // bitLongTail = $80; //future: for IdBlockCipherBlockSizeMax>256 procedure TIdBlockCipherIntercept.Encrypt(var VData : TIdBytes); begin if Assigned(FOnSend) then begin FOnSend(Self, VData); end;//ex: EncryptAES(LTempIn, ExpandedKey, LTempOut); end; procedure TIdBlockCipherIntercept.Decrypt(var VData : TIdBytes); Begin if Assigned(FOnReceive) then begin FOnReceive(Self, VData); end;//ex: DecryptAES(LTempIn, ExpandedKey, LTempOut); end; procedure TIdBlockCipherIntercept.Send(var VBuffer: TIdBytes); var LSrc, LBlock : TIdBytes; LSize, LCount, LMaxDataSize: Integer; LCompleteBlocks, LRemaining: Integer; begin LSrc := nil; // keep the compiler happy LSize := Length(VBuffer); if LSize > 0 then begin LSrc := VBuffer; LMaxDataSize := FBlockSize - 1; SetLength(VBuffer, ((LSize + LMaxDataSize - 1) div LMaxDataSize) * FBlockSize); SetLength(LBlock, FBlockSize); LCompleteBlocks := LSize div LMaxDataSize; LRemaining := LSize mod LMaxDataSize; //process all complete blocks for LCount := 0 to LCompleteBlocks-1 do begin CopyTIdBytes(LSrc, LCount * LMaxDataSize, LBlock, 0, LMaxDataSize); LBlock[LMaxDataSize] := LMaxDataSize; Encrypt(LBlock); CopyTIdBytes(LBlock, 0, VBuffer, LCount * FBlockSize, FBlockSize); end; //process the possible remaining bytes, ie less than a full block if LRemaining > 0 then begin CopyTIdBytes(LSrc, LSize - LRemaining, LBlock, 0, LRemaining); LBlock[LMaxDataSize] := LRemaining; Encrypt(LBlock); CopyTIdBytes(LBlock, 0, VBuffer, Length(VBuffer) - FBlockSize, FBlockSize); end; end; // let the next Intercept in the chain encode its data next // RLebeau: DO NOT call inherited! It will trigger the OnSend event // again with the entire altered buffer as input, which can cause user // code to re-encrypt the already-encrypted data. We do not want that // here! Just call the next Intercept directly... //inherited Send(VBuffer); if Intercept <> nil then begin Intercept.Send(VBuffer); end; end; procedure TIdBlockCipherIntercept.Receive(var VBuffer: TIdBytes); var LBlock : TIdBytes; LSize, LCount, LPos, LMaxDataSize, LCompleteBlocks: Integer; LRemaining: Integer; begin // let the next Intercept in the chain decode its data first // RLebeau: DO NOT call inherited! It will trigger the OnReceive event // with the entire decoded buffer as input, which can cause user // code to decrypt data prematurely/incorrectly. We do not want that // here! Just call the next Intercept directly... //inherited Receive(VBuffer); if Intercept <> nil then begin Intercept.Receive(VBuffer); end; LPos := 0; AppendBytes(FIncoming, VBuffer); LSize := Length(FIncoming); if LSize >= FBlockSize then begin // the length of ABuffer when we have finished is currently unknown, but must be less than // the length of FIncoming. We will reserve this much, then reallocate at the end SetLength(VBuffer, LSize); SetLength(LBlock, FBlockSize); LMaxDataSize := FBlockSize - 1; LCompleteBlocks := LSize div FBlockSize; LRemaining := LSize mod FBlockSize; for LCount := 0 to LCompleteBlocks-1 do begin CopyTIdBytes(FIncoming, LCount * FBlockSize, LBlock, 0, FBlockSize); Decrypt(LBlock); if (LBlock[LMaxDataSize] = 0) or (LBlock[LMaxDataSize] >= FBlockSize) then begin raise EIdBlockCipherInterceptException.CreateFmt(RSBlockIncorrectLength, [LBlock[LMaxDataSize]]); end; CopyTIdBytes(LBlock, 0, VBuffer, LPos, LBlock[LMaxDataSize]); Inc(LPos, LBlock[LMaxDataSize]); end; if LRemaining > 0 then begin CopyTIdBytes(FIncoming, LSize - LRemaining, FIncoming, 0, LRemaining); end; SetLength(FIncoming, LRemaining); end; SetLength(VBuffer, LPos); end; procedure TIdBlockCipherIntercept.CopySettingsFrom(ASrcBlockCipherIntercept: TIdBlockCipherIntercept); Begin FBlockSize := ASrcBlockCipherIntercept.FBlockSize; {$IFDEF USE_OBJECT_ARC} FDataObject := ASrcBlockCipherIntercept.FDataObject; FDataValue := ASrcBlockCipherIntercept.FDataValue; {$ELSE} FData := ASrcBlockCipherIntercept.FData; // not sure that this is actually safe {$ENDIF} FOnConnect := ASrcBlockCipherIntercept.FOnConnect; FOnDisconnect:= ASrcBlockCipherIntercept.FOnDisconnect; FOnReceive := ASrcBlockCipherIntercept.FOnReceive; FOnSend := ASrcBlockCipherIntercept.FOnSend; end; procedure TIdBlockCipherIntercept.SetBlockSize(const Value: Integer); Begin if (Value > 0) and (Value <= IdBlockCipherBlockSizeMax) then begin FBlockSize := Value; end; end; procedure TIdBlockCipherIntercept.InitComponent; begin inherited InitComponent; FBlockSize := IdBlockCipherBlockSizeDefault; SetLength(FIncoming, 0); end; { TIdServerBlockCipherIntercept } procedure TIdServerBlockCipherIntercept.InitComponent; begin inherited InitComponent; FBlockSize := IdBlockCipherBlockSizeDefault; end; procedure TIdServerBlockCipherIntercept.Init; begin end; function TIdServerBlockCipherIntercept.Accept(AConnection: TComponent): TIdConnectionIntercept; begin Result := TIdBlockCipherIntercept.Create(nil); TIdBlockCipherIntercept(Result).BlockSize := BlockSize; end; end.