YOUSEFUL / DELPHI / COMPONENTS

TAtlasFileProtector

Non-visual component that encrypts and decrypts files and text with a password (PBKDF2-HMAC-SHA256 and AES-256-GCM in authenticated chunks), with progress and cancel.

Non-visual component Palette: Atlas Crypto Unit Atlas.CryptoComponents · Inherits TComponent · Product Atlas Crypto

Description

A component front end to TAtlasStreamCrypt for files and TAtlasSecretBox for text. The password is always a method parameter, never a property, so it cannot be streamed into a form file. Iterations sets the PBKDF2 cost used when encrypting; decryption reads the count from the data, so files sealed with any valid count open without touching the property.

Files are processed in 64 KB chunks, so memory use does not grow with file size. Output goes to <DestName>.part, which is renamed over DestName only when the whole operation succeeded; after a wrong password, altered data, cancel or any other failure the .part file is deleted and an existing DestName is left as it was. SourceName and DestName must be different files. Encrypted files are in the ASF1 format and encrypted text is URL-safe base64 of an ASP1 box; both are Atlas Crypto's own formats, described at the top of Atlas.CryptoBox.pas.

Every failure to decrypt raises EAtlasCryptoError with one message, Decryption failed: wrong key or password, or the data was altered. File-system problems raise the RTL's exceptions: EFOpenError for the source, EFCreateError for the .part file, EOSError if the final rename fails.

Members

MemberKindWhat it does
CreateconstructorCreates the component with Iterations set to TAtlasKdf.DefaultPasswordIterations (600,000) and no OnProgress handler.
EncryptFilemethodEncrypts SourceName into DestName with Password, using Iterations PBKDF2 rounds, a fresh random salt and nonce prefix, and 64 KB chunks.
DecryptFilemethodDecrypts a file written by EncryptFile (or TAtlasStreamCrypt.EncryptFile) into DestName.
EncryptTextmethodSeals Text (as UTF-8) with Password and Iterations rounds, and returns URL-safe base64 text that is safe to keep in a database column, an INI file or a URL.
DecryptTextmethodOpens text made by EncryptText or TAtlasSecretBox.SealText and returns the original.
IterationspropertyPBKDF2-HMAC-SHA256 rounds used by EncryptFile and EncryptText.
OnProgresseventCalled by EncryptFile and DecryptFile once per 64 KB chunk.

Constructors

Create constructor

constructor Create(AOwner: TComponent); override;

Creates the component with Iterations set to TAtlasKdf.DefaultPasswordIterations (600,000) and no OnProgress handler.

pascal
Protector := TAtlasFileProtector.Create(nil);

Properties

Iterations property

property Iterations: Integer read FIterations write SetIterations default TAtlasKdf.DefaultPasswordIterations;

PBKDF2-HMAC-SHA256 rounds used by EncryptFile and EncryptText. Default 600,000. Setting a value outside 10,000 to 20,000,000 raises EAtlasCryptoError (Iterations must be between 10000 and 20000000) and keeps the old value. More rounds make each password guess slower, for you and for an attacker; because the count is stored with the data, changing it later does not affect what was already sealed.

pascal
Protector.Iterations := 1000000;

Events

OnProgress event

property OnProgress: TAtlasCryptoProgressEvent read FOnProgress write FOnProgress;

Called by EncryptFile and DecryptFile once per 64 KB chunk. When encrypting, Done and Total count plain bytes and Total is the size of the source. When decrypting they count the sealed bytes after the header (ciphertext plus the 16-byte tag of each chunk), so the numbers are larger than the plain size, but Done still reaches Total at the end. Set Cancel to True to stop: the method raises EAtlasCryptoError and removes the partial output. Not called by the text methods. See TAtlasCryptoProgressEvent.

Methods

EncryptFile method

procedure EncryptFile(const SourceName, DestName, Password: string);

Encrypts SourceName into DestName with Password, using Iterations PBKDF2 rounds, a fresh random salt and nonce prefix, and 64 KB chunks. The result is larger than the source by a 36-byte header and a 16-byte tag per chunk (an empty source still has one chunk). Raises EAtlasCryptoError for an empty password, for identical source and destination, and when OnProgress cancels.

pascal
Protector.EncryptFile(FileName, FileName + '.sealed', Password);

DecryptFile method

procedure DecryptFile(const SourceName, DestName, Password: string);

Decrypts a file written by EncryptFile (or TAtlasStreamCrypt.EncryptFile) into DestName. The iteration count comes from the file, not from Iterations, and a header asking for more than 20,000,000 rounds is refused at once. A wrong password, an altered, truncated, reordered or extended file, or a file encrypted with a key instead of a password all raise the single decryption message and leave no output file.

pascal
Protector.DecryptFile(FileName + '.sealed', FileName, Password);

EncryptText method

function EncryptText(const Text, Password: string): string;

Seals Text (as UTF-8) with Password and Iterations rounds, and returns URL-safe base64 text that is safe to keep in a database column, an INI file or a URL. The same as TAtlasSecretBox.SealText(Password, Text, Iterations), but note the argument order: text first, password second. The result is different on every call. Does not call OnProgress.

pascal
Sealed := Protector.EncryptText('note to self', Password);

DecryptText method

function DecryptText(const Sealed, Password: string): string;

Opens text made by EncryptText or TAtlasSecretBox.SealText and returns the original. White space before and after Sealed is ignored. Any failure raises EAtlasCryptoError with the single decryption message. Sealed text first, password second.

pascal
Text := Protector.DecryptText(Sealed, Password);

Examples

Protect and restore a file from a form

A form built in code with a password box and a progress bar. Restore writes the file back without its .sealed extension; an existing file of that name is replaced only if decryption succeeds.

pascal
uses System.SysUtils, System.Classes, Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.Dialogs,
  Atlas.Crypto, Atlas.CryptoComponents;

type
  TProtectForm = class(TForm)
  private
    FProtector: TAtlasFileProtector;
    FPassword: TEdit;
    FBar: TProgressBar;
    procedure ProtectorProgress(Sender: TObject; Done, Total: Int64; var Cancel: Boolean);
  public
    constructor Create(AOwner: TComponent); override;
    procedure Protect(const FileName: string);
    procedure Restore(const SealedName: string);
  end;

constructor TProtectForm.Create(AOwner: TComponent);
begin
  inherited CreateNew(AOwner);
  Caption := 'Protect a file';
  FProtector := TAtlasFileProtector.Create(Self);
  FProtector.OnProgress := ProtectorProgress;
  FPassword := TEdit.Create(Self);
  FPassword.Parent := Self;
  FPassword.SetBounds(12, 12, 300, 24);
  FPassword.PasswordChar := '*';
  FBar := TProgressBar.Create(Self);
  FBar.Parent := Self;
  FBar.SetBounds(12, 48, 300, 20);
  FBar.Max := 1000;
end;

procedure TProtectForm.ProtectorProgress(Sender: TObject; Done, Total: Int64; var Cancel: Boolean);
begin
  if Total > 0 then FBar.Position := Round(Done / Total * 1000);
  FBar.Update;
end;

procedure TProtectForm.Protect(const FileName: string);
begin
  FProtector.EncryptFile(FileName, FileName + '.sealed', FPassword.Text);
  ShowMessage('Wrote ' + FileName + '.sealed');
end;

procedure TProtectForm.Restore(const SealedName: string);
begin
  try
    FProtector.DecryptFile(SealedName, ChangeFileExt(SealedName, ''), FPassword.Text);
  except
    on E: EAtlasCryptoError do ShowMessage(E.Message);
  end;
end;

Keep a secret setting as text

Seals a value into URL-safe text that can be stored in a configuration file, and opens it again. Prints the sealed text (different on every run) and api-secret-123.

pascal
uses System.SysUtils, Atlas.Crypto, Atlas.CryptoComponents;

procedure StoreSetting;
var
  Protector: TAtlasFileProtector;
  Sealed: string;
begin
  Protector := TAtlasFileProtector.Create(nil);
  try
    Sealed := Protector.EncryptText('api-secret-123', 'master password');
    WriteLn(Sealed);
    WriteLn(Protector.DecryptText(Sealed, 'master password'));
  finally
    Protector.Free;
  end;
end;

TAtlasStreamCrypt · TAtlasSecretBox · TAtlasCryptoProgressEvent · TAtlasHashCalculator · EAtlasCryptoError

Also in Atlas Crypto

TAtlasHashCalculator