Compare commits

..

No commits in common. "master" and "v0.1.0" have entirely different histories.

3 changed files with 172 additions and 220 deletions

View File

@ -11,16 +11,13 @@
*} *}
unit BCrypt; unit BCrypt;
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
{$CODEPAGE UTF-8} {$codepage utf8}
interface interface
uses uses
sysutils, SysUtils,
classes Classes;
;
const const
// bcrypt uses 128-bit (16-byte) salt // bcrypt uses 128-bit (16-byte) salt
@ -222,7 +219,6 @@ type
BCryptSalt, BCryptSalt,
BCryptHash : AnsiString; BCryptHash : AnsiString;
end; end;
UTF8String = type AnsiString(CP_UTF8);
EHash = class(EArgumentException); EHash = class(EArgumentException);
@ -232,7 +228,7 @@ private
FPBox: array[0..17] of DWord; FPBox: array[0..17] of DWord;
function BsdBase64Encode(const RawByteData: TBytes; CharacterLength: Sizeint): AnsiString; function BsdBase64Encode(const RawByteData: TBytes; CharacterLength: Sizeint): AnsiString;
function BsdBase64Decode(const EncodedString : AnsiString): TBytes; function BsdBase64Decode(const EncodedString : AnsiString): TBytes;
function Crypt(const Password : UTF8String; const Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString; function Crypt(const Password, Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString;
function CryptRaw(const HashKey, Salt: TBytes; Cost : Byte): TBytes; function CryptRaw(const HashKey, Salt: TBytes; Cost : Byte): TBytes;
procedure EKSKey(const Salt, HashKey: TBytes); procedure EKSKey(const Salt, HashKey: TBytes);
procedure Encipher(var lr: array of DWord; const offset: SizeInt); procedure Encipher(var lr: array of DWord; const offset: SizeInt);
@ -251,10 +247,10 @@ private
public public
constructor Create; overload; constructor Create; overload;
destructor Destroy; override; destructor Destroy; override;
function CreateHash(const Password : UTF8String) : AnsiString; overload; function CreateHash(const Password : AnsiString) : AnsiString; overload;
function CreateHash(const Password : UTF8String; HashType : THashTypes) : AnsiString; overload; function CreateHash(const Password : AnsiString; HashType : THashTypes) : AnsiString; overload;
function CreateHash(const Password : UTF8String; HashType : THashTypes; Cost : Byte) : AnsiString; overload; function CreateHash(const Password : AnsiString; HashType : THashTypes; Cost : Byte) : AnsiString; overload;
function VerifyHash(const Password : UTF8STring; const Hash : AnsiString) : Boolean; function VerifyHash(const Password, Hash : AnsiString) : Boolean;
function NeedsRehash(const BCryptHash : AnsiString) : Boolean; overload; function NeedsRehash(const BCryptHash : AnsiString) : Boolean; overload;
function NeedsRehash(const BCryptHash : AnsiString; Cost : Byte) : Boolean; overload; function NeedsRehash(const BCryptHash : AnsiString; Cost : Byte) : Boolean; overload;
function HashGetInfo(const Hash : AnsiString) : RTPasswordInformation; function HashGetInfo(const Hash : AnsiString) : RTPasswordInformation;
@ -267,7 +263,6 @@ Uses
constructor TBCryptHash.Create; constructor TBCryptHash.Create;
begin begin
inherited Create; inherited Create;
end; end;
@ -486,6 +481,7 @@ begin
end; end;
end; { TBCryptHash.EKSKey } end; { TBCryptHash.EKSKey }
{$OVERFLOWCHECKS OFF}
procedure TBCryptHash.Encipher(var lr: array of DWord; const offset: SizeInt); procedure TBCryptHash.Encipher(var lr: array of DWord; const offset: SizeInt);
var var
i, n, block, r: DWord; i, n, block, r: DWord;
@ -497,22 +493,23 @@ begin
while i <= BLOWFISH_NUM_ROUNDS - 1 do while i <= BLOWFISH_NUM_ROUNDS - 1 do
begin begin
n := FSBox[(block shr 24) and $FF]; n := FSBox[(block shr 24) and $FF];
n := DWord(n + FSBox[$100 or ((block shr 16) and $FF)]); n := n + FSBox[$100 or ((block shr 16) and $FF)];
n := n xor FSBox[$200 or ((block shr 8) and $FF)]; n := n xor FSBox[$200 or ((block shr 8) and $FF)];
n := DWord(n + FSBox[$300 or (block and $FF)]); n := n + FSBox[$300 or (block and $FF)];
r := r xor (n xor FPBox[i]); r := r xor (n xor FPBox[i]);
Inc(i); Inc(i);
n := FSBox[(r shr 24) and $FF]; n := FSBox[(r shr 24) and $FF];
n := DWord(n + FSBox[$100 or ((r shr 16) and $FF)]); n := n + FSBox[$100 or ((r shr 16) and $FF)];
n := n xor FSBox[$200 or ((r shr 8) and $FF)]; n := n xor FSBox[$200 or ((r shr 8) and $FF)];
n := DWord(n + FSBox[$300 or (r and $FF)]); n := n + FSBox[$300 or (r and $FF)];
block := block xor (n xor FPBox[i]); block := block xor (n xor FPBox[i]);
Inc(i); Inc(i);
end; end;
lr[offset] := r xor FPBox[BLOWFISH_NUM_ROUNDS + 1]; lr[offset] := r xor FPBox[BLOWFISH_NUM_ROUNDS + 1];
lr[offset + 1] := block; lr[offset + 1] := block;
end; end;
{$OVERFLOWCHECKS ON}
function TBCryptHash.FormatPasswordHash(const Salt, Hash: TBytes; Cost : Byte; HashType : THashTypes): AnsiString; function TBCryptHash.FormatPasswordHash(const Salt, Hash: TBytes; Cost : Byte; HashType : THashTypes): AnsiString;
var var
@ -690,16 +687,16 @@ begin
Result := RandomFileBuffer; Result := RandomFileBuffer;
end; { TBCryptHash.unixRandomBytes } end; { TBCryptHash.unixRandomBytes }
function TBCryptHash.CreateHash(const Password : UTF8String) : AnsiString; overload; function TBCryptHash.CreateHash(const Password : AnsiString) : AnsiString; overload;
begin begin
Result := CreateHash(Password, bcPHP, BCRYPT_DEFAULT_COST); Result := CreateHash(Password, bcPHP, BCRYPT_DEFAULT_COST);
end; end;
function TBCryptHash.CreateHash(const Password : UTF8String; HashType : THashTypes) : AnsiString; overload; function TBCryptHash.CreateHash(const Password : AnsiString; HashType : THashTypes) : AnsiString; overload;
begin begin
Result := CreateHash(Password, HashType, BCRYPT_DEFAULT_COST); Result := CreateHash(Password, HashType, BCRYPT_DEFAULT_COST);
end; { TBCryptHash.CreateHash } end; { TBCryptHash.CreateHash }
function TBCryptHash.CreateHash(const Password : UTF8String; HashType : THashTypes; Cost : Byte) : AnsiString; overload; function TBCryptHash.CreateHash(const Password : AnsiString; HashType : THashTypes; Cost : Byte) : AnsiString; overload;
var var
PasswordKey, PasswordKey,
SaltBytes, SaltBytes,
@ -717,7 +714,7 @@ begin
Result := FormatPasswordHash(SaltBytes, Hash, Cost, HashType); Result := FormatPasswordHash(SaltBytes, Hash, Cost, HashType);
end; { TBCryptHash.CreateHash } end; { TBCryptHash.CreateHash }
function TBCryptHash.Crypt(const Password : UTF8String; const Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString; function TBCryptHash.Crypt(const Password, Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString;
var var
PasswordKey, PasswordKey,
SaltBytes, SaltBytes,
@ -743,12 +740,12 @@ begin
end; end;
else else
begin begin
Result := (bcUnknown); Result := (bcPHP);
end; end;
end; end;
end; end;
function TBCryptHash.VerifyHash(const Password : UTF8String; const Hash : AnsiString) : Boolean; function TBCryptHash.VerifyHash(const Password, Hash : AnsiString) : Boolean;
var var
WorkingBcryptHash, Salt : AnsiString; WorkingBcryptHash, Salt : AnsiString;
HashCounter, ResultStatus, BCryptCost : Byte; HashCounter, ResultStatus, BCryptCost : Byte;

View File

@ -2,7 +2,6 @@ Program BCryptHashTest;
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
{$ASSERTIONS ON} {$ASSERTIONS ON}
{$UNITPATH ../} {$UNITPATH ../}
{$CODEPAGE UTF-8}
uses BCrypt, Classes, SysUtils, Crt; uses BCrypt, Classes, SysUtils, Crt;
const const
@ -22,10 +21,6 @@ var
PassedAssertions : Word; PassedAssertions : Word;
Passed : Boolean; Passed : Boolean;
UTF8TestString : UTF8String = 'Τη γλώσσα μου έδωσαν ελληνική';
UTF8TestHash : AnsiString = '$2y$12$RSxqgCt5T4qPXLM3AzKMCueMBZo6cc9o/bN4wqcX6KA6lZnOkqzTG';
UTF8PHPHash : AnsiString = '$2y$12$KrBUSn54WO5C/aw2H3imKurgsnrGq7PsrIZYXusaTNIO.27IGsmkG';
PasswordHashes : array [1..14] of AnsiString = ( PasswordHashes : array [1..14] of AnsiString = (
'$2y$10$LCb3aOt8lAXSzNrEpQKDQO1zc2wCCQltrDwSEbb9JaUo4OKbphC3i', '$2y$10$LCb3aOt8lAXSzNrEpQKDQO1zc2wCCQltrDwSEbb9JaUo4OKbphC3i',
'$2y$11$H7TRTJZqQTzN5RCiwMOne.yjVxyKCd4GyLrBQzV91gK0T4XQeKTNa', '$2y$11$H7TRTJZqQTzN5RCiwMOne.yjVxyKCd4GyLrBQzV91gK0T4XQeKTNa',
@ -109,40 +104,8 @@ for i := 1 to 7 do
WriteLn(' - Pass'); WriteLn(' - Pass');
Inc(PassedAssertions); Inc(PassedAssertions);
end; end;
WriteLn(#10#13'Testing UTF8 with ', UTF8TestString, ' ... '#10#13); WriteLn(#10#13'Testing Failures ...'#10#13);
Write('Testing : ', UTF8TestHash); for i := 1 to 7 do
try
Assert(TBCrypt.VerifyHash(UTF8TestString, UTF8TestHash) = True, 'Should Be True');
Inc(Assertions);
Inc(PassedAssertions);
Writeln(' - Pass');
except
on e: EAssertionFailed do
begin
WriteLn(' - Fail');
Inc(FailedAssertions);
Dec(PassedAssertions);
end;
end;
WriteLn(#10#13'Testing UTF8 PHP Hash with ', UTF8TestString, ' ... '#10#13);
Write('Testing : ', UTF8PHPHash);
try
Assert(TBCrypt.VerifyHash(UTF8TestString, UTF8PHPHash) = True, 'Should Be True');
Inc(Assertions);
Inc(PassedAssertions);
Writeln(' - Pass');
except
on e: EAssertionFailed do
begin
WriteLn(' - Fail');
Inc(FailedAssertions);
Dec(PassedAssertions);
end;
end;
WriteLn(#10#13'Testing Failures ...'#10#13);
for i := 1 to 7 do
begin begin
Write('Testing : ', PasswordHashFailures[i]); Write('Testing : ', PasswordHashFailures[i]);
try try
@ -161,8 +124,8 @@ WriteLn(#10#13'Testing UTF8 with ', UTF8TestString, ' ... '#10#13);
end; end;
WriteLn(#10#13'Testing Rehash True ...'#10#13); WriteLn(#10#13'Testing Rehash True ...'#10#13);
for i := 1 to 7 do for i := 1 to 7 do
begin begin
Write('Testing : ', PasswordHashes[i]); Write('Testing : ', PasswordHashes[i]);
try try
@ -362,6 +325,7 @@ WriteLn(#10#13'Testing UTF8 with ', UTF8TestString, ' ... '#10#13);
Inc(FailedAssertions); Inc(FailedAssertions);
end; end;
Writeln(#10#13'Testing hashing ...'#10#13); Writeln(#10#13'Testing hashing ...'#10#13);
Writeln(TBCrypt.CreateHash(StaticPassword)); Writeln(TBCrypt.CreateHash(StaticPassword));
Writeln(TBCrypt.CreateHash(StaticPassword, bcBSD)); Writeln(TBCrypt.CreateHash(StaticPassword, bcBSD));
@ -372,9 +336,9 @@ WriteLn(#10#13'Testing UTF8 with ', UTF8TestString, ' ... '#10#13);
Writeln(TBCrypt.CreateHash(StaticPassword, bcPHP, 14)); Writeln(TBCrypt.CreateHash(StaticPassword, bcPHP, 14));
Writeln(#10#13); Writeln(#10#13);
TBCrypt.Free; TBCrypt.Free;
Writeln('Assertions : ', Assertions); Writeln('Assertions : ', Assertions);
Writeln('Passed Assertions : ', PassedAssertions); Writeln('Passed Assertions : ', PassedAssertions);
Writeln('Failed Assertions : ', FailedAssertions); Writeln('Failed Assertions : ', FailedAssertions);
Writeln; Writeln;
end. end.

View File

@ -14,8 +14,6 @@ assert_options(ASSERT_CALLBACK, 'bcrypt_assert_handler');
$bsdPascalHash = '$2a$12$9NWTTEbRtjLNd4KdW.VtUekFA6pJ3DF23FqdvwwvMtoMD9zqdaZg2'; $bsdPascalHash = '$2a$12$9NWTTEbRtjLNd4KdW.VtUekFA6pJ3DF23FqdvwwvMtoMD9zqdaZg2';
$bsdPascalHashFail = '$2a$12$9NWTTEbRtjLNd4KdW.VtUekFA6pJ3DF23FqdvwwvMtoMD9zqdaZg1'; $bsdPascalHashFail = '$2a$12$9NWTTEbRtjLNd4KdW.VtUekFA6pJ3DF23FqdvwwvMtoMD9zqdaZg1';
$utf8String = 'Τη γλώσσα μου έδωσαν ελληνική';
$utf8PascalHash = '$2y$12$RSxqgCt5T4qPXLM3AzKMCueMBZo6cc9o/bN4wqcX6KA6lZnOkqzTG';
$pascalHashesMT = [ $pascalHashesMT = [
'$2y$10$kJgRFQ993paFLArmPE3gn.8yuUB/SRpaEw7lkJJ1oVqhWVIecI5nO', '$2y$10$kJgRFQ993paFLArmPE3gn.8yuUB/SRpaEw7lkJJ1oVqhWVIecI5nO',
@ -37,13 +35,6 @@ $pascalHashesURandom = [
'$2y$16$Y0QNc8vaJJY5mQO0IkN6oeAxEVjtnHYqk0WeWLPm7bRjxA7fWHRBG', '$2y$16$Y0QNc8vaJJY5mQO0IkN6oeAxEVjtnHYqk0WeWLPm7bRjxA7fWHRBG',
]; ];
print PHP_EOL . 'Testing UTF8Hashe ... ' . str_repeat(PHP_EOL, 2);
print 'Testing : ' . $utf8PascalHash;
print PHP_EOL . ' with : ' . $utf8String;
if(true === assert(password_verify($utf8String, $utf8PascalHash), ' - Fail')) {
print ' - Pass' . PHP_EOL;
}
print PHP_EOL . 'Testing bsdPascalHash ... ' . str_repeat(PHP_EOL, 2); print PHP_EOL . 'Testing bsdPascalHash ... ' . str_repeat(PHP_EOL, 2);
print 'Testing : ' . $bsdPascalHash; print 'Testing : ' . $bsdPascalHash;
if (true === assert(password_verify('password', $bsdPascalHash), ' - Fail')) { if (true === assert(password_verify('password', $bsdPascalHash), ' - Fail')) {