Sample code for 30+ languages & platforms
Lazarus Pascal Requires Chilkat v11.6.0+

Derive a Key from a Password with Argon2

Demonstrates Crypt2.Argon2DeriveKey, which derives a key from a password using Argon2. The options JSON requires a salt (chosen and stored by the application) and accepts the cost parameters variant, version, iterations, memoryCostKb, parallelism, and keyLen.

Background. Argon2 (RFC 9106) is a memory-hard function that is deliberately expensive in CPU time and memory, which is what makes brute-forcing the password space costly. The argon2id variant is recommended unless there is a specific reason to choose argon2i or argon2d. memoryCostKb does the most to make an attack expensive. The salt, cost parameters, and options must be stored so the same key can be re-derived.

Chilkat Lazarus Pascal Downloads

Lazarus Pascal
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.BinData,
  Chilkat.JsonObject,
  Chilkat.Crypt2;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  success: Boolean;
  crypt: TCrypt2;
  password: string;
  saltB64: string;
  json: TJsonObject;
  bdKey: TBinData;
  keyHex: string;

begin
  success := False;

  crypt := TCrypt2.Create;

  //  The password should come from a secure source rather than being hard-coded.
  password := 'correct horse battery staple';

  //  A key-derivation salt is chosen and stored by the application.  Generate 16 random bytes and use
  //  them (base64) as the salt.  The salt encoding defaults to base64.
  crypt.EncodingMode := 'base64';
  saltB64 := crypt.GenRandomBytesENC(16);

  //  Build the Argon2 options JSON.  Only "salt" is required; every other member is optional and shown
  //  here with a typical explicit value:
  //    variant       argon2id (default), argon2i, or argon2d
  //    version       19 (default, 0x13) or 16 (0x10)
  //    iterations    passes over memory (t), >= 1, default 3
  //    memoryCostKb  memory in KB (m), >= 8*parallelism, default 65536 (64 MB)
  //    parallelism   lanes (p), default 1
  //    keyLen        derived key length in bytes, 4..1048576, default 32
  json := TJsonObject.Create;
  json.UpdateString('variant','argon2id');
  json.UpdateInt('version',19);
  json.UpdateInt('iterations',3);
  json.UpdateInt('memoryCostKb',65536);
  json.UpdateInt('parallelism',1);
  json.UpdateInt('keyLen',32);
  json.UpdateString('salt',saltB64);

  //  Derive the key.  The derived key is returned in the BinData (cleared first).
  bdKey := TBinData.Create;
  success := crypt.Argon2DeriveKey(password,json.Emit(),bdKey);
  if (success = False) then
    begin
      WriteLn(crypt.LastErrorText);
      Exit;
    end;

  //  The application stores the salt and cost parameters so the same key can be re-derived later.
  keyHex := bdKey.GetEncoded('hex');
  if (bdKey.LastMethodSuccess = False) then
    begin
      WriteLn(bdKey.LastErrorText);
      Exit;
    end;
  WriteLn('Derived ' + bdKey.NumBytes + '-byte key: ' + keyHex);


  crypt.Free;
  json.Free;
  bdKey.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.