Implement UTF8 support #7
31
BCrypt.pas
31
BCrypt.pas
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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')) {
|
||||||
|
|
Loading…
Reference in New Issue