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 SAFER ECB, CBC, CFB, OFB SAFER-SK ECB, CBC, CFB, OFB Notes: 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 configure 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 and RC4, 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? 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 and one call from . - 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 | Triple DES | User / ---- \ | +------------+ Programs \ ---- / | Library | IDEA | \ / | +------------+ | API | RC4 | | +------------+ | | Safer | | +------------+ | | 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_, 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; Structure CRYPT_INFO_SAFER: /* The number of rounds of encryption. The default settings are 6 rounds for SAFER-K64, 8 rounds for SAFER-SK64, and 10 rounds for SAFER-K128 and SAFER-SK128 */ int rounds; /* Whether to use SAFER or SAFER-SK. The default is to use the original algorithm SAFER. SAFER-SK is a version which improves the key schedule to eliminate some possible weaknesses when SAFER is used as a keyed hash function */ BOOLEAN useSaferSK; Error Checking -------------- When the library is initialised, each of the built-in encryption algorithms goes through a self-test procedure which checks the implementation using standard test vectors and methods given with the algorithm specification (typically FIPS publications, ANSI standards, or standard reference implementations). This self-test is used to verify that each encryption algorithm is performing as required. Each function in the library performs extensive error checking (although monitoring of error codes has been left out in the following code samples 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 SAFER: SAFER-SK: 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). 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 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. The SAFER code was written by Richard De Moliner of the Signal and Information Processing Laboratory, Swiss Federal Institute of Technology.