home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
crypl200.zip
/
LIB_MDC.C
< prev
next >
Wrap
Text File
|
1996-09-29
|
14KB
|
412 lines
/****************************************************************************
* *
* cryptlib MDC/SHS Encryption Routines *
* Copyright Peter Gutmann 1992-1996 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "crypt.h"
#ifdef INC_ALL
#include "sha.h"
#else
#include "hash/sha.h"
#endif /* Compiler-specific includes */
/* The default key setup iteration count. This gives roughly 0.5s delay on
a 10K dhrystone machine */
#define MDCSHS_DEFAULT_ITERATIONS 200
/* The size of an MDCSHS key. If we're running on a machine with a > 32
bit wordsize, we need to store the key as an array of words rather than
an array of bytes which is cast to an array of words */
#ifdef _BIG_WORDS
#define MDCSHS_KEY_WORDS ( SHA_DATASIZE / 4 )
#define MDCSHS_KEYSIZE ( MDCSHS_KEY_WORDS * sizeof( LONG ) )
#else
#define MDCSHS_KEYSIZE SHA_DATASIZE
#endif /* _BIG_WORDS */
/****************************************************************************
* *
* MDC/SHS Self-test Routines *
* *
****************************************************************************/
/* Test the SHA output against the test vectors given in FIPS 180 */
static LONG shaTestResults[][ SHA_DIGESTSIZE / 4 ] = {
{ 0x0164B8A9L, 0x14CD2A5EL, 0x74C4F7FFL, 0x082C4D97L, 0xF1EDF880L },
{ 0xD2516EE1L, 0xACFA5BAFL, 0x33DFC1C4L, 0x71E43844L, 0x9EF134C8L },
{ 0x3232AFFAL, 0x48628A26L, 0x653B5AAAL, 0x44541FD9L, 0x0D690603L }
};
static int compareSHSresults( SHA_INFO *shaInfo, int shaTestLevel )
{
int i;
/* Compare the returned digest and required values */
for( i = 0; i < SHA_DIGESTSIZE / 4; i++ )
if( shaInfo->digest[ i ] != shaTestResults[ shaTestLevel ][ i ] )
return( CRYPT_SELFTEST );
return( CRYPT_OK );
}
int mdcshsSelfTest( void )
{
SHA_INFO shaInfo;
/* Test SHA against values given in FIPS 180 */
shaInitial( &shaInfo );
shaUpdate( &shaInfo, ( BYTE * ) "abc", 3 );
shaFinal( &shaInfo );
if( compareSHSresults( &shaInfo, 0 ) != CRYPT_OK )
return( CRYPT_SELFTEST );
shaInitial( &shaInfo );
shaUpdate( &shaInfo, ( BYTE * ) "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56 );
shaFinal( &shaInfo );
if( compareSHSresults( &shaInfo, 1 ) != CRYPT_OK )
return( CRYPT_SELFTEST );
#if 0
/* We skip the third test since this takes several seconds to execute,
which leads to an unacceptable delay in the library startup time */
shaInitial( &shaInfo );
for( i = 0; i < 15625; i++ )
shaUpdate( &shaInfo, ( BYTE * ) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 64 );
shaFinal( &shaInfo );
if( compareSHSresults( &shaInfo, 2 ) != CRYPT_OK )
return( CRYPT_SELFTEST );
#endif /* 0 */
return( CRYPT_OK );
}
/****************************************************************************
* *
* Init/Shutdown Routines *
* *
****************************************************************************/
/* Perform auxiliary init and shutdown actions on an encryption context */
int mdcshsInitEx( CRYPT_INFO *cryptInfo, void *cryptInfoEx )
{
CRYPT_INFO_MDCSHS *cryptInfoExPtr = ( CRYPT_INFO_MDCSHS * ) cryptInfoEx;
int status;
/* Allocate memory for the key and the algorithm-specific data within
the crypt context and set up any pointers we need */
if( cryptInfo->key != NULL || cryptInfo->privateData != NULL )
return( CRYPT_INITED );
if( ( status = secureMalloc( &cryptInfo->key, MDCSHS_KEYSIZE ) ) != CRYPT_OK )
return( status );
if( cryptInfoExPtr->keySetupIterations == CRYPT_USE_DEFAULT )
cryptInfo->privateUseDefaults = TRUE;
if( ( status = secureMalloc( &cryptInfo->privateData, sizeof( CRYPT_INFO_MDCSHS ) ) ) != CRYPT_OK )
{
secureFree( &cryptInfo->key );
return( status );
}
if( cryptInfoExPtr->keySetupIterations == CRYPT_USE_DEFAULT )
setMDCSHSinfo( cryptInfo, MDCSHS_DEFAULT_ITERATIONS );
else
setMDCSHSinfo( cryptInfo, cryptInfoExPtr->keySetupIterations );
cryptInfo->keyLength = MDCSHS_KEYSIZE;
return( CRYPT_OK );
}
int mdcshsInit( CRYPT_INFO *cryptInfo )
{
CRYPT_INFO_MDCSHS cryptInfoEx;
/* Use the default number of setup iterations */
memset( &cryptInfoEx, 0, sizeof( CRYPT_INFO_MDCSHS ) );
cryptInfoEx.keySetupIterations = CRYPT_USE_DEFAULT;
/* Pass through to the extended setup routine */
return( mdcshsInitEx( cryptInfo, &cryptInfoEx ) );
}
int mdcshsEnd( CRYPT_INFO *cryptInfo )
{
/* Free any allocated memory */
secureFree( &cryptInfo->key );
secureFree( &cryptInfo->privateData );
return( CRYPT_OK );
}
/****************************************************************************
* *
* MDC/SHS En/Decryption Routines *
* *
****************************************************************************/
/* The SHS transformation. What we're doing here isn't totally correct since
we're forcing the IV to big-endian, transforming it, and returning it to
the local endianness, rather than changing the data to the local
endianness and back as we should. However doing it correctly isn't really
possible since we can only endianness-swap 4 bytes at a time which
precludes swapping the data. The use of the temp variable when the IV is
copied back in the _BIG_WORDS case is to eliminate multiple memory
accesses when the four bytes are extracted from ivLong[ n ], since many
compilers won't risk optimising the loads due to possible aliasing
problems. We declare the necessary ivLong array as part of the actual
encryption function to save it having to be instantiated for every use
of shsTransform() */
#ifdef _BIG_WORDS
#define shaTransform(cI) { \
LONG temp; \
BYTE *ivPtr; \
ivPtr = ( BYTE * ) cI->currentIV; \
for( i = 0; i < SHA_DIGESTSIZE / 4; i++ ) \
{ \
ivLong[ i ] = mgetBLong( ivPtr ); \
} \
SHATransform( ivLong, cI->key ); \
ivPtr = ( BYTE * ) cI->currentIV; \
for( i = 0; i < SHA_DIGESTSIZE / 4; i++ ) \
{ \
temp = ivLong[ i ]; \
mputBLong( ivPtr, temp ); \
} \
}
#else
#define shaTransform(cI) bigToLittleLong( ( LONG * ) cI->currentIV, SHA_DIGESTSIZE ); \
SHATransform( ( LONG * ) cI->currentIV, ( LONG * ) cI->key ); \
bigToLittleLong( ( LONG * ) cI->currentIV, SHA_DIGESTSIZE );
#endif /* _BIG_WORDS */
void SHATransform( LONG *digest, LONG *data );
/* Encrypt data in CFB mode */
int mdcshsEncrypt( CRYPT_INFO *cryptInfo, BYTE *buffer, int noBytes )
{
int i, ivCount = cryptInfo->ivCount;
#ifdef _BIG_WORDS
LONG ivLong[ SHA_DIGESTSIZE / 4 ];
#endif /* _BIG_WORDS */
/* If there's any encrypted material left in the IV, use it now */
if( ivCount )
{
int bytesToUse;
/* Find out how much material left in the encrypted IV we can use */
bytesToUse = SHA_DIGESTSIZE - ivCount;
if( noBytes < bytesToUse )
bytesToUse = noBytes;
/* Encrypt the data */
for( i = 0; i < bytesToUse; i++ )
buffer[ i ] ^= cryptInfo->currentIV[ i + ivCount ];
memcpy( cryptInfo->currentIV + ivCount, buffer, bytesToUse );
/* Adjust the byte count and buffer position */
noBytes -= bytesToUse;
buffer += bytesToUse;
ivCount += bytesToUse;
}
while( noBytes )
{
ivCount = ( noBytes > SHA_DIGESTSIZE ) ? SHA_DIGESTSIZE : noBytes;
/* Encrypt the IV */
shaTransform( cryptInfo );
/* XOR the buffer contents with the encrypted IV */
for( i = 0; i < ivCount; i++ )
buffer[ i ] ^= cryptInfo->currentIV[ i ];
/* Shift the ciphertext into the IV */
memcpy( cryptInfo->currentIV, buffer, ivCount );
/* Move on to next block of data */
noBytes -= ivCount;
buffer += ivCount;
}
/* Remember how much of the IV is still available for use */
cryptInfo->ivCount = ( ivCount % SHA_DIGESTSIZE );
return( CRYPT_OK );
}
/* Decrypt data in CFB mode. Note that the transformation can be made
faster (but less clear) with temp = buffer, buffer ^= iv, iv = temp
all in one loop */
int mdcshsDecrypt( CRYPT_INFO *cryptInfo, BYTE *buffer, int noBytes )
{
BYTE temp[ SHA_DIGESTSIZE ];
int i, ivCount = cryptInfo->ivCount;
#ifdef _BIG_WORDS
LONG ivLong[ SHA_DIGESTSIZE / 4 ];
#endif /* _BIG_WORDS */
/* If there's any encrypted material left in the IV, use it now */
if( ivCount )
{
int bytesToUse;
/* Find out how much material left in the encrypted IV we can use */
bytesToUse = SHA_DIGESTSIZE - ivCount;
if( noBytes < bytesToUse )
bytesToUse = noBytes;
/* Decrypt the data */
memcpy( temp, buffer, bytesToUse );
for( i = 0; i < bytesToUse; i++ )
buffer[ i ] ^= cryptInfo->currentIV[ i + ivCount ];
memcpy( cryptInfo->currentIV + ivCount, temp, bytesToUse );
/* Adjust the byte count and buffer position */
noBytes -= bytesToUse;
buffer += bytesToUse;
ivCount += bytesToUse;
}
while( noBytes )
{
ivCount = ( noBytes > SHA_DIGESTSIZE ) ? SHA_DIGESTSIZE : noBytes;
/* Encrypt the IV */
shaTransform( cryptInfo );
/* Save the ciphertext */
memcpy( temp, buffer, ivCount );
/* XOR the buffer contents with the encrypted IV */
for( i = 0; i < ivCount; i++ )
buffer[ i ] ^= cryptInfo->currentIV[ i ];
/* Shift the ciphertext into the IV */
memcpy( cryptInfo->currentIV, temp, ivCount );
/* Move on to next block of data */
noBytes -= ivCount;
buffer += ivCount;
}
/* Remember how much of the IV is still available for use */
cryptInfo->ivCount = ( ivCount % SHA_DIGESTSIZE );
/* Clear the temporary buffer */
zeroise( temp, SHA_DIGESTSIZE );
return( CRYPT_OK );
}
/****************************************************************************
* *
* MDC/SHS Key Management Routines *
* *
****************************************************************************/
/* Get/set algorithm-specific parameters */
int getMDCSHSinfo( const CRYPT_INFO *cryptInfo )
{
return( ( ( CRYPT_INFO_MDCSHS * ) cryptInfo->privateData )->keySetupIterations );
}
void setMDCSHSinfo( CRYPT_INFO *cryptInfo, const int keySetupIterations )
{
( ( CRYPT_INFO_MDCSHS * ) cryptInfo->privateData )->keySetupIterations = keySetupIterations;
}
/* The size of the key buffer. Note that increasing this value will result
in a significant increase in the setup time, so it will be necessary to
make a corresponding decrease in the iteration count */
#define KEYBUFFER_SIZE 256
/* Initialise an MDC/SHS key. This is done by repeatedly encrypting the
user key as follows:
IV <- 0
key <- 0
repeat
encrypt userkey with key
key <- userkey
The IV is updated transparently as part of the encryption process */
int mdcshsInitKey( CRYPT_INFO *cryptInfo )
{
BYTE keyData[ KEYBUFFER_SIZE ];
int keySetupIterations = ( ( CRYPT_INFO_MDCSHS * ) cryptInfo->privateData )->keySetupIterations;
int count;
/* Copy the key information into the key data buffer. We silently
truncate the key length to the size of the buffer, but this usually
is some hundreds of bytes so it shouldn't be a problem.
We also correct the endianness of the keyData at this point. From
here on all data is in the local endianness format so there is no need
for further corrections */
memset( keyData, 0, KEYBUFFER_SIZE );
keyData[ 0 ] = ( BYTE ) ( cryptInfo->userKeyLength >> 8 );
keyData[ 1 ] = ( BYTE ) cryptInfo->userKeyLength;
cryptInfo->userKeyLength %= KEYBUFFER_SIZE - 2;
memcpy( keyData + 2, cryptInfo->userKey, cryptInfo->userKeyLength );
/* Set the initial key and IV to null */
memset( cryptInfo->currentIV, 0, MAX_IVSIZE );
memset( cryptInfo->key, 0, MDCSHS_KEYSIZE );
/* Convert the endianness of the input data from the canonical form to
the local form */
bigToLittleLong( ( LONG * ) keyData, KEYBUFFER_SIZE );
/* "Encrypt" the keyData with the given IV and then set the key to
the encrypted keyData. The act of encryption also sets the IV.
This is particularly important in the case of SHA, since although only
the first SHA_DATASIZE bytes are copied into the key, the IV is
still affected by the entire buffer */
for( count = 0; count < keySetupIterations; count++ )
{
mdcshsEncrypt( cryptInfo, keyData, KEYBUFFER_SIZE );
copyToLong( cryptInfo->key, keyData, SHA_DATASIZE );
}
/* Perform one last copy in case they've specified zero iterations and
the loop was never executed. For MDCSHS, the transformed key is the
same as the raw encryption key, so we just copy it over */
copyToLong( cryptInfo->key, keyData, SHA_DATASIZE );
#if 0
/* Set the key check byte to the last WORD of the encrypted keyData.
This is never used for anything (it's 191 bytes past the end of the
last value used to set the key) so we're not revealing much to an
attacker */
bigToLittleLong( ( LONG * ) ( keyData + KEYBUFFER_SIZE - sizeof( LONG ) ), sizeof( LONG ) );
cryptInfo->keyCheck = ( ( WORD ) keyData[ KEYBUFFER_SIZE - 2 ] << 8 ) | \
keyData[ KEYBUFFER_SIZE - 1 ];
#endif /* 0 */
/* Wipe the keyData */
zeroise( keyData, KEYBUFFER_SIZE );
return( CRYPT_OK );
}
/* Initialise the IV */
int mdcshsInitIV( CRYPT_INFO *cryptInfo )
{
/* Convert the working IV from the canonical to the internal form */
bigToLittleLong( ( LONG * ) cryptInfo->currentIV, MAX_IVSIZE );
return( CRYPT_OK );
}