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.
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
| Member | Kind | What it does |
|---|---|---|
Create | constructor | Creates the component with Iterations set to TAtlasKdf.DefaultPasswordIterations (600,000) and no OnProgress handler. |
EncryptFile | method | Encrypts SourceName into DestName with Password, using Iterations PBKDF2 rounds, a fresh random salt and nonce prefix, and 64 KB chunks. |
DecryptFile | method | Decrypts a file written by EncryptFile (or TAtlasStreamCrypt.EncryptFile) into DestName. |
EncryptText | method | 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. |
DecryptText | method | Opens text made by EncryptText or TAtlasSecretBox.SealText and returns the original. |
Iterations | property | PBKDF2-HMAC-SHA256 rounds used by EncryptFile and EncryptText. |
OnProgress | event | Called 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.
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.
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.
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.
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.
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.
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.
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.
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;
Related
TAtlasStreamCrypt · TAtlasSecretBox · TAtlasCryptoProgressEvent · TAtlasHashCalculator · EAtlasCryptoError