mirror of
https://github.com/StephenGenusa/DCPCrypt.git
synced 2025-06-02 21:57:23 +02:00
DCPCrypt by David Barton updated and tested with Delphi 2009, 2010, XE, XE2, XE3, XE4, XE5 by Warren Postma. Updated for XE7 by Stephen Genusa.
417 lines
14 KiB
ObjectPascal
417 lines
14 KiB
ObjectPascal
{******************************************************************************}
|
|
{* DCPcrypt v2.0 written by David Barton (crypto@cityinthesky.co.uk) **********}
|
|
{******************************************************************************}
|
|
{* A file encryption/decryption demo ******************************************}
|
|
{******************************************************************************}
|
|
{* Copyright (c) 2003 David Barton *}
|
|
{* Permission is hereby granted, free of charge, to any person obtaining a *}
|
|
{* copy of this software and associated documentation files (the "Software"), *}
|
|
{* to deal in the Software without restriction, including without limitation *}
|
|
{* the rights to use, copy, modify, merge, publish, distribute, sublicense, *}
|
|
{* and/or sell copies of the Software, and to permit persons to whom the *}
|
|
{* Software is furnished to do so, subject to the following conditions: *}
|
|
{* *}
|
|
{* The above copyright notice and this permission notice shall be included in *}
|
|
{* all copies or substantial portions of the Software. *}
|
|
{* *}
|
|
{* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *}
|
|
{* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *}
|
|
{* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *}
|
|
{* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *}
|
|
{* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *}
|
|
{* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *}
|
|
{* DEALINGS IN THE SOFTWARE. *}
|
|
{******************************************************************************}
|
|
{* *}
|
|
{* This isn't the most well written of demos but it should be relatively *}
|
|
{* informative. Any problems, queries, (bugs?) feel free to email me at the *}
|
|
{* above address (I may not reply depending on my workload at the time, but I *}
|
|
{* will do my best). *}
|
|
{* Dave. *}
|
|
{* *}
|
|
{* Note: this program does not store the cipher or hash used to encrypt the *}
|
|
{* original file and so you will need to note this yourself. Also it will *}
|
|
{* happily decrypt with the wrong cipher/hash/passphrase and give you utter *}
|
|
{* garbage out :-) *}
|
|
{* *}
|
|
{******************************************************************************}
|
|
unit uMain;
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
Dialogs, DCPtiger, DCPsha512, DCPsha256, DCPsha1, DCPripemd160,
|
|
DCPripemd128, DCPmd5, DCPmd4, DCPcrypt2, DCPhaval, DCPtwofish, DCPtea,
|
|
DCPserpent, DCPblockciphers, DCPrijndael, DCPrc4, DCPrc2, DCPice, DCPdes,
|
|
DCPcast128, DCPblowfish, StdCtrls, Buttons;
|
|
|
|
type
|
|
TfrmMain = class(TForm)
|
|
grpInput: TGroupBox;
|
|
boxInputFile: TEdit;
|
|
btnInputBrowse: TSpeedButton;
|
|
lblInputFileSize: TLabel;
|
|
dblInputFileSize: TLabel;
|
|
grpOutput: TGroupBox;
|
|
boxOutputFile: TEdit;
|
|
btnOutputBrowse: TSpeedButton;
|
|
grpOptions: TGroupBox;
|
|
cbxCipher: TComboBox;
|
|
lblCipher: TLabel;
|
|
lblHash: TLabel;
|
|
cbxHash: TComboBox;
|
|
lblKeySize: TLabel;
|
|
dblKeySize: TLabel;
|
|
boxPassphrase: TEdit;
|
|
lblPassphrase: TLabel;
|
|
boxConfirmPassphrase: TEdit;
|
|
lblConfirmPassphrase: TLabel;
|
|
btnEncrypt: TButton;
|
|
btnDecrypt: TButton;
|
|
btnClose: TButton;
|
|
DCP_blowfish1: TDCP_blowfish;
|
|
DCP_cast1281: TDCP_cast128;
|
|
DCP_des1: TDCP_des;
|
|
DCP_3des1: TDCP_3des;
|
|
DCP_ice1: TDCP_ice;
|
|
DCP_thinice1: TDCP_thinice;
|
|
DCP_ice21: TDCP_ice2;
|
|
DCP_rc21: TDCP_rc2;
|
|
DCP_rc41: TDCP_rc4;
|
|
DCP_rijndael1: TDCP_rijndael;
|
|
DCP_serpent1: TDCP_serpent;
|
|
DCP_tea1: TDCP_tea;
|
|
DCP_twofish1: TDCP_twofish;
|
|
DCP_haval1: TDCP_haval;
|
|
DCP_md41: TDCP_md4;
|
|
DCP_md51: TDCP_md5;
|
|
DCP_ripemd1281: TDCP_ripemd128;
|
|
DCP_ripemd1601: TDCP_ripemd160;
|
|
DCP_sha11: TDCP_sha1;
|
|
DCP_sha2561: TDCP_sha256;
|
|
DCP_sha3841: TDCP_sha384;
|
|
DCP_sha5121: TDCP_sha512;
|
|
DCP_tiger1: TDCP_tiger;
|
|
dlgInput: TOpenDialog;
|
|
dlgOutput: TSaveDialog;
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure boxInputFileExit(Sender: TObject);
|
|
procedure btnInputBrowseClick(Sender: TObject);
|
|
procedure btnOutputBrowseClick(Sender: TObject);
|
|
procedure cbxCipherChange(Sender: TObject);
|
|
procedure boxPassphraseChange(Sender: TObject);
|
|
procedure btnEncryptClick(Sender: TObject);
|
|
procedure btnDecryptClick(Sender: TObject);
|
|
procedure btnCloseClick(Sender: TObject);
|
|
private
|
|
{ Private declarations }
|
|
procedure DisableForm;
|
|
public
|
|
{ Public declarations }
|
|
end;
|
|
|
|
var
|
|
frmMain: TfrmMain;
|
|
|
|
implementation
|
|
|
|
{$R *.dfm}
|
|
|
|
procedure TfrmMain.DisableForm;
|
|
begin
|
|
grpInput.Enabled:= false;
|
|
grpOutput.Enabled:= false;
|
|
grpOptions.Enabled:= false;
|
|
btnEncrypt.Enabled:= false;
|
|
btnDecrypt.Enabled:= false;
|
|
end;
|
|
|
|
procedure TfrmMain.FormCreate(Sender: TObject);
|
|
var
|
|
i: integer;
|
|
begin
|
|
Randomize;
|
|
ClientWidth:= 296;
|
|
ClientHeight:= 440;
|
|
MessageDlg('This is a file encryption demo using the DCPcrypt component set.'+#13+'For more information see http://www.cityinthesky.co.uk/cryptography.html',mtInformation,[mbOK],0);
|
|
|
|
// iterate through all the components and find the ciphers/hashes
|
|
for i := 0 to (ComponentCount - 1) do
|
|
begin
|
|
if (Components[i] is TDCP_cipher) then
|
|
cbxCipher.Items.AddObject(TDCP_cipher(Components[i]).Algorithm,Components[i])
|
|
else if (Components[i] is TDCP_hash) then
|
|
cbxHash.Items.AddObject(TDCP_hash(Components[i]).Algorithm,Components[i]);
|
|
end;
|
|
if (cbxCipher.Items.Count = 0) then
|
|
begin
|
|
MessageDlg('No ciphers were found',mtError,[mbOK],0);
|
|
DisableForm;
|
|
end
|
|
else
|
|
begin
|
|
cbxCipher.ItemIndex := 0;
|
|
if (cbxHash.Items.Count = 0) then
|
|
begin
|
|
MessageDlg('No hashes were found',mtError,[mbOK],0);
|
|
DisableForm;
|
|
end
|
|
else
|
|
begin
|
|
cbxHash.ItemIndex := 0;
|
|
cbxCipher.OnChange(cbxCipher);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Add commas into a numerical string (e.g. 12345678 becomes 12,345,678)
|
|
// Not the best way but I can't find the code I wrote last time...
|
|
function AddCommas(const S: string): string;
|
|
var
|
|
i, j: integer;
|
|
begin
|
|
i := Length(S) mod 3;
|
|
if ((i <> 0) and (Length(S) > 3)) then
|
|
Result := Copy(S,1,i) + ',';
|
|
for j := 0 to ((Length(S) div 3) - 2) do
|
|
Result := Result + Copy(S,1 + i + j*3,3) + ',';
|
|
if (Length(S) > 3) then
|
|
Result := Result + Copy(S,Length(S) - 2,3)
|
|
else
|
|
Result := S;
|
|
end;
|
|
|
|
procedure TfrmMain.boxInputFileExit(Sender: TObject);
|
|
var
|
|
strmInput: TFileStream;
|
|
begin
|
|
if (boxInputFile.Text = '') then
|
|
dblInputFileSize.Caption := 'no file specified'
|
|
else if FileExists(boxInputFile.Text) then
|
|
begin
|
|
// If the file exists then see how big it is
|
|
strmInput := nil;
|
|
try
|
|
strmInput := TFileStream.Create(boxInputFile.Text,fmOpenRead);
|
|
dblInputFileSize.Caption := AddCommas(IntToStr(strmInput.Size)) + ' bytes';
|
|
strmInput.Free;
|
|
except
|
|
strmInput.Free;
|
|
dblInputFileSize.Caption := 'unable to open file';
|
|
end;
|
|
end
|
|
else
|
|
dblInputFileSize.Caption := 'file does not exist';
|
|
end;
|
|
|
|
procedure TfrmMain.btnInputBrowseClick(Sender: TObject);
|
|
begin
|
|
if dlgInput.Execute then
|
|
begin
|
|
boxInputFile.Text := dlgInput.FileName;
|
|
boxInputFile.OnExit(boxInputFile);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmMain.btnOutputBrowseClick(Sender: TObject);
|
|
begin
|
|
if dlgOutput.Execute then
|
|
boxOutputFile.Text := dlgOutput.FileName;
|
|
end;
|
|
|
|
procedure TfrmMain.cbxCipherChange(Sender: TObject);
|
|
var
|
|
Cipher: TDCP_cipher;
|
|
Hash: TDCP_hash;
|
|
begin
|
|
// Set the effective keysize to be the minimum of the hash size and the max key size
|
|
// i.e. if the max key size is sufficiently large then use the entire hash as the
|
|
// key, other wise truncate the hash
|
|
Cipher := TDCP_cipher(cbxCipher.Items.Objects[cbxCipher.ItemIndex]);
|
|
Hash := TDCP_hash(cbxHash.Items.Objects[cbxHash.ItemIndex]);
|
|
if (Cipher.MaxKeySize < Hash.HashSize) then
|
|
dblKeySize.Caption := IntToStr(Cipher.MaxKeySize) + ' bits'
|
|
else
|
|
dblKeySize.Caption := IntToStr(Hash.HashSize) + ' bits'
|
|
end;
|
|
|
|
procedure TfrmMain.boxPassphraseChange(Sender: TObject);
|
|
begin
|
|
if (Length(boxPassphrase.Text) > 0) then
|
|
begin
|
|
btnDecrypt.Enabled := true;
|
|
if (boxPassphrase.Text = boxConfirmPassphrase.Text) then
|
|
btnEncrypt.Enabled := true
|
|
else
|
|
btnEncrypt.Enabled := false;
|
|
end
|
|
else
|
|
btnDecrypt.Enabled := false;
|
|
end;
|
|
|
|
function Min(a, b: integer): integer;
|
|
begin
|
|
if (a < b) then
|
|
Result := a
|
|
else
|
|
Result := b;
|
|
end;
|
|
|
|
|
|
function DoEncryptFile(infile,outfile,passphrase:String;
|
|
Hash: TDCP_hash;Cipher: TDCP_cipher):Boolean;
|
|
var
|
|
CipherIV: array of byte; // the initialisation vector (for chaining modes)
|
|
HashDigest: array of byte; // the result of hashing the passphrase with the salt
|
|
Salt: array[0..7] of byte; // a random salt to help prevent precomputated attacks
|
|
strmInput, strmOutput: TFileStream;
|
|
i: integer;
|
|
begin
|
|
result := true;
|
|
strmInput := nil;
|
|
strmOutput := nil;
|
|
try
|
|
strmInput := TFileStream.Create(infile,fmOpenRead);
|
|
strmOutput := TFileStream.Create(outfile,fmCreate);
|
|
|
|
SetLength(HashDigest,Hash.HashSize div 8);
|
|
for i := 0 to 7 do
|
|
Salt[i] := Random(256); // just fill the salt with random values (crypto secure PRNG would be better but not _really_ necessary)
|
|
strmOutput.WriteBuffer(Salt,Sizeof(Salt)); // write out the salt so we can decrypt!
|
|
Hash.Init;
|
|
Hash.Update(Salt[0],Sizeof(Salt)); // hash the salt
|
|
Hash.UpdateStr(passphrase); // and the passphrase
|
|
Hash.Final(HashDigest[0]); // store the output in HashDigest
|
|
|
|
if (Cipher is TDCP_blockcipher) then // if the cipher is a block cipher we need an initialisation vector
|
|
begin
|
|
SetLength(CipherIV,TDCP_blockcipher(Cipher).BlockSize div 8);
|
|
for i := 0 to (Length(CipherIV) - 1) do
|
|
CipherIV[i] := Random(256); // again just random values for the IV
|
|
strmOutput.WriteBuffer(CipherIV[0],Length(CipherIV)); // write out the IV so we can decrypt!
|
|
Cipher.Init(HashDigest[0],Min(Cipher.MaxKeySize,Hash.HashSize),CipherIV); // initialise the cipher with the hash as key
|
|
TDCP_blockcipher(Cipher).CipherMode := cmCBC; // use CBC chaining when encrypting
|
|
end
|
|
else
|
|
Cipher.Init(HashDigest[0],Min(Cipher.MaxKeySize,Hash.HashSize),nil); // initialise the cipher with the hash as key
|
|
|
|
Cipher.EncryptStream(strmInput,strmOutput,strmInput.Size); // encrypt the entire file
|
|
Cipher.Burn; // important! get rid of keying information
|
|
strmInput.Free;
|
|
strmOutput.Free;
|
|
|
|
except
|
|
strmInput.Free;
|
|
strmOutput.Free;
|
|
result := false;
|
|
end;
|
|
end;
|
|
|
|
function DoDecryptFile(infile,outfile,passphrase:String;
|
|
Hash: TDCP_hash;Cipher: TDCP_cipher):Boolean;
|
|
var
|
|
CipherIV: array of byte; // the initialisation vector (for chaining modes)
|
|
HashDigest: array of byte; // the result of hashing the passphrase with the salt
|
|
Salt: array[0..7] of byte; // a random salt to help prevent precomputated attacks
|
|
strmInput, strmOutput: TFileStream;
|
|
begin
|
|
|
|
strmInput := nil;
|
|
strmOutput := nil;
|
|
try
|
|
strmInput := TFileStream.Create(infile,fmOpenRead);
|
|
strmOutput := TFileStream.Create(outfile,fmCreate);
|
|
|
|
SetLength(HashDigest,Hash.HashSize div 8);
|
|
strmInput.ReadBuffer(Salt[0],Sizeof(Salt)); // read the salt in from the file
|
|
Hash.Init;
|
|
Hash.Update(Salt[0],Sizeof(Salt)); // hash the salt
|
|
Hash.UpdateStr(passphrase); // and the passphrase
|
|
Hash.Final(HashDigest[0]); // store the hash in HashDigest
|
|
|
|
if (Cipher is TDCP_blockcipher) then // if it is a block cipher we need the IV
|
|
begin
|
|
SetLength(CipherIV,TDCP_blockcipher(Cipher).BlockSize div 8);
|
|
strmInput.ReadBuffer(CipherIV[0],Length(CipherIV)); // read the initialisation vector from the file
|
|
Cipher.Init(HashDigest[0],Min(Cipher.MaxKeySize,Hash.HashSize),CipherIV); // initialise the cipher
|
|
TDCP_blockcipher(Cipher).CipherMode := cmCBC;
|
|
end
|
|
else
|
|
Cipher.Init(HashDigest[0],Min(Cipher.MaxKeySize,Hash.HashSize),nil); // initialise the cipher
|
|
|
|
Cipher.DecryptStream(strmInput,strmOutput,strmInput.Size - strmInput.Position); // decrypt!
|
|
Cipher.Burn;
|
|
strmInput.Free;
|
|
strmOutput.Free;
|
|
|
|
except
|
|
strmInput.Free;
|
|
strmOutput.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TfrmMain.btnEncryptClick(Sender: TObject);
|
|
var
|
|
Hash: TDCP_hash; // the hash to use
|
|
Cipher: TDCP_cipher; // the cipher to use
|
|
begin
|
|
if FileExists(boxOutputFile.Text) then
|
|
if (MessageDlg('Output file already exists. Overwrite?',mtConfirmation,mbYesNoCancel,0) <> mrYes) then
|
|
Exit;
|
|
|
|
Hash := TDCP_hash(cbxHash.Items.Objects[cbxHash.ItemIndex]);
|
|
Cipher := TDCP_cipher(cbxCipher.Items.Objects[cbxCipher.ItemIndex]);
|
|
|
|
if not DoEncryptFile( boxInputFile.Text,
|
|
boxOutputFile.Text,
|
|
boxPassphrase.Text,
|
|
hash,
|
|
cipher ) then begin
|
|
MessageDlg('An error occurred while processing the file',mtError,[mbOK],0);
|
|
end else begin
|
|
MessageDlg('File encrypted',mtInformation,[mbOK],0);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TfrmMain.btnDecryptClick(Sender: TObject);
|
|
var
|
|
Cipher: TDCP_cipher; // the cipher to use
|
|
Hash: TDCP_hash; // the hash to use
|
|
begin
|
|
if FileExists(boxOutputFile.Text) then
|
|
if (MessageDlg('Output file already exists. Overwrite?',mtConfirmation,mbYesNoCancel,0) <> mrYes) then
|
|
Exit;
|
|
|
|
Hash := TDCP_hash(cbxHash.Items.Objects[cbxHash.ItemIndex]);
|
|
Cipher := TDCP_cipher(cbxCipher.Items.Objects[cbxCipher.ItemIndex]);
|
|
|
|
if not DoDecryptFile( boxInputFile.Text,
|
|
boxOutputFile.Text,
|
|
boxPassphrase.Text,
|
|
hash,
|
|
cipher ) then begin
|
|
|
|
MessageDlg('An error occurred while processing the file',mtError,[mbOK],0);
|
|
|
|
|
|
end else begin
|
|
MessageDlg('File decrypted',mtInformation,[mbOK],0);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TfrmMain.btnCloseClick(Sender: TObject);
|
|
begin
|
|
Close;
|
|
end;
|
|
|
|
|
|
end.
|
|
|