|
Introduction to strong crypto programming in Borland Delphi
2001 Uri
Fridman <urifrid@hushmail.com>
Download the source
code of this article!
Some words
Historically, cryptography was used by governments throughout the ages
to conceal secret messages. The need to keep "state secrets" out of
reach of the general public led to the invention of several encryption
methods.
In the 20th century these methods were taken one step beyond as they
were automated by introducing machines such as the Enigma (broken by the
allies in WWII).
With the introduction of the computer, this automated process became
even easier and yet newer cryptography methods were invented.
Today computers are everywhere.
Governments have access to almost everything. System Administrators have
access to most of the files in your HD. Competition has access to stolen
laptops... I mean, I don't want to sound "too" paranoid but in today's
world, privacy is getting more and more difficult to achieve.
But don't worry. We have Cryptography to help us.
Warranty
NO WARRANTY.
THIS ARTICLE COMES WITH NO WARRANTY, AND IS PROVIDED ON AN "AS IS"
BASIS. YOU ASSUME THE ENTIRE COST OF ANY DAMAGE RESULTING FROM THE
INFORMATION CONTAINED IN OR PRODUCED BY THE CONTENTS OF THIS ARTICLE.
YOU ASSUME ALL RESPONSIBILITIES FOR SELECTION OF THIS ARTICLE TO
ACHIEVE YOUR INTENDED RESULTS, AND FOR THE INSTALLATION OF, USE OF, AND
RESULTS OBTAINED FROM IT. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE
LAW, URI, HIS SUPPLIERS AND OTHERS WHO MAY DISTRIBUTE THIS ARTICLE
DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, CONFORMANCE WITH DESCRIPTION, AND NON-INFRINGEMENT,
WITH RESPECT TO THIS SOFTWARE PRODUCT.
IN OTHER WORDS: USE AT YOUR OWN RISK
Definitions
In crypto terminology a plain readable file is called Plaintext. The
process of scrambling its contents in order to make it unreadable is
called Encryption, thus we are Encrypting the file. This unreadable
message is now called Ciphertext. The opposite operation, unscrambling
the ciphertext, is called Decryption, thus we Decrypt the file.
These two processes use a Key to encrypt or decrypt the file. Only by
knowing the correct key we should be able to decrypt a message encrypted
with that key.
Warning
These definitions are all good and nice but the real world is a little
different. It might look easy and fun to create Ciphers (cryptographic
algorithms) but it's not.
Good crypto looks the same as bad crypto. Both output unreadable
garbage. But don't be fooled. Always use public and well known ciphers,
one that has been studied by professionals and has been analyzed over
the years. Always try to get the source code for the ciphers. Go to the
following link to learn how to differentiate a good product from a bad
one:
http://www.interhack.net/people/cmcurtin/snake-oil-faq.html
I tried to be as accurate as possible when writing this article. However
English is not my mother tongue and I'm not a professional
cryptographer, so some mistakes can appear in the article. Please feel
free to comment about them or other issues by sending an email
to <urifrid@hushmail.com>.
The algorithm
I chose Twofish as the algorithm for this introduction. I did it for
several reasons:
It was written by Bruce Schneier (http://www.counterpane.com), a well known
cryptographer and cryptanalyst
It hasn't been broken
It's easy to use
IT'S PUBLIC. The source code can be found in several places.
It can be used in any mode: ECB, CBC, CFB 8bit, OFB, OFB counter
8bit. To know more about modes read Applied Crytography by Bruce
Schneier or go to his web site: http://www.counterpane.com
In short, Twofish is:
A 128 bit block cipher. Encrypts blocks of 128 bits in size.
128, 192 or 256 bits keys. Can use several lengths of keys. In
general, if a cipher has 128 bits key it would take 2^128 tries to
try each and every key.
Performs 16 rounds encryption. It iterates the cipher 16 times before
returning ciphertext.
The code in Delphi for this algorithm was ported from the original C
implementation by David Barton (davebarton@bigfoot.com). You can
download several other ciphers from his web page:
http://www.scramdisk.clara.net
I'm providing all the files that this article uses. You will find them
in the archive that accompanies this article.
The code
Create a new project. Add 3 edit boxes
(Edit1, Edit2 and Edit3). Add 4 buttons
(encrypt, decrypt, hash and browse).
Also add a radiogroup and call it RadioHex, insert two radiobuttons
in there, one "Show in Hex" and the other "Show in Base64".
We are going to add the following to the uses clause:
uses SHA1, Twofish, Base64;
SHA1 is the Hash. One-way hash functions are like fingerprints of the
data being hashed, it takes variable-length input and produces a
fixed-length output (160 bits in SHA1). The hash function ensures
that, if the information is changed in any way –even by just one bit–
an entirely different output value is produced. This is useful in many
ways. One of them for example is to encrypt a file and then make a
hash of that encrypted file. You send both the file and the hash of
it. When the other end gets the file he or she can check if the file
was tampered with by creating a hash of the file and comparing it with
the hash sent. Of course the hash has to be sent in a secure way to be
sure that it is untouched (probably using some public key encryption
algorithm like RSA). We are going to use hashes to create a
fingerprint of both the password entered by the user and the encrypted
file.
TWOFISH is the encryption algorithm.
Base64 is a way of coding output so it has only printable characters.
Add the following procedures to your program:
// produces a hash of a string
procedure _HashString(s: string; var Digest: TSHA1Digest);
var
Context: TSHA1Context; // record to store intermediate
// data
begin
SHA1Init(Context); // initialize the data record
SHA1Update(Context,@S[1],Length(S)); // update the data record with
// the string
SHA1Final(Context,Digest); // produce the final hash
end;
// produces a hash of a file
procedure _HashFile(filename: string; var Digest: TSHA1Digest);
var
Context: TSHA1Context; // record to store intermediate
// data
Source: file; // source file
Buffer: array[1..8192] of byte; // read buffer
Read: integer; // number of bytes read
begin
AssignFile(Source,filename);
try
Reset(Source,1);
except
MessageDlg('Unable to open source file.',mtInformation,[mbOK],0);
Exit;
end;
SHA1Init(Context); // initialize the data structure
repeat
BlockRead(Source,Buffer,Sizeof(Buffer),Read);
SHA1Update(Context,@Buffer,Read); // update the hash
until Read<> Sizeof(Buffer);
SHA1Final(Context,Digest); // produce the final hash
CloseFile(Source);
end;
These procedures will produce the One-Way Hash for files and strings.
Note that the encryption and decryption process will be like this:
get a hash of the password
initialize the key in the algorithm using the hash
make an IV (the first block to use in the chaining modes)
encrypt or decrypt
burn data (key in memory, IV, etc.) and sets the algorithm for the
next task.
(Optional) get a hash of the encrypted file
In (1) we generate the hash of the user's password. This hash will be
used in (2) to initialize the key. This key is NOT the password the
user typed. In (3) we generate the first block of encrypted data, this
block contains random data. This is done because we are using one of
the chaining modes and we need and each block depends on the previous
one to be encrypted. This helps to hide patters in the text like
redundancy and other file headers (like the "PK" in zipped files). In
(4) we perform the actual encryption/decryption and in (5) we get rid
of the data place in memory (we don't want any sniffer stealing our
password, right?)
So, in the OnClick procedure for the button you
called Encrypt you can
write the following (Warning, this will OVERWRITE the original file.
Unless you remember the password you won't be able to recover the data):
var
KeyData: TTwofishData; // the initialized key data
Digest: TSHA1Digest;
IV: array[0..15] of byte; // the initialization vector needed for
// chaining modes
Buffer: array[0..8191] of byte;
Source,source2: file;
i, j, n: integer;
Key: string;
NumRead, NumWritten: Integer;
begin
Key:= edit2.Text; // you wrote the password in edit2
_HashString(Key,Digest);
TwofishInit(KeyData,@Digest,Sizeof(Digest),nil); // initialize the
// key data using a hash of the key
FillChar(IV,Sizeof(IV),0); // make the IV all zeros
TwofishEncryptCBC(KeyData,@IV,@IV); // encrypt the IV to
// get a 'random' IV
Move(IV,KeyData.InitBlock,Sizeof(KeyData.InitBlock)); // move the
// IV into the keydata so can use chaining
TwofishReset(KeyData); // reset the keydata so it uses the new IV
AssignFile(Source, edit1.Text); // file to encrypt in edit1
try
Reset(Source,1);
except
TwofishBurn(KeyData); // get rid of the data in memory
MessageDlg('File cannot be opened...', mtInformation, [mbOK], 0);
Edit2.text:='00000000000000000000000000000000';
Edit2.text:='';
Exit;
end;
repeat
n:= FilePos(Source);
BlockRead(Source,Buffer,Sizeof(Buffer),i);
for j:= 1 to (i div 16) do // 16 is the blocksize of Twofish
// so process in 16 byte blocks
TwofishEncryptCBC(KeyData,@Buffer[(j-1)*Sizeof(IV)], // encrypt!
@Buffer[(j-1)*Sizeof(IV)]);
if (i mod 16)<> 0 then // encrypt the last bytes that don't
// fit in to a full block
begin
Move(KeyData.LastBlock,IV,Sizeof(IV));
TwofishEncryptCBC(KeyData,@IV,@IV); // encrypt the full block
// again (so that it is encrypted twice)
for j:= 1 to (i mod 16) do
// xor this encrypted block with the short block
Buffer[(i and not 15)+j]:= Buffer[(i and not 15)+j] xor IV[j];
end;
Seek(Source,n);
BlockWrite(Source,Buffer,i); // write out the buffer to the file
until i<> Sizeof(Buffer);
CloseFile(Source);
TwofishBurn(KeyData); // get rid of data
Edit2.text:='00000000000000000000000000000000';
Edit2.text:='';
Edit1.text:='';
MessageDlg('All done. File Encrypted with the same name as the ' +
'original.', mtInformation,[mbOK],0);
end;
Presto! We just encrypted the file in Edit1 with
the password in Edit2!
To decrypt do exactly the same, but instead of using TwofishEncryptCBC()
use:
TwofishDecryptCBC(KeyData,@Buffer[(j-1)*Sizeof(IV)],
@Buffer[(j-1)*Sizeof(IV)]);
Simple, huh?
Ok, you have your encrypted file. Some nice touch will be to generate
the hash of that encrypted file and send it along with it.
To achieve this we will use the _HashFile procedure we wrote before. On
the OnClick procedure for the button you named Hash add
the following:
procedure TForm1.HashClick(Sender: TObject);
var
Digest: TSHA1Digest; // binary form of the hash
i: integer;
begin
if Edit1.Text='' then exit; // make sure we have a file to hash
_HashFile(Edit1.Text,Digest); // calculate the hash
// and store in Digest
if radiohex.ItemIndex=0 then // we want to show the
// hash in hexadecimal
begin
Edit3.Text:= '0x';
for i:= 0 to (Sizeof(Digest)-1) do
Edit3.Text:= Edit3.Text+IntToHex(Digest[i],2); // convert Digest
// to a hexadecimal string
end
else // in base64
begin
Edit3.Text:= '';
for i:= 0 to (Sizeof(Digest)-1) do
Edit3.Text:= Edit3.Text+chr(Digest[i]); // convert Digest to a
// base64
Edit3.Text:=B64Encode(Edit3.Text);
end;
end;
Now can choose to save the hash to a file and compress both the
encrypted file and its hash in one single .zip archive. We can now send
the zip file to a friend being sure that only him/her can decrypt it.
This is a simple application. A lot can be added to make it more secure
but this is just and example, it's up to you to add your own code and
remember:
"...may the source be with you."
Source Code
for SHA1, Twofish and Base64 along
with a program using them
can be found in the archive that
accompanies this article. Please be sure to read the warranty very carefully.
Thanks.
Uri, February 10, 2001. - urifrid@hushmail.com
NOTE: In case you are insterested, Uri has a freeware application that
presents an explorer-like interface and allows to encrypt and decrypt
files. There's also a console version available:
http://www.geocities.com/urifrid/soft.html
|