Implement UTF8 support #7

Merged
sikofitt merged 1 commits from 5-handle-unicode-correctly-if-possible into master 2017-02-22 17:21:53 -08:00
3 changed files with 211 additions and 161 deletions

View File

@ -11,13 +11,16 @@
*} *}
unit BCrypt; unit BCrypt;
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
{$codepage utf8} {$CODEPAGE UTF-8}
interface interface
uses uses
SysUtils, sysutils,
Classes; classes
;
const const
// bcrypt uses 128-bit (16-byte) salt // bcrypt uses 128-bit (16-byte) salt
@ -219,6 +222,7 @@ type
BCryptSalt, BCryptSalt,
BCryptHash : AnsiString; BCryptHash : AnsiString;
end; end;
UTF8String = type AnsiString(CP_UTF8);
EHash = class(EArgumentException); EHash = class(EArgumentException);
@ -228,7 +232,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, Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString; function Crypt(const Password : UTF8String; const 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);
@ -247,10 +251,10 @@ private
public public
constructor Create; overload; constructor Create; overload;
destructor Destroy; override; destructor Destroy; override;
function CreateHash(const Password : AnsiString) : AnsiString; overload; function CreateHash(const Password : UTF8String) : AnsiString; overload;
function CreateHash(const Password : AnsiString; HashType : THashTypes) : AnsiString; overload; function CreateHash(const Password : UTF8String; HashType : THashTypes) : AnsiString; overload;
function CreateHash(const Password : AnsiString; HashType : THashTypes; Cost : Byte) : AnsiString; overload; function CreateHash(const Password : UTF8String; HashType : THashTypes; Cost : Byte) : AnsiString; overload;
function VerifyHash(const Password, Hash : AnsiString) : Boolean; function VerifyHash(const Password : UTF8STring; const 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;
@ -263,6 +267,7 @@ Uses
constructor TBCryptHash.Create; constructor TBCryptHash.Create;
begin begin
inherited Create; inherited Create;
end; end;
@ -687,16 +692,16 @@ begin
Result := RandomFileBuffer; Result := RandomFileBuffer;
end; { TBCryptHash.unixRandomBytes } end; { TBCryptHash.unixRandomBytes }
function TBCryptHash.CreateHash(const Password : AnsiString) : AnsiString; overload; function TBCryptHash.CreateHash(const Password : UTF8String) : 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 : AnsiString; HashType : THashTypes) : AnsiString; overload; function TBCryptHash.CreateHash(const Password : UTF8String; 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 : AnsiString; HashType : THashTypes; Cost : Byte) : AnsiString; overload; function TBCryptHash.CreateHash(const Password : UTF8String; HashType : THashTypes; Cost : Byte) : AnsiString; overload;
var var
PasswordKey, PasswordKey,
SaltBytes, SaltBytes,
@ -714,7 +719,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, Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString; function TBCryptHash.Crypt(const Password : UTF8String; const Salt : AnsiString; Cost : Byte; HashType : THashTypes) : AnsiString;
var var
PasswordKey, PasswordKey,
SaltBytes, SaltBytes,
@ -745,7 +750,7 @@ begin
end; end;
end; end;
function TBCryptHash.VerifyHash(const Password, Hash : AnsiString) : Boolean; function TBCryptHash.VerifyHash(const Password : UTF8String; const Hash : AnsiString) : Boolean;
var var
WorkingBcryptHash, Salt : AnsiString; WorkingBcryptHash, Salt : AnsiString;
HashCounter, ResultStatus, BCryptCost : Byte; HashCounter, ResultStatus, BCryptCost : Byte;

View File

@ -2,6 +2,7 @@ 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
@ -21,6 +22,10 @@ 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',
@ -104,8 +109,40 @@ for i := 1 to 7 do
WriteLn(' - Pass'); WriteLn(' - Pass');
Inc(PassedAssertions); Inc(PassedAssertions);
end; end;
WriteLn(#10#13'Testing Failures ...'#10#13); WriteLn(#10#13'Testing UTF8 with ', UTF8TestString, ' ... '#10#13);
for i := 1 to 7 do Write('Testing : ', UTF8TestHash);
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
@ -124,8 +161,8 @@ for i := 1 to 7 do
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
@ -325,7 +362,6 @@ for i := 1 to 7 do
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));
@ -336,9 +372,9 @@ for i := 1 to 7 do
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,6 +14,8 @@ 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',
@ -35,6 +37,13 @@ $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')) {