pascal_bcrypt/README.md

4.3 KiB

Free Pascal BCrypt

Free Pascal BCrypt implementation.

This started because I wanted something that would be compatible with PHP's 2y BCrypt hashing. Ultimately there is no difference between the 2a algorithm and the 2y algorithm. Just the a, and y. But I didn't want to have a wrapper function that replaced the a with the y.

If you try to verify a 2a password with PHP it will verify, but if you run the needs rehash function it will always say it needs a rehash. So I moved this to Free Pascal compatible class format.

Tested with :

  • Free Pascal

    • 2.6.4
      • Linux
        • Gentoo, 2.2-Current-x64
        • Raspbian
    • 3.0.0
      • Linux
        • Gentoo, 2.2-Current-x64
      • FreeBSD
        • 12.0-CURRENT-x64
      • Windows
        • Windows 10-x64
    • 3.1.1
      • Linux
        • Gentoo, 2.2-Current-x64
  • PHP

    • 5.5.x
      • 5.5.38-pl0-gentoo
    • 5.6.x
      • 5.6.20-pl0-gentoo
      • 5.6.28-pl0-gentoo
    • 7.0.x
      • 7.0.6_rc1-pl0-gentoo
      • 7.0.13-pl0-gentoo
    • 7.x.x (dev)
      • 7.2.0-dev-x64 (ZTS) 10/31/2016, Gentoo 2.2 Current
      • 7.2.0-dev-x64 (ZTS) 11/02/2016, FreeBSD 12.0-CURRENT
  • HHVM

    • Soon

Usage

BCrypt.CreateHash(Password);
BCrypt.CreateHash(Password, HashType);
BCrypt.CreateHash(Password, HashType, Cost);

Where

  • Password is the password to be hashed
  • HashType is one of bcPHP, bcBSD, or bcDefault, bcPHP is the default 2y
  • and Cost is a number between 10 and 30, default is 12.
var
  BCrypt : TBCryptHash;
  Hash   : AnsiString;
begin
  BCrypt := TBCryptHash.Create;
  Hash := BCrypt.CreateHash('password'); // PHP $2y$ hash with a cost of 12
  // or
  Hash := BCrypt.CreateHash('password', bcBSD); // BSD $2a$ hash with a cost of 12
  // or
  Hash := BCrypt.CreateHash('password', bcPHP, 14); // PHP hash, with a cost of 14
  Writeln(Hash);
  BCrypt.Free;
end;

To verify

var
  BCrypt : TBCryptHash;
  Hash : AnsiString;
  Verify : Boolean;
begin
  Hash := '$2y$12$GuC.Gk2YDsp8Yvga.IuSNOWM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq';
  BCrypt := TBCryptHash.Create;
  Verify := BCrypt.VerifyHash('password', Hash);
  BCrypt.Free;
end;

HashGetInfo - raises EHash exception if the hash is bad, (too short, too long);

var
  BCrypt : TBCryptHash;
  Hash : AnsiString;
  PasswordInfo : RTPasswordInformation;
begin
  BCrypt := TBCryptHash.Create;
  Hash := '$2y$12$GuC.Gk2YDsp8Yvga.IuSNOWM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq';
  PasswordInfo := BCrypt.HashGetInfo(Hash);
  with PasswordInfo do
    begin
      WriteLn('Algo : ', Algo); // bcPHP  
      WriteLn('AlgoName : ', AlgoName); // bcrypt
      WriteLn('Cost : ', Cost); // 12
      WriteLn('Salt : ', BCryptSalt); // GuC.Gk2YDsp8Yvga.IuSNO
      WriteLn('Hash : ', BCryptHash); // WM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq
    end;

  Hash := '$2a$12$GuC.Gk2YDsp8Yvga.IuSNOWM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq';
  PasswordInfo := BCrypt.HashGetInfo(Hash);
  with PasswordInfo do
    begin
      WriteLn('Algo : ', Algo); // bcBSD  
      WriteLn('AlgoName : ', AlgoName); // bcrypt
      WriteLn('Cost : ', Cost); // 12
      WriteLn('Salt : ', BCryptSalt); // GuC.Gk2YDsp8Yvga.IuSNO
      WriteLn('Hash : ', BCryptHash); // WM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq
    end;
    BCrypt.Free;
end;  

NeedsRehash

var
  BCrypt : TBCryptHash;
  Hash : AnsiString;
  Rehash : Boolean;
begin
  BCrypt := TBCryptHash.Create;
  Hash := '$2a$12$GuC.Gk2YDsp8Yvga.IuSNOWM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq';
  Rehash := BCrypt.NeedsRehash(Hash); // false
  Rehash := BCrypt.NeedsRehash(Hash, 14); // true
  Hash := '$2y$14$GuC.Gk2YDsp8Yvga.IuSNOWM0fxEIsAEaWC1hqEI14Wa.7Ps3iYFq';
  Rehash := BCrypt.NeedsRehash(Hash); // true
  Rehash := BCrypt.NeedsRehash(Hash, 14); // false
  BCrypt.Free;
end;

Evolution

This has had quite the evolution.

FreeBSD crypt.c

BCrypt for Delphi

BCrypt for Delphi, Lazarus, FPC

PHP password.c For the verify logic.

To here.