1
0
mirror of https://github.com/StephenGenusa/DCPCrypt.git synced 2025-06-02 21:57:23 +02:00

417 lines
14 KiB
ObjectPascal
Raw Normal View History

{******************************************************************************}
{* 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.