Componentes.Terceros.RemObj.../internal/5.0.23.613/1/RemObjects SDK for Delphi/Source/RODEC/uRORFC2289.pas
david 2824855ea7 - Modificación del paquete RemObjects_Core_D10 para que sea un paquete de runtime/designtime (antes era designtime sólo)
- Recompilación en Delphi10 de todos los paquetes de RO para generar las DCU's en Lib\D10
- Recompilación en Delphi10 de todos los paquetes de DA para generar las DCU's en Lib\D10

git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.RemObjects@9 b6239004-a887-0f4b-9937-50029ccdca16
2007-09-10 14:06:19 +00:00

623 lines
20 KiB
ObjectPascal

{Copyright: Hagen Reddmann mailto:HaReddmann@AOL.COM
Author: Hagen Reddmann
Remarks: freeware, but this Copyright must be included
known Problems: none
Version: 3.0, Delphi Encryption Compendium
Delphi 2-4, BCB 3-4, designed and testet under D3 and D4
Description: RFC1938 Standard One Time Password Routines "otp-"
RO_RFC2289 Standard One Time Password Routines "otp-"
RFC2444 Standard One Time Password Routines "otp-" "ext"
RFC1760 S/Key One Time Password Routines "s/key"
RFC1760 Six Word String Converting
Remarks: The RFC1760 Six Word Converting in these Library is an
extended (modified) Version. Normaly works the Standard
RFC1760 ONLY with 64 Bit (8 Bytes) Inputs, Six Words Output (66 Bit)
and a Dictionary with 2048 Entries.
The here implemented Version is absolutly compatible with
Standard RFC1760 but works with any Sizes and other
userdefined Dictionary's with variable Entrycount.
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
unit uRORFC2289;
interface
uses Windows, SysUtils, Classes, uROHash, uRODECUtil;
{$I uROVer.inc}
const
fmtRFC1760 = 1760; // New StringFormat
type
EROOTPException = class(Exception);
TROOneTimePassword = class(TComponent)
private
FSeed: String;
FIdent: String;
FHash: TROHashClass;
FCount: Cardinal;
FExtended: Boolean;
FFormat: Integer;
FLastOTP: String;
function GetChallenge: String;
procedure SetChallenge(Value: String);
function GetAlgorithm: String;
procedure SetAlgorithm(Value: String);
procedure SetIdent(Value: String);
procedure SetHash(Value: TROHashClass);
procedure SetSeed(Value: String);
protected
function Calc(const Value: String; ACount: Integer): String;
public
constructor Create(AOwner: TComponent); override;
function Execute(const Password: String): String;
function Check(const OTP: String): Boolean;
procedure Next(const OTP: String);
function NextPhrase(const OTP: String; ACount: Integer): String;
function FirstPhrase(const Password: String): String;
property LastOTP: String read FLastOTP write FLastOTP;
property RO_Hash: TROHashClass read FHash write SetHash stored False;
published
property Algorithm: String read GetAlgorithm write SetAlgorithm stored False;
property Ident: String read FIdent write SetIdent stored False;
property Count: Cardinal read FCount write FCount stored False;
property Seed: String read FSeed write SetSeed stored False;
property Extended: Boolean read FExtended write FExtended stored False;
property Format: Integer read FFormat write FFormat default fmtRFC1760;
property Challenge: String read GetChallenge write SetChallenge;
end;
PDictionary = ^TDictionary;
TDictionary = packed record
EntryCRC: LongWord; // computed CRC
EntryCount: Integer; // count of Words in Entries
EntrySize: Integer; // size of one Word in Entries
Entries: array[0..0] of Char; // the Dictionary
end;
// TStringFormat_RFC1760Class = class of TROStringFormat_RFC1760;
TROStringFormat_RFC1760 = class(TROStringFormat)
private
class function GetDict: PDictionary;
protected
class function Dictionary: PDictionary; virtual;
public
class function ToStr(Value: PChar; Len: Integer): String; override;
class function StrTo(Value: PChar; Len: Integer): String; override;
class function Format: Integer; override;
class function IsValid(Value: PChar; Len: Integer; ToStr: Boolean): Boolean; override;
end;
// convert any OTP- HEX or WORD Format to ToFormat
function OTPFormat(const Value: String; ToFormat: Integer): String;
implementation
uses uRODECConst;
{$R uRORFC1760.RES}
const
SepChars = [#0,#9,#10,#13,#32,',',';',':','$','(',')','[',']'] +
['{','}','-','"','''','\','/','+','*'];
RFC1760Dict : PDictionary = nil; // Standard RFC1760 Dictionary Resource
function OTPFormat(const Value: String; ToFormat: Integer): String;
var
I: Integer;
begin
Result := Value;
if Result = '' then Exit;
I := Pos(sOTPWord, AnsiLowerCase(Result));
if I > 0 then
begin
Delete(Result, 1, I + Length(sOTPWord) -1);
I := fmtRFC1760;
end else
begin
I := Pos(sOTPHex, AnsiLowerCase(Result));
if I > 0 then
begin
Delete(Result, 1, I + Length(sOTPHex) -1);
I := fmtHEX;
end else I := fmtRFC1760;
end;
try
Result := FormatToStr(PChar(Result), -1, I);
except
if I <> fmtRFC1760 then raise
else Result := FormatToStr(PChar(Result), -1, fmtHEX)
end;
Result := StrToFormat(PChar(Result), Length(Result), ToFormat);
end;
function TROOneTimePassword.GetChallenge: String;
begin
if FHash = nil then FHash := TROHash_MD4;
if Trim(FSeed) = '' then
begin
SetLength(FSeed, 2);
RndXORBuffer(RndTimeSeed, PChar(FSeed)^, 2);
FSeed := StrToFormat(PChar(FSeed), 2, fmtHEX);
end;
Result := FIdent;
if AnsiCompareText(FIdent, sSKeyIdent) <> 0 then
Result := Result + GetShortClassName(FHash);
Result := Result + ' ' + IntToStr(FCount) + ' ' + FSeed;
if FExtended then Result := Result + ' ' + sOTPExt;
end;
procedure TROOneTimePassword.SetChallenge(Value: String);
type
TCharSet = set of Char;
function CheckAlpha(const Value: String; Chars: TCharSet): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to Length(Value) do
if not (Value[I] in Chars) then Exit;
Result := True;
end;
var
I: Integer;
P,T,L: PChar;
S: String;
H: TROHashClass;
C: Integer;
E: Boolean;
begin
if Trim(Value) = '' then
begin
FHash := TROHash_MD4;
FIdent := sOTPIdent;
FExtended := False;
FSeed := '';
FCount := 0;
Exit;
end;
H := nil;
C := -1;
E := False;
try
P := PChar(Value);
L := StrEnd(P);
I := Pos(sOTPIdent, AnsiLowerCase(Value)); // check of "otp-"
if I > 0 then // it's a RO_RFC2289 Challenge
begin
Inc(P, I + Length(sOTPIdent) - 1);
T := P;
if T >= L then Exit;
while not (T^ in SepChars) do Inc(T); // find End
if T >= L then Exit;
S := P;
SetLength(S, T - P); // Algo Ident currect used "md4", "md5", "sha1"
H := GetHashClass(S);
end else
begin
I := Pos(sSKeyIdent, AnsiLowerCase(Value)); // check of "s/key"
if I = 0 then Exit; // isn't a RFC1760 Challenge
Inc(P, I + Length(sSKeyIdent) - 1);
T := P;
if T >= L then Exit;
H := TROHash_MD4;
end;
while T^ in SepChars do Inc(T); // find next Begin
if T >= L then Exit;
P := T;
while not (T^ in SepChars) do Inc(T); // find End
if T >= L then Exit;
S := P;
SetLength(S, T - P); // extract Count Value
C := StrToIntDef(S, -1); // convert to Integer
while T^ in SepChars do Inc(T); // find next Begin
if T > L then Exit;
P := T;
while not (T^ in SepChars) do Inc(T); // find End
if T > L then Exit;
S := P;
SetLength(S, T - P); // extract Seed Value
while T^ in SepChars do Inc(T); // find next Begin
if T > L then Exit;
E := StrLIComp(T, PChar(sOTPExt), Length(sOTPExt)) = 0;
finally
if (H = nil) or (C < 0) then
raise EROOTPException.Create(sInvalidChallenge);
if (S = '') or not CheckAlpha(S, ['a'..'z', 'A'..'Z', '0'..'9']) then
raise EROOTPException.Create(sInvalidSeed);
FHash := H;
FSeed := S;
FCount := C;
FExtended := E;
end;
end;
function TROOneTimePassword.GetAlgorithm: String;
begin
Result := GetHashName(FHash);
end;
procedure TROOneTimePassword.SetAlgorithm(Value: String);
begin
SetHash(GetHashClass(Value));
end;
procedure TROOneTimePassword.SetIdent(Value: String);
begin
FIdent := Value;
if AnsiCompareText(FIdent, sSKeyIdent) = 0 then FHash := TROHash_MD4;
end;
procedure TROOneTimePassword.SetHash(Value: TROHashClass);
begin
FHash := Value;
if (FHash = nil) or
(FHash.DigestKeySize < 16) or
FHash.InheritsFrom(TROChecksum) then FHash := TROHash_MD4 else
if (AnsiCompareText(FIdent, sSKeyIdent) = 0) and (FHash <> TROHash_MD4) then
FIdent := sOTPIdent;
end;
procedure TROOneTimePassword.SetSeed(Value: String);
var
I: Integer;
begin
FSeed := Value;
for I := Length(FSeed) downto 1 do
if not (FSeed[I] in ['a'..'z', 'A'..'Z', '0'..'9']) then Delete(FSeed, I, 1);
end;
constructor TROOneTimePassword.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetChallenge('');
FFormat := fmtRFC1760;
end;
function TROOneTimePassword.Calc(const Value: String; ACount: Integer): String;
var
I: Integer;
begin
if (FHash = nil) or (ACount < 0) then
raise EROOTPException.Create(sInvalidCalc);
Result := Value;
repeat
Result := FHash.CalcBuffer(PChar(Result)^, Length(Result), nil, fmtCOPY);
// Fold the Digest to 8 Bytes
I := 8;
repeat
XORBuffers(PChar(Result), @PChar(Result)[I], 8, PChar(Result));
Inc(I, 8);
until I >= FHash.DigestKeySize;
// convert Endianes
if FHash.InheritsFrom(TROHash_SHA) then
SwapIntegerBuffer(PChar(Result), PChar(Result), 2);
// truncate the Result
SetLength(Result, 8);
Dec(ACount);
until ACount < 0;
end;
function TROOneTimePassword.Execute(const Password: String): String;
begin
Result := Calc(AnsiLowerCase(FSeed) + Password, Count);
Result := StrToFormat(PChar(Result), 8, Format);
if FExtended then
with StringFormat(Format) do
if InheritsFrom(TROStringFormat_RFC1760) then Result := sOTPWord + Result else
if InheritsFrom(TROStringFormat_HEX) then Result := sOTPHex + Result;
end;
procedure TROOneTimePassword.Next(const OTP: String);
begin
FLastOTP := OTP;
Dec(FCount);
end;
function TROOneTimePassword.Check(const OTP: String): Boolean;
begin
Result := Calc(OTPFormat(OTP, fmtCOPY), 0) = OTPFormat(FLastOTP, fmtCOPY);
end;
function TROOneTimePassword.FirstPhrase(const Password: String): String;
begin
Result := Calc(AnsiLowerCase(FSeed) + Password, 0);
Result := StrToFormat(PChar(Result), 8, Format);
end;
function TROOneTimePassword.NextPhrase(const OTP: String; ACount: Integer): String;
begin
Result := Calc(OTPFormat(OTP, fmtCOPY), ACount);
Result := StrToFormat(PChar(Result), 8, Format);
end;
// RFC1760 Six Word String Format
class function TROStringFormat_RFC1760.GetDict: PDictionary;
begin
Result := Dictionary;
if (Result = nil) or
(Result.EntryCount <= 0) or
(Result.EntrySize <= 0) then
raise EROStringFormat.CreateFMT(sInvalidDictionary, [GetShortClassName(Self)]);
end;
class function TROStringFormat_RFC1760.Dictionary: PDictionary;
{ Standard RFC1760 Dictionary Format
TDictionary = packed record
EntryCount = 2048;
EntrySize = 4;
EntryCRC = $94CE8163; a standard CRC32 Checksum
Entries = array[0..2047] of array[0..3] of Char; see RFC1760.INC
end;
}
begin
if RFC1760Dict = nil then // Load Dictionary from resource
begin
RFC1760Dict := LockResource(LoadResource(HInstance, FindResource(HInstance,
PChar(GetShortClassName(Self)), RT_RCDATA)));
if RFC1760Dict <> nil then
with RFC1760Dict^ do
if EntryCRC <> not CRC32($FFFFFFFF, @Entries, EntryCount * EntrySize) then
begin // Resource is modified, any Hacker present ?! :-)
FreeResource(Integer(RFC1760Dict));
RFC1760Dict := nil;
end;
end;
Result := RFC1760Dict;
end;
function ExtractBits(Value: PChar; BitOffset, Bits, MaxLen: Integer): Integer;
var // Extract from BitOffset count Bits
I: Integer;
begin
Result := 0;
for I := BitOffset div 8 -1 to BitOffset div 8 + 2 do
begin
Result := Result shl 8;
if (I >= 0) and (I * 8 <= MaxLen) then
Result := Result or PByteArray(Value)[I];
end;
Result := (Result shr (24 - Bits - BitOffset mod 8)) and (1 shl Bits -1);
if BitOffset + Bits > MaxLen then
Result := Result and not (1 shl (BitOffset + Bits - MaxLen) - 1);
end;
class function TROStringFormat_RFC1760.ToStr(Value: PChar; Len: Integer): String;
function FindEntry(Dictionay: PDictionary; Entry: PChar): Integer; register;
asm
PUSH EDI
PUSH ESI
PUSH EBX
PUSH EBP
MOV ESI,EDX
MOV DL,[ESI]
MOV EBX,[EAX].TDictionary.EntrySize
MOV EBP,EBX
MOV ECX,[EAX].TDictionary.EntryCount
LEA EDI,[EAX].TDictionary.Entries
XOR EAX,EAX
JMP @@2
@@1: ADD EDI,EBP
@@2: CMP DL,[EDI]
JZ @@4
@@3: INC EAX
DEC ECX
JNZ @@1
MOV EAX,-1
JMP @@6
@@4: MOV EBX,EBP
@@5: DEC EBX
JZ @@6
MOV DH,[ESI + EBX]
CMP DH,[EDI + EBX]
JNZ @@3
JMP @@5
@@6: POP EBP
POP EBX
POP ESI
POP EDI
end;
var
Last,T,R: PChar;
Entry: String;
Dict: PDictionary;
Index: Integer;
Bits: Integer;
Offs: Integer;
Parity: Integer;
begin
Result := '';
Dict := Dictionary;
if Dict = nil then Exit;
Bits := MSBit(Dict.EntryCount);
Last := Value + Len;
SetLength(Entry, Dict.EntrySize);
SetLength(Result, Len * Dict.EntrySize); // allocate enough Space
FillChar(PChar(Result)^, Len * Dict.EntrySize, 0);
R := PChar(Result);
Offs := 0;
repeat
// Extract one Word
while Value^ in SepChars do Inc(Value);
if Value >= Last then
raise EROStringFormat.CreateFmt(sInvalidStringFormat, [GetShortClassName(Self)]);
T := Value;
while not (Value^ in SepChars) do Inc(Value);
if Value - T > Dict.EntrySize then
raise EROStringFormat.CreateFmt(sInvalidStringFormat, [GetShortClassName(Self)]);
// Move and Uppercase Word to Entrybuffer
FillChar(PChar(Entry)^, Dict.EntrySize, 0);
Move(T^, PChar(Entry)^, Value - T);
CharUpperBuff(PChar(Entry), Dict.EntrySize);
// Lookup Entry in Dictionary
Index := FindEntry(Dict, PChar(Entry));
if Index < 0 then
raise EROStringFormat.CreateFmt(sInvalidStringFormat, [GetShortClassName(Self)]);
// put the Result
asm
MOV EAX,R // R
MOV EDX,Offs // Offs
SHR EDX,3 // Offs div 8
ADD EAX,EDX // @R[Offs div 8]
MOV ECX,32 // 32
SUB ECX,Bits // 32 - Bits
MOV EDX,Offs // Offs
AND EDX,07h // Offs mod 8
SUB ECX,EDX // 32 - Bits - Offs mod 8
MOV EDX,Index // Index
SHL EDX,CL // Index shl (32 - Bits - Offs mod 8)
OR [EAX + 3],DL
OR [EAX + 2],DH
SHR EDX,16
OR [EAX + 1],DL
OR [EAX + 0],DH
end;
Inc(Offs, Bits);
until Value >= Last;
Entry := '';
// calculate Parity
Index := (Offs div 8) and not 1;
Bits := Offs - Index * 8;
if Bits = 0 then Bits := MSBit(Dict.EntryCount);
Dec(Offs, Bits);
Len := ExtractBits(R, Offs, Bits, Offs + Bits);
Index := 0;
Parity := 0;
while Index < Offs do
begin
Inc(Parity, ExtractBits(R, Index, Bits, Offs));
Inc(Index, Bits);
end;
SetLength(Result, Offs div 8);
Index := Length(Result);
if (Result[Index] = #0) and (Index <> 8) then
SetLength(Result, Index-1);
Parity := Parity and (1 shl Bits -1);
if Len <> Parity then
raise EROStringFormat.CreateFmt(sInvalidStringFormat, [GetShortClassName(Self)]);
end;
class function TROStringFormat_RFC1760.StrTo(Value: PChar; Len: Integer): String;
var
Dict: PDictionary;
Bits: Integer;
BitLen: Integer;
Parity: Integer;
Offset: Integer;
Entry: Integer;
Text: String;
Temp: String;
begin
Result := '';
Dict := GetDict;
SetLength(Text, Dict.EntrySize);
SetLength(Temp, Len +2);
FillChar(PChar(Temp)^, Len +2, 0);
Move(Value^, PChar(Temp)^, Len);
Value := PChar(Temp);
// calculate the Parity (Checksum )
BitLen := ((Len +1) and not 1) * 8; // Input Length in Bits
Offset := 0;
Parity := 0;
Bits := MSBit(Dict.EntryCount); // Standard = 2048 -> 11 Bits
Bits := Bits - BitLen mod Bits; // Standard = 8 Byte Input -> 64 Bits -> 2 Bits Parity -> effective 6 * 11 Bits Output
while Offset < BitLen do
begin
Inc(Parity, ExtractBits(Value, Offset, Bits, BitLen));
Inc(Offset, Bits);
end;
// calculate the Result
Offset := 0;
Parity := Parity and (1 shl Bits -1); // Standard = 2 Bits used
Bits := MSBit(Dict.EntryCount); // Standard = 11 Bits
while Offset <= BitLen do
begin
Entry := ExtractBits(Value, Offset, Bits, BitLen);
Inc(Offset, Bits);
if Offset > BitLen then Entry := Entry or Parity;
Move(Dict.Entries[Entry * Dict.EntrySize], PChar(Text)^, Dict.EntrySize);
Result := Result + PChar(Text) + ' ';
end;
SetLength(Result, Length(Result) -1);
end;
class function TROStringFormat_RFC1760.Format: Integer;
begin
Result := fmtRFC1760;
end;
class function TROStringFormat_RFC1760.IsValid(Value: PChar; Len: Integer; ToStr: Boolean): Boolean;
begin
Result := False;
try
if Dictionary = nil then Exit;
if ToStr then Self.ToStr(Value, Len);
Result := True;
except
end;
end;
{
procedure SaveDictionary(Format: TStringFormat_RFC1760Class);
//save a Dictionary as Resource File
var
Dict: PDictionary;
HeaderSize: Integer;
Origin, ImageSize: Longint;
Header: array[0..79] of Char;
begin
Dict := Format.GetDict;
with TFileStream.Create(GetShortClassName(Format) + '.RES', fmCreate) do
try
Byte((@Header[0])^) := $FF;
Word((@Header[1])^) := 10;
HeaderSize := StrLen(StrUpper(StrPLCopy(@Header[3], GetShortClassName(Format), 63))) + 10;
Word((@Header[HeaderSize - 6])^) := $1030;
Longint((@Header[HeaderSize - 4])^) := 0;
WriteBuffer(Header, HeaderSize);
Origin := Position;
Dict.EntryCRC := not CRC32(-1, @Dict.Entries, Dict.EntryCount * Dict.EntrySize);
WriteBuffer(Dict^, SizeOf(TDictionary) + Dict.EntryCount * Dict.EntrySize);
ImageSize := Position - Origin;
Position := Origin - 4;
WriteBuffer(ImageSize, SizeOf(Longint));
finally
Free;
end;
end;
}
initialization
// SaveDictionary(TROStringFormat_RFC1760);
RegisterStringFormats([TROStringFormat_RFC1760]);
finalization
if RFC1760Dict <> nil then FreeResource(Integer(RFC1760Dict));
end.