Delphi authentication of Drupal 7 passwords

79 Views Asked by At

I am trying to authenticate Drupal 7 passwords with Delphi 10.

I am mimicking the _password_crypt() function which uses the PHP hash( ,,TRUE) function.

The hashing process is repeated and starts with a string and switched to bytes;

  $hash = hash($algo, $salt . $password, TRUE);
  do {
    $hash = hash($algo, $hash . $password, TRUE);
  } while (--$count);
  $len = strlen($hash);
  $output = $setting . _password_base64_encode($hash, $len);

Can this be replicated with THashSHA2? If so, can you outline the approach?

1

There are 1 best solutions below

0
Ephraim On
unit DrupalPasseordClass;

interface

uses System.SysUtils, System.Classes, System.Hash;

const
  DRUPAL_HASH_LENGTH=55;

type
  TDrupalPassword=record

  private
    function _password_itoa64(): String;
    function CombineByteArrays(ABytes, BBytes: TBytes): TBytes;
    function _password_get_count_log2(setting: String): Integer;
    function _password_base64_encode(hash: TBytes; count: Integer): String;
  public
    function CheckPassword(Password, StoredHash: String): Boolean;

End;


implementation

function TDrupalPassword._password_itoa64(): String;
begin
  result:='./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
end;

function TDrupalPassword._password_get_count_log2(setting: String): Integer;
var
 itoa64: String;
begin
  itoa64:= _password_itoa64();
  result:=Pos(setting[4], itoa64)-1;
end;

function TDrupalPassword.CombineByteArrays(ABytes, BBytes: TBytes): TBytes;
var
  k: Integer;
begin
  result:=ABytes;
  SetLength(result, Length(ABytes)+Length(BBytes));
  for k := 0 to Length(BBytes)-1 do
    result[Length(ABytes)+k]:=BBytes[k];
end;

function TDrupalPassword.CheckPassword(Password, StoredHash: String): Boolean;
var
  Count, k: Integer;
  algorithm, ComputedHash, Salt, Setting: String;
  Hash: TBytes;
  bPassword, bSalt, bSeed: TBytes;
  BS: TBytesStream;
begin
  BS:=TBytesStream.Create;
  try
    algorithm:=Copy(StoredHash,1, 3);
    if not algorithm.Equals('$S$') then
       Exit(False);

    Setting:=Copy(StoredHash,1, 12);
    Salt:=Copy(StoredHash, 5, 8);

    bSalt:=TEncoding.UTF8.GetBytes(sALT);
    bPassword:=TEncoding.UTF8.GetBytes(Password);
    bSeed:=CombineByteArrays(bSalt, bPassword);

    BS.WriteData(bSeed, Length(bSeed));
    BS.Seek(0, soBeginning);
    Hash:=THashSHA2.GetHashBytes(BS, SHA512);

    Count:=1 ShL _password_get_count_log2(Setting);
    for k:=1 to Count do
    begin
      bSeed:=CombineByteArrays(Hash, bPassword);
      BS.Clear;
      BS.Write(bSeed, Length(bSeed));
      BS.Seek(0, soBeginning);
      Hash:=THashSHA2.GetHashBytes(BS, SHA512);
    end;
    ComputedHash:=Setting+_password_base64_encode(Hash, Length(Hash));
    ComputedHash:=Copy(ComputedHash,1,DRUPAL_HASH_LENGTH);
    result:=(StoredHash=ComputedHash);
  finally
    bs.Free;
  end;
end;

function TDrupalPassword._password_base64_encode(hash: TBytes; count: Integer): String;
var
  Index, Value: Integer;
  itoa64: String;
begin
  result:='';
  itoa64:= _password_itoa64();
  Index:=0;
  repeat
    value:=ord(hash[Index]);
    result:=result+itoa64[(value and $3f)+1];
    inc(Index);
    if (Index < count) then
      value:=value or  ord(Hash[Index]) shl 8;

    result:=result+itoa64[(value shr 6 and $3f)+1];
    if (Index >= count) then
      break;
    inc(Index);

    if (Index < count) then
      value:=value or ord(Hash[Index]) shl 16;

    result:=result+itoa64[(value shr 12 and $3f)+1];
    if (Index>= count) then
      break;
    inc(Index);
    result:=result+itoa64[(value shr 18 and $3f)+1];
  until (Index>count);
end;

end.