After much procrastinating I'm finally ready to release the initial version of
my encryption library.  I wrote this in response to random requests I keep
seeing in various newsgroups for encryption libraries, usually with no
responses.  This library provides a (hopefully) universal interface to all
kinds of conventional-key encryption algorithms in an easy-to-use manner.  The
library is divided into two parts:

  1. Routines to query the library capabilities.
  2. Routines to perform the en/decryption.

The library currently provides the following encryption algorithms and modes:

    MDC/SHS CFB
    DES ECB, CBC, CFB, OFB, PCBC
    3DES ECB, CBC, CFB, OFB
    IDEA ECB, CBC, CFB, OFB
    RC4

Notes

1. This is a pre-release version of the library.  There are some binaries
   included for people who don't have C compiler but still want to use the
   libraries with Visual Cobol for Windows or whatever (I know about "no
   binaries in alt.sources" but in this case it's appropriate, I hope).

2. Some older versions of gcc seem to choke on the endianness test used in
   crypt.h.

3. Currently only a few encryption types are implemented.  I have code for, it
   seems, half the encryption types in existence, but I've tried to limit
   what's included to the more useful ones.  You can add your own favourite
   algorithm as a plug-in module if you want.

4. The DES/PCBC code has a chaining problem.  Our people are working on it.

Things to Do

1. The Visual Basic interface hasn't been tested much.  If someone could do
   a Delphi interface and a VBX or OCX version that'd be nice.

2. A proper install script with everything would be nice for Unix users.
   Anyone want to create one of these?

3. Optimised asm implementations of some of the routines would be useful.
   There are 80x86 and 68K asm routines included for IDEA, but I haven't had
   time to tie them into the code yet.  I have 386+ asm code for SHA, but need
   to tie that in as well.  Volunteers?

4. The SHA code doesn't work on an Alpha because of some problems with 64-bit
   ints, but I really don't want to spend hours with adb or dbx or whatever
   this system has installed to dig out the problem.  Anyone want to fix this?
   All you need to do to test it is compile shs.c standalone with TEST defined.


Requirements for a Standard Encryption Library
----------------------------------------------

- It should be idiot-proof.

  It's quite difficult to implement insecure encryption using this code without
  actually trying.

- It should provide extensive error checking.

  Each parameter and function call is checked for errors before any actions are
  performed, with error reporting down to the level of individual parameters.

- It should be portable.

  The library is written entirely in ANSI C, with hooks for certain OS-specific
  oddities such as Windows/OS/2 dynamic link libraries.  The only external
  routines used are ones from <string.h> and one call from <time.h>.

- It should be extendable.

  The library provides the capability to add encryption capabilities at runtime
  (currently only partially implemented, since the interface is somewhat messy
  - you need to pass in about 15 parameters to do it).

- It should be free of silly restrictions.

  This software is distributed as copyrighted freeware, with copyrights on
  individual encryption modules being held by the contributing authors.  You
  are free to use the code in any way you want, with the following
  restrictions:

    If you make any changes to the code, you should send a copy of the changes
    to the author or authors to allow them to integrate the changes into the
    code.  This is to allow a central consistent version to be maintained.

    If you use the library as part of a product, you should offer a copy to the
    author or authors of the library routines you are using.  This is to let
    the authors know that their work is being usefully applied.

    Any commercial software you create with this code may not be merely a set
    or subset of the encryption libraries, with or without minor added
    functionality.  This is to stop people adding their own wrappers and
    selling it as "their" encryption product.

  These terms are pretty much identical the the library GPL, which seem to be
  about the least restrictive usage terms around apart from outright public
  domain software.

- It should be buzzword-complete.

  The library is written in C, but is basically laid out as a C++ class
  library.  It can work as a static library, a shared library, or a dynamic
  link library.  It'll even work with things like Visual Basic.


Library Basics
--------------

Like the standard C file I/O libraries which work with FILE objects, this
library works with an "encryption context" of type CRYPT_INFO.  To encrypt
data, you create an encryption context, load a user key into it, en/decrypt
data, and destroy it when you've finished.  This concept lends itself to
implementation either as a C++ class or as C routines.  Throughout this
document all examples are given in C, translation to C++ is a simple step.

The overall library structure is as follows:

                                 Library core     Plug-in
                                                  modules
                               +---------------+-----------+
                               |               |  MDC/SHS  |
                               |               +-----------+
                               |               |    DES    |
                    /      \   |     Crypt     +-----------+
       User       /   ----   \ |               |   3DES    |
     Programs     \   ----   / |    Library    +-----------+
                    \      /   |               |   IDEA    |
                               |      API      +-----------+
                               |               |    RC4    |
                               |               +-----------+
                               |               |  others   |
                               +---------------+-----------+

The library API serves as an interface to a range of plug-in encryption modules
which allow encryption algorithms to be added in a fairly transparent manner.
The standardised API allows any of the algorithms and modes supported by the
library to be used with a minimum of coding effort.  As such the main function
of the library is to provide a standard, portable, easy-to-use interface
between the underlying encryption routines and the user software.


Portability
-----------

All data made accessible through the library API is in big-endian format.  The
library automatically takes care of endianness conversion to the form used by
the local system in a manner invisible to the end user.

All code is plain ANSI C, with no machine or OS-specific functions or calls
being used.


Creating/Destroying Encryption Contexts
---------------------------------------

When you create an encryption context, you must specify the encryption
algorithm and mode you want to use for that context.  The encryption algorithms
and modes are given in the crypt.h file, and are updated along with the library
itself.  For example, to create and destroy an encryption context for DES in
CBC mode, you would use the following code:

  CRYPT_INFO cryptInfo;

  initCryptContext( &cryptInfo, CRYPT_ALGO_DES, CRYPT_MODE_CBC );

  /* Perform en/decryption */

  endCryptContext( &cryptInfo );

The initCryptContext() and endCryptContext() functions take care of issues like
initialization, memory management, and erasure of data after use.


Loading Keys into Encryption Contexts
-------------------------------------

Once an encryption context has been created, you need to load a key into it.
This is done with the loadCryptContext() function.  For example to load the key
"Secret key" into the previously-created encryption context you would use:

  loadCryptContext( &cryptInfo, "Secret key", 10 );

Some hardware modules which enforce red/black seperation will not allow
plaintext keys to pass across the library interface.  In this case the key
parameter passed to loadCryptContext() will be a key selector or key encryption
key to be passed to the underlying hardware.  For example to pass a key
selector to a key stored inside a DES hardware module you would use:

  loadCryptContext( &cryptInfo, &keySelector, sizeof( keySelector ) );

You can also load an IV into the context, although for encryption you would
usually leave this to the library to perform automatically.  To load an IV you
would use:

  loadIV( &cryptInfo, iv, ivSize );

To retrieve an IV which has been generated by the library you would use:

  retrieveIV( &cryptInfo, iv );

The loadCryptContext(), loadIV(), and retrieveIV() functions take care of
issues like initialization, memory management, endianness conversion, and key
setup.

If you need to reserver space for keys and IV's, you can use the
CRYPT_MAX_KEYSIZE and CRYPT_MAX_IVSIZE defines from crypt.h to determine the
mount of memory you need.  No key or IV used by the library will ever need more
storage than the settings given in these defines.


Encrypting/Decrypting Data
--------------------------

Now that the encryption context and (if necessary) IV are set up, you're ready
to encrypt or decrypt data.  To encrypt or decrypt a block of data you use:

  encryptBuffer( &cryptInfo, buffer, length );

and:

  decryptBuffer( &cryptInfo, buffer, length );

If a block encryption mode is being used, these functions will recognise a call
with length == 0 as a courtesy call to indicate that this is the last data
block and will take whatever special action is necessary for this case.


Extended Initialization
-----------------------

The initCryptContext() has a companion function initCryptContextEx() which may
be used to perform an extended, algorithm-specific initialisation.  The second
parameter passed to the function is an algorithm-dependant structure used to
specify extra information to be used in the initalisation.  Not all algorithms
will support extended initialisation parameters.  If they are supported, the
structures have the name CRYPT_INFO_<algorithm_name>, and are laid out as
follows:

Structure CRYPT_INFO_MDCSHS:

  /* The number of iterations used during the key setup process triggered
     by a loadCryptContext() call.  Using a high value (500 or more) will
     greatly slow down password-guessing attacks by making the key setup
     process painfully slow.  Values in the range of 50-100 are recommended for
     most systems */
  int keySetupIterations;


Error Checking
--------------

Each function in the library performs extensive error checking (although this
has been left out in the following example for readability).  The file TEST.C,
included with the crypt library, includes better error handling than the short
examples below.

For functions which complete with no error, the library will return CRYPT_OK.
For functions which complete with some form of error internal to the library
itself, the library will return CRYPT_ERROR (this situation should never occur
and should be reported to the library authors).

If the encryption code or hardware fails an internal self-test, the library
will return CRYPT_SELFTEST if an attempt is made to use the encryption module
which failed the self-test.

If there is a problem with a parameter passed to a library function, the
library will return one of CRYPT_BADPARM1 ... CRYPT_BADPARM15 depending on the
parameter in error, or simply CRYPT_BADPARM if it cannot resolve the exact
parameter error type.

General resource and programming errors are flagged by the return values
CRYPT_NOMEM for a lack of memory, CRYPT_NOTINITED if you try to use an
encryption context hasn't been initialised yet or which has been destroyed,
CRYPT_INITED when you try to re-initialise an encryption context or re-load an
encryption key, CRYPT_NOALGO or CRYPT_NOMODE if the requested encryption
algorithm or mode is unavailable, CRYPT_NOKEY if the encryption key hasn't been
loaded into an encryption context, and CRYPT_NOIV if the IV hasn't been loaded
into an encryption context.

The macros isStatusError() and isStatusOK() can be used to determine whether a
return value denotes an error condition, for example:

  int status;

  status = initCryptContext( &cryptInfo, CRYPT_ALGO_IDEA, CRYPT_MODE_CFB );
  if( isStatusError( status ) )
      /* Perform error processing */


Examples
--------

The following examples have had error checking removed for readability.  See
the test code in TEST.C for an example with full error checking.

To encrypt a buffer using DES in CFB mode with the password 0x12345678ABCDEF:

  CRYPT_INFO cryptInfo;
  BYTE key[] = { 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF };

  /* Load the key, encrypt the buffer (the IV is automatically generated
     if we don't specify one, it can be obtained with retrieveIV()), and
     destroy the encryption context */
  initCryptContext( &cryptInfo, CRYPT_ALGO_DES, CRYPT_MODE_CFB );
  loadCryptContext( &cryptInfo, key, sizeof( key ) );
  encryptBuffer( &cryptInfo, buffer, length );
  destroyCryptContext( &cryptInfo );

To hash an arbitrary-size passphrase down to the one used by a particular
cryptosystem (in this case triple DES) using MDC/SHS:

  CRYPT_QUERY_INFO cryptQueryInfo;
  CRYPT_INFO cryptInfo;
  BYTE key[ 100 ];
  int keySize, ivSize;

  /* Find out how long we can make the key */
  queryAlgoModeInformation( CRYPT_ALGO_3DES, CRYPT_MODE_CBC,
                            &cryptQueryInfo );
  keySize = cryptQueryInfo->maxKeySize;   /* Use all we can */
  ivSize = cryptQueryInfo->ivSize;

  /* Encrypt a null data block using a null IV with the passphrase as the
     key.  This works vaguely like the Unix password encryption except
     that we don't have a salt (which would be the IV) */
  memset( key, 0, keySize );
  initCryptContext( &cryptInfo, CRYPT_ALGO_MDCSHS, CRYPT_MODE_CFB );
  loadCryptContext( &cryptInfo, passphrase, strlen( passphrase ) );
  loadIV( &cryptInfo, key, ivSize );
  encryptBuffer( &cryptInfo, key, keySize );
  destroyCryptContext( &cryptInfo );

To encrypt a file using triple DES in CBC mode with the key generated in the
previous example:

  BOOLEAN firstTime = TRUE;

  /* Load the previously-generated key */
  initCryptContext( &cryptInfo, CRYPT_ALGO_3DES, CRYPT_MODE_CBC );
  loadCryptContext( &cryptInfo, key, keySize );

  /* Copy the data across, encrypting as we go */
  while( ( length = fread( buffer, 1, BUFSIZE, inFile ) ) != 0 )
      {
      /* Encrypt the data in the buffer */
      encryptBuffer( &cryptInfo, buffer, length );

      /* If it's the first block, retrieve the IV and prepend it to the
         output data.  Note the since we've let the library generate the
         IV for us automatically, we can't retrieve it until after the first
         encryptBuffer() call */
      if( firstTime )
          {
          CRYPT_QUERY_INFO cryptQueryInfo;
          BYTE iv[ CRYPT_MAX_IVSIZE ];
          int ivSize;

          /* Find out how long the IV we're using is */
          queryContextInformation( &cryptInfo, &cryptQueryInfo );
          ivSize = cryptQueryInfo->ivSize;

          /* Retrieve the IV and write it to the output file */
          retrieveIV( &cryptInfo, iv );
          fwrite( iv, 1, ivSize, outFile );

          firstTime = FALSE;
          }

      /* Write the encrypted data to the output file */
      fwrite( buffer, 1, length, outFile );
      }

  /* Since CBC is a block cipher, we perform a courtesy close call to let
     the encryption routines handle the last block */
  encryptBuffer( &cryptInfo, buffer, 0 );

  destroyCryptContext( &cryptInfo );

To decrypt the previously encrypted file with the key generated in the previous
example:

  CRYPT_QUERY_INFO cryptQueryInfo;
  BYTE iv[ CRYPT_MAX_IVSIZE ];
  int ivSize;

  /* Load the previously-generated key */
  initCryptContext( &cryptInfo, CRYPT_ALGO_3DES, CRYPT_MODE_CBC );
  loadCryptContext( &cryptInfo, key, keySize );

  /* Find out how long the IV we're using is */
  queryContextInformation( &cryptInfo, &cryptQueryInfo );
  ivSize = cryptQueryInfo->ivSize;

   /* Read the IV from the input file and load it into the encryption
      context */
   fread( iv, 1, ivSize, inFile );
   loadIV( &cryptInfo, iv, ivSize );

  /* Copy the data across, decrypting as we go */
  while( ( length = fread( buffer, 1, BUFSIZE, inFile ) ) != 0 )
      {
      /* Encrypt the data in the buffer */
      decryptBuffer( &cryptInfo, buffer, length );

      /* Write the decrypted data to the output file */
      fwrite( buffer, 1, length, outFile );
      }

  /* Since CBC is a block cipher, we perform a courtesy close call to let
     the encryption routines handle the last block */
  decryptBuffer( &cryptInfo, buffer, 0 );

  destroyCryptContext( &cryptInfo );

A longer usage example including proper error checking and with various other
test routines can be found in the file test.c included with the code.


Querying the Encryption Library Capabilities
--------------------------------------------

The previous examples showed the use of the queryAlgoModeInformation() and
queryContextInformation() calls to retrieve information about the
characteristics of a particular algorithm as implemented by the encryption
library.  There are four such calls in total, of which two query the existence
of an implementation of an algorithm or algorithm/mode combination, and two
return information about the implementation.

The library can be interrogated about the existence of a particular encryption
algorithm or algorithm and encryption mode combination with:

  status = queryAlgoAvailability( algorithm );

or:

  status = queryModeAvailability( algorithm, mode );

In addition to requesting information on the availability of an encryption
algorithm and mode, you can request extra information to be returned in a
CRYPT_QUERY_INFO structure with:

  status = queryAlgoModeInformation( algorithm, mode, &cryptQueryInfo );

or

  status = queryContextInformation( &cryptInfo, &cryptQueryInfo );

with the former being used to request information on a given algorithm/mode
combination and the latter being used to request information about the
encryption context being used.

The CRYPT_QUERY_INFO structure contains the following fields:

  /* The encryption algorithm, encryption mode, and general algorithm name
     as an ASCII string */
  CRYPT_ALGO cryptAlgo;
  CRYPT_MODE cryptMode;
  char *algoName;

  /* The algorithm block size in bytes, the minimum, recommended, and
     maximum key size in bytes, and the minimum, recommended, and maximum
     IV size in bytes */
  int blockSize;
  int minKeySize;
  int keySize;
  int maxKeySize;
  int minIVsize;
  int ivSize;
  int maxIVsize;

  /* The algorithm speed relative to a block copy operation.  This value
     ranges from 1 ... CRYPT_MAX_SPEED, or CRYPT_ERROR if no speed rating is
     available */
  int speed;


Algorithm-Specific Notes
------------------------

MDCSHS:

  None

DES:

  loadCryptContext() will return a bad parameter code if the DES key parity is
  wrong or if the key is a weak key (CRYPT_BADPARM2 in this case).

  encryptBuffer() and decryptBuffer() will return a bad parameter code if the
  encryption mode is ECB, CBC, or PCBC and the encrypted data length is not a
  multiple of the block size (CRYPT_BADPARM3 in this case).

Triple DES:

  loadCryptContext() will return a bad parameter code if the DES key parity is
  wrong or if the key is a weak key (CRYPT_BADPARM2 in this case).

  encryptBuffer() and decryptBuffer() will return a bad parameter code if the
  encryption mode is ECB or CBC and the encrypted data length is not a multiple
  of the block size (CRYPT_BADPARM3 in this case).

IDEA:

  encryptBuffer() and decryptBuffer() will return a bad parameter code if the
  encryption mode is ECB or CBC and the encrypted data length is not a multiple
  of the block size (CRYPT_BADPARM3 in this case).

  The IDEA algorithm is patented by Ascom Systec AG, CH-5506 Maegenwil,
  Switzerland, ph. +41 64 56 59 45, email idea@ascom.ch, and cannot be used
  commercially without a license.  As of June 1995, the licensing terms were
  120 SFr (about US$90) per user for 1-10 users, 80 Sfr (about US$60) per user
  for 11-20 users, and 60 Sfr (about US$46) per user for 21-100 users.

RC4:

  None


Plug-In Module Implementation
-----------------------------

These notes are rather brief since there is a lot of ground to cover, and the
easiest way to see what is required is to look at the crypt.c code and the way
it interfaces to the lib_XXX.c code, which may be regarded as standard
implementations of a plug-in module (the triple DES and IDEA modules are
probably the best examples).  Basically, implementors must provide the
following services in their plug-in modules:

int selfTestFunction( void );

  Perform a self-test on the encryption hardware/software module.  This
  function is called once before any other function is called.

int initFunction( CRYPT_INFO *cryptInfo );

  Initialise the appropriate private CRYPT_INFO fields.

int initExFunction( CRYPT_INFO *cryptInfo, void *cryptInfoEx );

  Initialiase the appropriate private CRYPT_INFO fields based on the extended
  information given in the second parameter.

int endFunction( CRYPT_INFO *cryptInfo );

  Clear the appropriate private CRYPT_INFO fields.

int initKeyFunction( CRYPT_INFO *cryptInfo );

  Initialise the internal key based on the user key.  Typically this might
  perform one or more of the following:

    Hash the user key down to the size of the key used by the algorithm

    Perform some type of key schedule operation

    Perform endianness conversion for the current operating environment

    Load the key into external crypto hardware.

    Perform a key selection in external hardware based on the specified key
    selector.

  For algorithms with asymmetric internal keys, both an encryption and a
  decryption key will have to be generated, as a crypt context may be used for
  encryption or decryption.

int initIVFunction( CRYPT_INFO *cryptInfo );

  Initialise the IV.  Typically this might perform one or more of the
  following:

    Perform endianness conversion for the current operating environment

    Load the IV into external crypto hardware.

int encryptFunction( CRYPT_INFO *cryptInfo, void *buffer, int length );
int decryptFunction( CRYPT_INFO *cryptInfo, void *buffer, int length );

  Encrypt/decrypt a block of data based on the algorithm and keying information
  in CRYPT_INFO.  Some algorithms may require special handling for the last
  block (eg block truncation in CBC) so both routines should recognise a call
  with length = 0 to indicate the end of the encrypted data block.


Acknowledgements
----------------

The DES and 3DES encryption code was contributed by Eric Young
<eay@psych.psy.uq.oz.au> and is part of his libdes package.  The primary ftp
site for the full libdes is
ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-x.xx.tar.gz.  libdes is now also
shipped with SSLeay.  The primary site for this is
ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/SSLeay-x.xx.tar.gz.

The IDEA code was contributed by Colin Plumb.
