/**************************************************************************** * * * cryptlib Enveloping Test Routines * * Copyright Peter Gutmann 1996-2005 * * * ****************************************************************************/ #include /* To determine max.buffer size we can encrypt */ #include "cryptlib.h" #include "test/test.h" #if defined( __MVS__ ) || defined( __VMCMS__ ) /* Suspend conversion of literals to ASCII. */ #pragma convlit( suspend ) #endif /* IBM big iron */ #if defined( __ILEC400__ ) #pragma convert( 0 ) #endif /* IBM medium iron */ /* Test data to use for the self-test. The PGP test data is slightly different since it's not possible to include a null character in data generated via the command-line versions of PGP. On EBCDIC systems we have to hardcode in the character codes since the pre-generated data came from an ASCII system */ #if defined( __MVS__ ) || defined( __VMCMS__ ) #define ENVELOPE_TESTDATA ( ( BYTE * ) "\x53\x6F\x6D\x65\x20\x74\x65\x73\x74\x20\x64\x61\x74\x61" ) #define ENVELOPE_PGP_TESTDATA ( ( BYTE * ) "\x53\x6F\x6D\x65\x20\x74\x65\x73\x74\x20\x64\x61\x74\x61\x2E" ) #define ENVELOPE_COMPRESSEDDATA "\x2F\x2A\x20\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x6C\x6F\x77\x65\x73\x74\x2D" #else #define ENVELOPE_TESTDATA ( ( BYTE * ) "Some test data" ) #define ENVELOPE_PGP_TESTDATA ( ( BYTE * ) "Some test data." ) #define ENVELOPE_COMPRESSEDDATA "/* This is a lowest-" #endif /* EBCDIC systems */ #define ENVELOPE_TESTDATA_SIZE 15 #define ENVELOPE_COMPRESSEDDATA_SIZE 20 /* To convert a CMS blob into an S/MIME message, base64 encode it and add the following header: To:
Subject: S/MIME test From:
MIME-Version: 1.0 Content-Type: application/x-pkcs7-mime;smime-type=signed-data;name="smime.p7m" Content-Disposition: attachment;filename="smime.p7m" Content-Transfer-Encoding: base64 To allow the inner message to be processed by a mailer, the contents will themselves have to be MIME-formatted: MIME-Version: 1.0 Content-Type: text/plain;charset="us-ascii" Content-Transfer-Encoding: 7bit */ /* External flags which indicate that the key read/update routines worked OK. This is set by earlier self-test code, if it isn't set some of the tests are disabled */ extern int keyReadOK, doubleCertOK; #if defined( TEST_ENVELOPE ) || defined( TEST_SESSION ) /* For TSP enveloping */ /**************************************************************************** * * * Utility Routines * * * ****************************************************************************/ /* The general-purpose buffer used for enveloping. We use a fixed buffer if possible to save having to add huge amounts of allocation/deallocation code */ BYTE FAR_BSS globalBuffer[ BUFFER_SIZE ]; /* Determine the size of a file. If there's a problem, we return the default buffer size, which will cause a failure further up the chain where the error can be reported better */ static int getFileSize( const char *fileName ) { FILE *filePtr; long size; if( ( filePtr = fopen( fileName, "rb" ) ) == NULL ) return( BUFFER_SIZE ); fseek( filePtr, 0L, SEEK_END ); size = ftell( filePtr ); fclose( filePtr ); if( size > INT_MAX ) return( BUFFER_SIZE ); return( ( int ) size ); } /* Read test data from a file */ static int readFileData( const char *fileName, const char *description, BYTE *buffer, const int bufSize ) { FILE *filePtr; int count; if( ( filePtr = fopen( fileName, "rb" ) ) == NULL ) { printf( "Couldn't find %s file, skipping test of data import...\n", description ); return( 0 ); } printf( "Testing %s import...\n", description ); count = fread( buffer, 1, bufSize, filePtr ); fclose( filePtr ); if( count == bufSize ) { puts( "The data buffer size is too small for the data. To fix this, " "either increase\nthe BUFFER_SIZE value in " __FILE__ " and " "recompile the code, or use the\ntest code with dynamically-" "allocated buffers." ); return( 0 ); /* Skip this test and continue */ } if( count < 16 ) { printf( "Read failed, only read %d bytes.\n", count ); return( 0 ); /* Skip this test and continue */ } printf( "%s has size %d bytes.\n", description, count ); return( count ); } /* Common routines to create an envelope, add enveloping information, push data, pop data, and destroy an envelope */ static int createEnvelope( CRYPT_ENVELOPE *envelope, const CRYPT_FORMAT_TYPE formatType ) { int status; /* Create the envelope */ status = cryptCreateEnvelope( envelope, CRYPT_UNUSED, formatType ); if( cryptStatusError( status ) ) { printf( "cryptCreateEnvelope() failed with error code %d, line %d.\n", status, __LINE__ ); return( FALSE ); } return( TRUE ); } static int createDeenvelope( CRYPT_ENVELOPE *envelope ) { int status; /* Create the envelope */ status = cryptCreateEnvelope( envelope, CRYPT_UNUSED, CRYPT_FORMAT_AUTO ); if( cryptStatusError( status ) ) { printf( "cryptCreateDeevelope() failed with error code %d, line %d.\n", status, __LINE__ ); return( FALSE ); } return( TRUE ); } static int addEnvInfoString( const CRYPT_ENVELOPE envelope, const CRYPT_ATTRIBUTE_TYPE type, const void *envInfo, const int envInfoLen ) { int status; status = cryptSetAttributeString( envelope, type, envInfo, envInfoLen ); if( cryptStatusError( status ) ) { printf( "cryptSetAttributeString() failed with error code %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } return( TRUE ); } static int addEnvInfoNumeric( const CRYPT_ENVELOPE envelope, const CRYPT_ATTRIBUTE_TYPE type, const int envInfo ) { int status; status = cryptSetAttribute( envelope, type, envInfo ); if( cryptStatusError( status ) ) { printf( "cryptSetAttribute() of %d failed with error code %d, " "line %d.\n", type, status, __LINE__ ); return( FALSE ); } return( TRUE ); } static int pushData( const CRYPT_ENVELOPE envelope, const BYTE *buffer, const int length, const void *stringEnvInfo, const int numericEnvInfo ) { BOOLEAN isRestartable = FALSE; int bytesIn, contentType, status; /* Push in the data */ status = cryptPushData( envelope, buffer, length, &bytesIn ); if( status == CRYPT_ENVELOPE_RESOURCE ) { int cryptEnvInfo; /* Add the appropriate enveloping information we need to continue */ status = cryptSetAttribute( envelope, CRYPT_ATTRIBUTE_CURRENT_GROUP, CRYPT_CURSOR_FIRST ); if( cryptStatusError( status ) ) { printf( "Attempt to move cursor to start of list failed with " "error code %d, line %d.\n", status, __LINE__ ); return( status ); } do { C_CHR label[ CRYPT_MAX_TEXTSIZE + 1 ]; int labelLength; status = cryptGetAttribute( envelope, CRYPT_ATTRIBUTE_CURRENT, &cryptEnvInfo ); if( cryptStatusError( status ) ) { printf( "Attempt to read current group failed with error " "code %d, line %d.\n", status, __LINE__ ); return( status ); } switch( cryptEnvInfo ) { case CRYPT_ATTRIBUTE_NONE: /* The required information was supplied via other means (in practice this means there's a crypto device available and that was used for the decrypt), there's nothing left to do */ puts( "(Decryption key was recovered using crypto device " "or non-password-protected\n private key)." ); break; case CRYPT_ENVINFO_PRIVATEKEY: /* If there's no decryption password present, the private key must be passed in directly */ if( stringEnvInfo == NULL ) { status = cryptSetAttribute( envelope, CRYPT_ENVINFO_PRIVATEKEY, numericEnvInfo ); if( cryptStatusError( status ) ) { printf( "Attempt to add private key failed with " "error code %d, line %d.\n", status, __LINE__ ); return( status ); } isRestartable = TRUE; break; } /* A private-key keyset is present in the envelope, we need a password to decrypt the key */ status = cryptGetAttributeString( envelope, CRYPT_ENVINFO_PRIVATEKEY_LABEL, label, &labelLength ); if( cryptStatusError( status ) ) { printf( "Private key label read failed with error " "code %d, line %d.\n", status, __LINE__ ); return( status ); } #ifdef UNICODE_STRINGS label[ labelLength / sizeof( wchar_t ) ] = '\0'; printf( "Need password to decrypt private key '%S'.\n", label ); #else label[ labelLength ] = '\0'; printf( "Need password to decrypt private key '%s'.\n", label ); #endif /* UNICODE_STRINGS */ if( !addEnvInfoString( envelope, CRYPT_ENVINFO_PASSWORD, stringEnvInfo, paramStrlen( stringEnvInfo ) ) ) return( SENTINEL ); isRestartable = TRUE; break; case CRYPT_ENVINFO_PASSWORD: puts( "Need user password." ); if( !addEnvInfoString( envelope, CRYPT_ENVINFO_PASSWORD, stringEnvInfo, paramStrlen( stringEnvInfo ) ) ) return( SENTINEL ); isRestartable = TRUE; break; case CRYPT_ENVINFO_SESSIONKEY: puts( "Need session key." ); if( !addEnvInfoNumeric( envelope, CRYPT_ENVINFO_SESSIONKEY, numericEnvInfo ) ) return( SENTINEL ); isRestartable = TRUE; break; case CRYPT_ENVINFO_KEY: puts( "Need conventional encryption key." ); if( !addEnvInfoNumeric( envelope, CRYPT_ENVINFO_KEY, numericEnvInfo ) ) return( SENTINEL ); isRestartable = TRUE; break; case CRYPT_ENVINFO_SIGNATURE: /* If we've processed the entire data block in one go, we may end up with only signature information available, in which case we defer processing them until after we've finished with the deenveloped data */ break; default: printf( "Need unknown enveloping information type %d.\n", cryptEnvInfo ); return( SENTINEL ); } } while( cryptSetAttribute( envelope, CRYPT_ATTRIBUTE_CURRENT_GROUP, CRYPT_CURSOR_NEXT ) == CRYPT_OK ); /* If we're using some form of encrypted enveloping, report the algorithm and keysize used */ if( cryptEnvInfo == CRYPT_ATTRIBUTE_NONE || \ cryptEnvInfo == CRYPT_ENVINFO_PRIVATEKEY || \ cryptEnvInfo == CRYPT_ENVINFO_PASSWORD ) { int cryptAlgo, keySize; status = cryptGetAttribute( envelope, CRYPT_CTXINFO_ALGO, &cryptAlgo ); if( cryptStatusOK( status ) ) status = cryptGetAttribute( envelope, CRYPT_CTXINFO_KEYSIZE, &keySize ); if( cryptStatusError( status ) ) { printf( "Couldn't query encryption algorithm and keysize " "used in envelope, status %d, line %d.\n", status, __LINE__ ); return( status ); } printf( "Data is protected using algorithm %d with %d bit key.\n", cryptAlgo, keySize * 8 ); } /* If we only got some of the data in due to the envelope stopping to ask us for a decryption resource, push in the rest */ if( bytesIn < length && isRestartable ) { const int initialBytesIn = bytesIn; status = cryptPushData( envelope, buffer + initialBytesIn, length - initialBytesIn, &bytesIn ); if( cryptStatusError( status ) ) { printf( "cryptPushData() for remaining data failed with " "error code %d, line %d.\n", status, __LINE__ ); return( status ); } bytesIn += initialBytesIn; } } else if( cryptStatusError( status ) ) { printf( "cryptPushData() failed with error code %d, line %d.\n", status, __LINE__ ); printErrorAttributeInfo( envelope ); return( status ); } if( bytesIn != length ) { printf( "cryptPushData() only copied %d of %d bytes, line %d.\n", bytesIn, length, __LINE__ ); return( SENTINEL ); } status = cryptGetAttribute( envelope, CRYPT_ENVINFO_CONTENTTYPE, &contentType ); if( cryptStatusOK( status ) && contentType != CRYPT_CONTENT_DATA ) printf( "Nested content type = %d.\n", contentType ); /* Flush the data */ status = cryptFlushData( envelope ); if( cryptStatusError( status ) && status != CRYPT_ERROR_COMPLETE ) { printf( "cryptFlushData() failed with error code %d, line %d.\n", status, __LINE__ ); printErrorAttributeInfo( envelope ); return( status ); } return( bytesIn ); } static int popData( CRYPT_ENVELOPE envelope, BYTE *buffer, int bufferSize ) { int status, bytesOut; status = cryptPopData( envelope, buffer, bufferSize, &bytesOut ); if( cryptStatusError( status ) ) { printf( "cryptPopData() failed with error code %d, line %d.\n", status, __LINE__ ); printErrorAttributeInfo( envelope ); return( status ); } return( bytesOut ); } static int destroyEnvelope( CRYPT_ENVELOPE envelope ) { int status; /* Destroy the envelope */ status = cryptDestroyEnvelope( envelope ); if( cryptStatusError( status ) ) { printf( "cryptDestroyEnvelope() failed with error code %d, line %d.\n", status, __LINE__ ); return( FALSE ); } return( TRUE ); } /**************************************************************************** * * * Enveloping Test Routines * * * ****************************************************************************/ /* Test raw data enveloping */ static int envelopeData( const char *dumpFileName, const BOOLEAN useDatasize, const int bufferSize, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; BYTE *inBufPtr, *outBufPtr = globalBuffer; int length, bufSize, count; switch( bufferSize ) { case 0: printf( "Testing %splain data enveloping%s...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "", ( useDatasize && ( formatType != CRYPT_FORMAT_PGP ) ) ? \ " with datasize hint" : "" ); length = ENVELOPE_TESTDATA_SIZE; inBufPtr = ENVELOPE_TESTDATA; break; case 1: printf( "Testing %splain data enveloping of intermediate-size data...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "" ); length = 512; inBufPtr = globalBuffer; for( count = 0; count < length; count++ ) inBufPtr[ count ] = count & 0xFF; break; case 2: printf( "Testing %senveloping of large data quantity...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "" ); /* Allocate a large buffer and fill it with a known value */ length = ( INT_MAX <= 32768L ) ? 16384 : 1048576; if( ( inBufPtr = malloc( length + 128 ) ) == NULL ) { printf( "Couldn't allocate buffer of %d bytes, skipping large " "buffer enveloping test.\n", length ); return( TRUE ); } outBufPtr = inBufPtr; for( count = 0; count < length; count++ ) inBufPtr[ count ] = count & 0xFF; break; default: return( FALSE ); } bufSize = length + 128; /* Create the envelope, push in the data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) ) return( FALSE ); if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, length ); if( bufferSize > 1 ) cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, length + 1024 ); count = pushData( cryptEnvelope, inBufPtr, length, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, outBufPtr, bufSize ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); if( bufferSize == 0 && \ count != length + ( ( formatType == CRYPT_FORMAT_PGP ) ? 8 : \ useDatasize ? 17 : 25 ) ) { printf( "Enveloped data length %d, should be %d, line %d.\n", count, length + 25, __LINE__ ); return( FALSE ); } /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); if( bufferSize < 2 ) debugDump( dumpFileName, outBufPtr, count ); /* Create the envelope, push in the data, pop the de-enveloped result, and destroy the envelope */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); if( bufferSize > 1 ) cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, length + 1024 ); count = pushData( cryptEnvelope, outBufPtr, count, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, outBufPtr, bufSize ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Make sure that the result matches what we pushed */ if( count != length ) { puts( "De-enveloped data length != original length." ); return( FALSE ); } if( bufferSize > 0 ) { int i; for( i = 0; i < length; i++ ) if( outBufPtr[ i ] != ( i & 0xFF ) ) { printf( "De-enveloped data != original data at byte %d, " "line %d.\n", i, __LINE__ ); return( FALSE ); } } else if( memcmp( outBufPtr, ENVELOPE_TESTDATA, length ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ if( bufferSize > 1 ) free( inBufPtr ); puts( "Enveloping of plain data succeeded.\n" ); return( TRUE ); } int testEnvelopeData( void ) { if( !envelopeData( "env_datn", FALSE, 0, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite-length */ if( !envelopeData( "env_dat", TRUE, 0, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize */ if( !envelopeData( "env_dat.pgp", TRUE, 0, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format */ return( envelopeData( "env_datl.pgp", TRUE, 1, CRYPT_FORMAT_PGP ) ); } /* PGP format, longer data */ int testEnvelopeDataLargeBuffer( void ) { if( !envelopeData( NULL, TRUE, 2, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, large buffer */ return( envelopeData( NULL, TRUE, 2, CRYPT_FORMAT_PGP ) ); } /* Large buffer, PGP format */ /* Test compressed enveloping */ static int envelopeDecompress( BYTE *buffer, const int bufSize, const int length ) { CRYPT_ENVELOPE cryptEnvelope; BYTE smallBuffer[ 128 ]; int count, zeroCount; /* Create the envelope, push in the data, and pop the de-enveloped result */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); count = pushData( cryptEnvelope, buffer, length, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, buffer, bufSize ); if( cryptStatusError( count ) ) { #ifdef __hpux if( count == -1 ) puts( "Older HPUX compilers break zlib, to remedy this you can " "either get a better\ncompiler/OS or grab a debugger and " "try to figure out what HPUX is doing to\nzlib. To " "continue the self-tests, comment out the call to\n" "testEnvelopeCompress() and rebuild." ); #endif /* __hpux */ return( FALSE ); } /* See what happens when we try and pop out more data. This test is done because some compressed-data formats don't indicate the end of the data properly, and we need to make sure that the de-enveloping code handles this correctly */ zeroCount = popData( cryptEnvelope, smallBuffer, 128 ); if( zeroCount != 0 ) { puts( "Attempt to pop more data after end-of-data had been reached " "succeeded, the\nenvelope should have reported 0 bytes " "available." ); return( FALSE ); } /* Clean up */ if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); return( count ); } static int envelopeCompress( const char *dumpFileName, const BOOLEAN useDatasize, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; FILE *inFile; BYTE *buffer, *envelopedBuffer; int dataCount = 0, count, status; printf( "Testing %scompressed data enveloping%s...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "", useDatasize ? " with datasize hint" : "" ); /* Since this needs a nontrivial amount of data for the compression, we read it from an external file into dynamically-allocated buffers */ if( ( ( buffer = malloc( FILEBUFFER_SIZE ) ) == NULL ) || \ ( ( envelopedBuffer = malloc( FILEBUFFER_SIZE ) ) == NULL ) ) { if( buffer != NULL ) free( buffer ); puts( "Couldn't allocate test buffers." ); return( FALSE ); } inFile = fopen( convertFileName( COMPRESS_FILE ), "rb" ); if( inFile != NULL ) { dataCount = fread( buffer, 1, FILEBUFFER_SIZE, inFile ); fclose( inFile ); assert( dataCount < FILEBUFFER_SIZE ); } if( dataCount < 1000 || dataCount == FILEBUFFER_SIZE ) { free( buffer ); free( envelopedBuffer ); puts( "Couldn't read test file for compression." ); return( FALSE ); } /* Create the envelope, push in the data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) ) return( FALSE ); status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_COMPRESSION, CRYPT_UNUSED ); if( cryptStatusError( status ) ) { printf( "Attempt to enable compression failed, status = %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, dataCount ); count = pushData( cryptEnvelope, buffer, dataCount, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, envelopedBuffer, FILEBUFFER_SIZE ); if( count > dataCount - 1000 ) { printf( "Compression of data failed, %d bytes in -> %d bytes out, " "line %d.\n", dataCount, count, __LINE__ ); return( FALSE ); } if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( dumpFileName, envelopedBuffer, count ); /* De-envelope the data and make sure that the result matches what we pushed */ count = envelopeDecompress( envelopedBuffer, FILEBUFFER_SIZE, count ); if( count <= 0 ) return( FALSE ); if( count != dataCount || memcmp( buffer, envelopedBuffer, dataCount ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ free( buffer ); free( envelopedBuffer ); puts( "Enveloping of compressed data succeeded.\n" ); return( TRUE ); } int testEnvelopeCompress( void ) { /* In practice these two produce identical output since we always have to use the indefinite-length encoding internally because we don't know in advance how large the compressed data will be */ if( !envelopeCompress( "env_cprn", FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length */ if( !envelopeCompress( "env_cpr", TRUE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize */ return( envelopeCompress( "env_cpr.pgp", TRUE, CRYPT_FORMAT_PGP ) ); } /* PGP format */ /* Test encrypted enveloping with a raw session key */ static int envelopeSessionCrypt( const char *dumpFileName, const BOOLEAN useDatasize, const BOOLEAN useLargeBuffer, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_CONTEXT cryptContext; CRYPT_ALGO_TYPE cryptAlgo = ( formatType == CRYPT_FORMAT_PGP ) ? \ selectCipher( CRYPT_ALGO_IDEA ) : \ selectCipher( CRYPT_ALGO_CAST ); BYTE *inBufPtr = ENVELOPE_TESTDATA, *outBufPtr = globalBuffer; #if defined( __MSDOS16__ ) || defined( __WIN16__ ) const int length = useLargeBuffer ? 16384 : ENVELOPE_TESTDATA_SIZE; #else const int length = useLargeBuffer ? 1048576L : ENVELOPE_TESTDATA_SIZE; #endif /* 16- vs.32-bit systems */ const int bufSize = length + 128; int count; if( useLargeBuffer ) { int i; printf( "Testing %sraw-session-key encrypted enveloping of large " "data quantity...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "" ); /* Allocate a large buffer and fill it with a known value */ if( ( inBufPtr = malloc( bufSize ) ) == NULL ) { printf( "Couldn't allocate buffer of %d bytes, skipping large " "buffer enveloping test.\n", length ); return( TRUE ); } outBufPtr = inBufPtr; for( i = 0; i < length; i++ ) inBufPtr[ i ] = i & 0xFF; } else printf( "Testing %sraw-session-key encrypted enveloping%s...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "", ( useDatasize && ( formatType != CRYPT_FORMAT_PGP ) ) ? \ " with datasize hint" : "" ); if( formatType != CRYPT_FORMAT_PGP ) { /* Create the session key context. We don't check for errors here since this code will already have been tested earlier */ cryptCreateContext( &cryptContext, CRYPT_UNUSED, cryptAlgo ); } else { /* PGP only allows a limited subset of algorithms and modes, in addition we have to specifically check that IDEA is available since it's possible to build cryptlib without IDEA support */ if( cryptAlgo != CRYPT_ALGO_IDEA ) { puts( "Can't test PGP enveloping because the IDEA algorithm " "isn't available in this\nbuild of cryptlib.\n" ); return( TRUE ); } cryptCreateContext( &cryptContext, CRYPT_UNUSED, cryptAlgo ); cryptSetAttribute( cryptContext, CRYPT_CTXINFO_MODE, CRYPT_MODE_CFB ); } cryptSetAttributeString( cryptContext, CRYPT_CTXINFO_KEY, "0123456789ABCDEF", 16 ); /* Create the envelope, push in a password and the data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) || \ !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_SESSIONKEY, cryptContext ) ) return( FALSE ); if( useDatasize && !useLargeBuffer ) { /* Test the ability to destroy the context after it's been added (we replace it with a different context that's used later for de-enveloping) */ cryptDestroyContext( cryptContext ); cryptCreateContext( &cryptContext, CRYPT_UNUSED, cryptAlgo ); cryptSetAttributeString( cryptContext, CRYPT_CTXINFO_KEY, "0123456789ABCDEF", 16 ); } if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, length ); if( useLargeBuffer ) cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, length + 1024 ); count = pushData( cryptEnvelope, inBufPtr, length, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, outBufPtr, bufSize ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); if( !useLargeBuffer ) debugDump( dumpFileName, outBufPtr, count ); /* Create the envelope, push in the data, pop the de-enveloped result, and destroy the envelope */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); if( useLargeBuffer ) cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, length + 1024 ); count = pushData( cryptEnvelope, outBufPtr, count, NULL, cryptContext ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, outBufPtr, bufSize ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Make sure that the result matches what we pushed */ if( count != length ) { puts( "De-enveloped data length != original length." ); return( FALSE ); } if( useLargeBuffer ) { int i; for( i = 0; i < length; i++ ) if( outBufPtr[ i ] != ( i & 0xFF ) ) { printf( "De-enveloped data != original data at byte %d, " "line %d.\n", i, __LINE__ ); return( FALSE ); } } else if( memcmp( outBufPtr, ENVELOPE_TESTDATA, length ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ if( useLargeBuffer ) free( inBufPtr ); cryptDestroyContext( cryptContext ); puts( "Enveloping of raw-session-key-encrypted data succeeded.\n" ); return( TRUE ); } int testEnvelopeSessionCrypt( void ) { if( !envelopeSessionCrypt( "env_sesn", FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length */ if( !envelopeSessionCrypt( "env_ses", TRUE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize */ #if 0 /* Although in theory PGP supports raw session-key based enveloping, in practice this key is always (implicitly) derived from a user password, so the enveloping code doesn't allow the use of raw session keys */ return( envelopeSessionCrypt( "env_ses.pgp", TRUE, FALSE, CRYPT_FORMAT_PGP ) ); #endif /* 0 */ return( TRUE ); } int testEnvelopeSessionCryptLargeBuffer( void ) { return( envelopeSessionCrypt( "env_ses", TRUE, TRUE, CRYPT_FORMAT_CRYPTLIB ) ); } /* Datasize, large buffer */ /* Test encrypted enveloping */ static int envelopeDecrypt( BYTE *buffer, const int length, const CRYPT_CONTEXT cryptContext ) { CRYPT_ENVELOPE cryptEnvelope; int count; /* Create the envelope, push in the data, pop the de-enveloped result, and destroy the envelope */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); count = pushData( cryptEnvelope, buffer, length, NULL, cryptContext ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, buffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); destroyEnvelope( cryptEnvelope ); return( count ); } static int envelopeCrypt( const char *dumpFileName, const BOOLEAN useDatasize, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_CONTEXT cryptContext; CRYPT_ENVELOPE cryptEnvelope; int count; printf( "Testing encrypted enveloping%s...\n", useDatasize ? " with datasize hint" : "" ); /* Create the session key context. We don't check for errors here since this code will already have been tested earlier */ cryptCreateContext( &cryptContext, CRYPT_UNUSED, CRYPT_ALGO_3DES ); cryptSetAttributeString( cryptContext, CRYPT_CTXINFO_KEY, "0123456789ABCDEF", 16 ); /* Create the envelope, push in a KEK and the data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) || \ !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEY, cryptContext ) ) return( FALSE ); if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, ENVELOPE_TESTDATA_SIZE ); count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( dumpFileName, globalBuffer, count ); /* De-envelope the data and make sure that the result matches what we pushed */ count = envelopeDecrypt( globalBuffer, count, cryptContext ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ cryptDestroyContext( cryptContext ); puts( "Enveloping of encrypted data succeeded.\n" ); return( TRUE ); } int testEnvelopeCrypt( void ) { if( !envelopeCrypt( "env_kekn", FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length */ return( envelopeCrypt( "env_kek", TRUE, CRYPT_FORMAT_CRYPTLIB ) ); } /* Datasize */ /* Test password-based encrypted enveloping */ static int envelopePasswordDecrypt( BYTE *buffer, const int length ) { CRYPT_ENVELOPE cryptEnvelope; int count; /* Create the envelope, push in the data, pop the de-enveloped result, and destroy the envelope */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); count = pushData( cryptEnvelope, buffer, length, TEXT( "Password" ), paramStrlen( TEXT( "Password" ) ) ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, buffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); destroyEnvelope( cryptEnvelope ); return( count ); } static int envelopePasswordCrypt( const char *dumpFileName, const BOOLEAN useDatasize, const BOOLEAN useAltCipher, const BOOLEAN multiKeys, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; int count; printf( "Testing %s%spassword-encrypted enveloping%s", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : "", multiKeys ? "multiple-" : "", ( useDatasize && ( formatType != CRYPT_FORMAT_PGP ) ) ? \ " with datasize hint" : "" ); if( useAltCipher ) printf( ( formatType == CRYPT_FORMAT_PGP ) ? \ " with non-default cipher type" : " and stream cipher" ); puts( "..." ); /* Create the envelope, push in a password and the data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) || \ !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, TEXT( "Password" ), paramStrlen( TEXT( "Password" ) ) ) ) return( FALSE ); if( useAltCipher ) { CRYPT_CONTEXT sessionKeyContext; int status; /* Test enveloping with an IV-less stream cipher, which tests the handling of algorithms that can't be used to wrap themselves in the RecipientInfo */ status = cryptCreateContext( &sessionKeyContext, CRYPT_UNUSED, CRYPT_ALGO_RC4 ); if( status == CRYPT_ERROR_NOTAVAIL ) { puts( "Warning: Couldn't set non-default envelope cipher " "RC4, this may be disabled\n in this build of " "cryptlib.\n" ); destroyEnvelope( cryptEnvelope ); return( TRUE ); } if( cryptStatusOK( status ) ) { cryptGenerateKey( sessionKeyContext ); status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_SESSIONKEY, sessionKeyContext ); cryptDestroyContext( sessionKeyContext ); } if( cryptStatusError( status ) ) { printf( "Couldn't set non-default envelope cipher, error code " "%d, line %d.\n", status, __LINE__ ); return( FALSE ); } } if( multiKeys && \ ( !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, TEXT( "Password1" ), paramStrlen( TEXT( "Password1" ) ) ) || \ !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, TEXT( "Password2" ), paramStrlen( TEXT( "Password2" ) ) ) || \ !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, TEXT( "Password3" ), paramStrlen( TEXT( "Password3" ) ) ) ) ) return( FALSE ); if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, ENVELOPE_TESTDATA_SIZE ); count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( dumpFileName, globalBuffer, count ); /* De-envelope the data and make sure that the result matches what we pushed */ count = envelopePasswordDecrypt( globalBuffer, count ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ puts( "Enveloping of password-encrypted data succeeded.\n" ); return( TRUE ); } int testEnvelopePasswordCrypt( void ) { if( !envelopePasswordCrypt( "env_pasn", FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length */ if( !envelopePasswordCrypt( "env_pas", TRUE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize */ if( !envelopePasswordCrypt( "env_mpas", TRUE, FALSE, TRUE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, multiple keys */ if( !envelopePasswordCrypt( "env_pas.pgp", TRUE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format */ return( envelopePasswordCrypt( "env_pasr", TRUE, TRUE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ); } /* IV-less cipher */ /* Test PKC-encrypted enveloping */ static int envelopePKCDecrypt( BYTE *buffer, const int length, const KEYFILE_TYPE keyFileType ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_KEYSET cryptKeyset; const C_STR keysetName = getKeyfileName( keyFileType, TRUE ); const C_STR password = getKeyfilePassword( keyFileType ); int count, status; /* Create the envelope and push in the decryption keyset */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, keysetName, CRYPT_KEYOPT_READONLY ); if( cryptStatusOK( status ) ) status = addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEYSET_DECRYPT, cryptKeyset ); cryptKeysetClose( cryptKeyset ); if( status <= 0 ) return( FALSE ); /* Push in the data */ count = pushData( cryptEnvelope, buffer, length, password, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, buffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); destroyEnvelope( cryptEnvelope ); return( count ); } static int envelopePKCDecryptDirect( BYTE *buffer, const int length, const KEYFILE_TYPE keyFileType ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_CONTEXT cryptContext; int count, status; /* Create the envelope and get the decryption key */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); status = getPrivateKey( &cryptContext, getKeyfileName( keyFileType, TRUE ), getKeyfileUserID( keyFileType, TRUE ), getKeyfilePassword( keyFileType ) ); if( cryptStatusError( status ) ) return( FALSE ); /* Push in the data */ count = pushData( cryptEnvelope, buffer, length, NULL, cryptContext ); cryptDestroyContext( cryptContext ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, buffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); destroyEnvelope( cryptEnvelope ); return( count ); } static int envelopePKCCrypt( const char *dumpFileName, const BOOLEAN useDatasize, const KEYFILE_TYPE keyFileType, const BOOLEAN useRecipient, const BOOLEAN useMultipleKeyex, const BOOLEAN useAltAlgo, const BOOLEAN useDirectKey, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_KEYSET cryptKeyset; CRYPT_HANDLE cryptKey; const C_STR keysetName = getKeyfileName( keyFileType, FALSE ); /* When reading keys we have to explicitly use the first matching key in the PGP 2.x keyring since the remaining keys are (for some reason) stored unencrypted, and the keyring read code will disallow the use of the key if it's stored in this manner */ const C_STR keyID = ( keyFileType == KEYFILE_PGP ) ? \ TEXT( "test" ) : getKeyfileUserID( keyFileType, FALSE ); int count, status; if( !keyReadOK ) { puts( "Couldn't find key files, skipping test of public-key " "encrypted enveloping..." ); return( TRUE ); } printf( "Testing %spublic-key encrypted enveloping", ( formatType == CRYPT_FORMAT_PGP ) ? \ ( ( keyFileType == KEYFILE_PGP ) ? "PGP " : "OpenPGP " ) : "" ); if( useDatasize && ( formatType != CRYPT_FORMAT_PGP ) && \ !( useRecipient || useMultipleKeyex || useDirectKey ) ) printf( " with datasize hint" ); printf( " using " ); printf( ( keyFileType == KEYFILE_PGP || \ keyFileType == KEYFILE_OPENPGP ) ? \ ( ( formatType == CRYPT_FORMAT_PGP ) ? \ "PGP key" : "raw public key" ) : \ "X.509 cert" ); if( useRecipient && !useAltAlgo ) printf( " and recipient info" ); if( useMultipleKeyex ) printf( " and additional keying info" ); if( useAltAlgo ) printf( " and alt.encr.algo" ); if( useDirectKey ) printf( " and direct key add" ); puts( "..." ); /* If we're using OpenPGP keys we have to use a recipient rather than adding the key directly because there's no way to tell in advance, when reading a dual DSA/Elgamal key, which one is actually needed. Since the signing private key is the one which is usually needed in standalone reads, a straight read will return the DSA rather than Elgamal key. It's only through the use of recipient info that the (cryptlib-internal) code can specify a preference for an encryption key */ assert( ( keyFileType == KEYFILE_OPENPGP && useRecipient ) || \ !( keyFileType == KEYFILE_OPENPGP ) ); /* Open the keyset and either get the public key the hard (to make sure that this version works) or leave the keyset open to allow it to be added to the envelope */ status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, keysetName, CRYPT_KEYOPT_READONLY ); if( cryptStatusError( status ) ) { printf( "Couldn't open keyset '%s', status %d, line %d.\n", keysetName, status, __LINE__ ); return( FALSE ); } if( !useRecipient ) { status = cryptGetPublicKey( cryptKeyset, &cryptKey, CRYPT_KEYID_NAME, keyID ); cryptKeysetClose( cryptKeyset ); if( cryptStatusError( status ) ) { puts( "Read of public key from file keyset failed." ); return( FALSE ); } } /* Create the envelope, push in the recipient info or public key and data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) ) return( FALSE ); if( useAltAlgo ) { /* Specify the use of an alternative (non-default) bulk encryption algorithm */ if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_OPTION_ENCR_ALGO, CRYPT_ALGO_BLOWFISH ) ) return( FALSE ); } if( useRecipient ) { /* Add recipient information to the envelope. Since we can't guarantee for enveloping with cryptlib native key types that we have a real public-key keyset available at this time (it's created by a different part of the self-test code that may not have run yet) we're actually reading the public key from the private-key keyset. Normally we couldn't do this, however since PKCS #15 doesn't store email addresses as key ID's (there's no need to), the code will drop back to trying for a match on the key label. Because of this we specify the private key label instead of a real recipient email address. Note that this trick only works because of a coincidence of two or three factors and wouldn't normally be used, it's only used here because we can't assume that a real public-key keyset is available for use. An additional test that would be useful is the ability to handle multiple key exchange records, however the keyset kludge makes this rather difficult. Since the functionality is tested by the use of multiple passwords in the conventional-encryption test earlier on this isn't a major issue */ if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEYSET_ENCRYPT, cryptKeyset ) || \ !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_RECIPIENT, keyID, paramStrlen( keyID ) ) ) return( FALSE ); cryptKeysetClose( cryptKeyset ); } else { if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_PUBLICKEY, cryptKey ) ) return( FALSE ); cryptDestroyObject( cryptKey ); } if( useMultipleKeyex && \ !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, TEXT( "test" ), paramStrlen( TEXT( "test" ) ) ) ) return( FALSE ); if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, ENVELOPE_TESTDATA_SIZE ); count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( dumpFileName, globalBuffer, count ); /* De-envelope the data and make sure that the result matches what we pushed */ if( useDirectKey ) count = envelopePKCDecryptDirect( globalBuffer, count, keyFileType ); else count = envelopePKCDecrypt( globalBuffer, count, keyFileType ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ puts( "Enveloping of public-key encrypted data succeeded.\n" ); return( TRUE ); } int testEnvelopePKCCrypt( void ) { if( cryptQueryCapability( CRYPT_ALGO_IDEA, NULL ) == CRYPT_ERROR_NOTAVAIL ) puts( "Skipping raw public-key and PGP enveloping, which requires " "the IDEA cipher to\nbe enabled.\n" ); else { if( !envelopePKCCrypt( "env_pkcn", FALSE, KEYFILE_PGP, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length, raw key */ if( !envelopePKCCrypt( "env_pkc", TRUE, KEYFILE_PGP, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, raw key */ if( !envelopePKCCrypt( "env_pkc.pgp", TRUE, KEYFILE_PGP, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format */ if( !envelopePKCCrypt( "env_pkc.pgp", TRUE, KEYFILE_PGP, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format, recipient */ if( !envelopePKCCrypt( "env_pkca.pgp", TRUE, KEYFILE_PGP, TRUE, FALSE, TRUE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format, recipient, nonstandard bulk encr.algo */ if( !envelopePKCCrypt( "env_pkc.gpg", TRUE, KEYFILE_OPENPGP, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* OpenPGP format, recipient (required for DSA/Elgamal keys) */ if( !envelopePKCCrypt( "env_pkce.der", TRUE, KEYFILE_OPENPGP, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, recipient w/Elgamal key for indef-length recipient info */ } if( !envelopePKCCrypt( "env_crt.pgp", TRUE, KEYFILE_X509, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format, certificate */ if( !envelopePKCCrypt( "env_crtn", FALSE, KEYFILE_X509, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length, certificate */ if( !envelopePKCCrypt( "env_crt", TRUE, KEYFILE_X509, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, certificate */ if( !envelopePKCCrypt( "env_crt", TRUE, KEYFILE_X509, FALSE, FALSE, FALSE, TRUE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, certificate, decrypt key provided directly */ if( !envelopePKCCrypt( "env_crt", TRUE, KEYFILE_X509, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, cerficate, recipient */ return( envelopePKCCrypt( "env_crtp", TRUE, KEYFILE_X509, FALSE, TRUE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ); } /* Datasize, cerficate+password */ /* Test signed enveloping */ static int getSigCheckResult( const CRYPT_ENVELOPE cryptEnvelope, const CRYPT_CONTEXT sigCheckContext, const BOOLEAN showAttributes ) { int value, status; /* Display all of the attributes that we've got */ if( showAttributes && !displayAttributes( cryptEnvelope ) ) return( FALSE ); /* Determine the result of the signature check */ status = cryptGetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_CURRENT, &value ); if( cryptStatusError( status ) ) { printf( "Read of required attribute for signature check returned " "status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } if( value != CRYPT_ENVINFO_SIGNATURE ) { printf( "Envelope requires unexpected enveloping information type " "%d, line %d.\n", value, __LINE__ ); return( FALSE ); } if( sigCheckContext != CRYPT_UNUSED ) { status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, sigCheckContext ); if( cryptStatusError( status ) ) { printf( "Attempt to add signature check key returned status " "%d, line %d.\n", status, __LINE__ ); return( FALSE ); } } status = cryptGetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE_RESULT, &value ); if( cryptStatusError( status ) ) { printf( "Signature check returned status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } switch( value ) { case CRYPT_OK: puts( "Signature is valid." ); return( TRUE ); case CRYPT_ERROR_NOTFOUND: puts( "Cannot find key to check signature." ); break; case CRYPT_ERROR_SIGNATURE: puts( "Signature is invalid." ); break; case CRYPT_ERROR_NOTAVAIL: puts( "Warning: Couldn't verify signature due to use of a " "deprecated/insecure\n signature algorithm.\n" ); return( TRUE ); default: printf( "Signature check failed, result = %d, line %d.\n", value, __LINE__ ); } return( FALSE ); } static int envelopeSigCheck( BYTE *buffer, const int length, const CRYPT_CONTEXT hashContext, const CRYPT_CONTEXT sigContext, const BOOLEAN useRawKey, const BOOLEAN useAltRawKey, const BOOLEAN detachedSig, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; int count, status; /* Create the envelope and push in the sig.check keyset if we're not using a supplied context for the sig.check */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); if( sigContext == CRYPT_UNUSED ) { CRYPT_KEYSET cryptKeyset; if( useRawKey ) status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, useAltRawKey ? \ OPENPGP_PUBKEY_FILE : PGP_PUBKEY_FILE, CRYPT_KEYOPT_READONLY ); else status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, USER_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY ); if( cryptStatusOK( status ) ) status = addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEYSET_SIGCHECK, cryptKeyset ); cryptKeysetClose( cryptKeyset ); if( status <= 0 ) return( FALSE ); } /* If the hash value is being supplied externally, add it to the envelope before we add the signature data */ if( detachedSig && hashContext != CRYPT_UNUSED ) { status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_HASH, hashContext ); if( cryptStatusError( status ) ) { printf( "Couldn't add externally-generated hash value to " "envelope, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } } /* Push in the data */ count = pushData( cryptEnvelope, buffer, length, NULL, 0 ); if( !cryptStatusError( count ) ) { if( detachedSig ) { if( hashContext == CRYPT_UNUSED ) count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); } else count = popData( cryptEnvelope, buffer, length ); } if( cryptStatusError( count ) ) return( FALSE ); /* Determine the result of the signature check */ if( !getSigCheckResult( cryptEnvelope, sigContext, TRUE ) ) return( FALSE ); /* If we supplied the sig-checking key, make sure that it's handled correctly by the envelope. We shouldn't be able to read it back from a PGP envelope, and from a cryptlib/CMS/SMIME envelope we should get back only a cert, not the full private key that we added */ if( sigContext != CRYPT_UNUSED ) { CRYPT_CONTEXT sigCheckContext; status = cryptGetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, &sigCheckContext ); if( formatType == CRYPT_FORMAT_PGP ) { /* If it's a PGP envelope, we can't retrieve the signing key from it */ if( cryptStatusOK( status ) ) { printf( "Attempt to read signature check key from PGP " "envelope succeeded when it\nshould have failed, " "line %d.\n", __LINE__ ); return( FALSE ); } } else { CRYPT_ENVELOPE testEnvelope; /* If it's a cryptlib/CMS/SMIME envelope, we should be able to retrieve the signing key from it */ if( cryptStatusError( status ) ) { printf( "Couldn't retrieve signature check key from " "envelope, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } /* The signing key should be a pure cert, not the private key+ cert combination that we pushed in. Note that the following will result in an error message being printed in addEnvInfoNumeric() */ createEnvelope( &testEnvelope, CRYPT_FORMAT_CRYPTLIB ); if( addEnvInfoNumeric( testEnvelope, CRYPT_ENVINFO_SIGNATURE, sigCheckContext ) ) { printf( "Retrieved signature check key is a private key, not " "a certificate, line %d.\n", __LINE__ ); return( FALSE ); } else puts( " (The above message indicates that the test " "condition was successfully\n checked)." ); destroyEnvelope( testEnvelope ); cryptDestroyCert( sigCheckContext ); } } /* Clean up */ if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); return( count ); } static int envelopeSign( const void *data, const int dataLength, const char *dumpFileName, const BOOLEAN useDatasize, const BOOLEAN useRawKey, const BOOLEAN useAltRawKey, const BOOLEAN useCustomHash, const BOOLEAN useSuppliedKey, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_KEYSET cryptKeyset; CRYPT_CONTEXT cryptContext; int count, status; if( !keyReadOK ) { puts( "Couldn't find key files, skipping test of signed " "enveloping..." ); return( TRUE ); } printf( "Testing %ssigned enveloping%s", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : \ ( formatType == CRYPT_FORMAT_SMIME ) ? "S/MIME " : "", ( useDatasize && ( formatType != CRYPT_FORMAT_PGP ) ) ? \ " with datasize hint" : "" ); if( useCustomHash ) printf( " %s custom hash", ( formatType == CRYPT_FORMAT_PGP ) ? "with" :"and" ); printf( " using %s", useAltRawKey ? "raw DSA key" : \ useRawKey ? "raw public key" : useSuppliedKey ? \ "supplied X.509 cert" : "X.509 cert" ); puts( "..." ); /* Get the private key */ if( useRawKey || useAltRawKey ) { status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, useAltRawKey ? \ OPENPGP_PRIVKEY_FILE : PGP_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY ); if( cryptStatusOK( status ) ) { status = cryptGetPrivateKey( cryptKeyset, &cryptContext, CRYPT_KEYID_NAME, getKeyfileUserID( KEYFILE_PGP, TRUE ), getKeyfilePassword( useAltRawKey ? \ KEYFILE_OPENPGP : KEYFILE_PGP ) ); cryptKeysetClose( cryptKeyset ); } } else status = getPrivateKey( &cryptContext, USER_PRIVKEY_FILE, USER_PRIVKEY_LABEL, TEST_PRIVKEY_PASSWORD ); if( cryptStatusError( status ) ) { printf( "Read of private key from key file failed, status %d, " "line %d, cannot test enveloping.\n", status, __LINE__ ); return( FALSE ); } /* Create the envelope, push in the signing key, any extra information, and the data to sign, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) ) return( FALSE ); if( useCustomHash ) { CRYPT_CONTEXT hashContext; /* Add the (nonstandard) hash algorithm information. We need to do this before we add the signing key since it's automatically associated with the last hash algorithm added */ cryptCreateContext( &hashContext, CRYPT_UNUSED, CRYPT_ALGO_RIPEMD160 ); status = addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_HASH, hashContext ); cryptDestroyContext( hashContext ); if( status <= 0 ) return( FALSE ); } if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, cryptContext ) ) return( FALSE ); if( useDatasize && !useRawKey && !useCustomHash && \ ( formatType != CRYPT_FORMAT_PGP ) ) { CRYPT_CONTEXT hashContext; /* Make sure that adding a (pseudo-duplicate) hash action that duplicates the one already added implicitly by the addition of the signature key succeeds (internally, nothing is really done since the hash action is already present) */ cryptCreateContext( &hashContext, CRYPT_UNUSED, CRYPT_ALGO_SHA ); status = addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_HASH, hashContext ); cryptDestroyContext( hashContext ); if( status <= 0 ) return( FALSE ); } if( cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, cryptContext ) != CRYPT_ERROR_INITED ) { puts( "Addition of duplicate key to envelope wasn't detected." ); return( FALSE ); } if( !useSuppliedKey ) cryptDestroyContext( cryptContext ); if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, dataLength ); count = pushData( cryptEnvelope, data, dataLength, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( dumpFileName, globalBuffer, count ); /* De-envelope the data and make sure that the result matches what we pushed */ count = envelopeSigCheck( globalBuffer, count, CRYPT_UNUSED, ( useSuppliedKey ) ? cryptContext : CRYPT_UNUSED, useRawKey, useAltRawKey, FALSE, formatType ); if( count <= 0 ) return( FALSE ); if( count != dataLength || memcmp( globalBuffer, data, dataLength ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } if( useSuppliedKey ) { /* If the following fails, there's a problem with handling reference counting for keys */ status = cryptDestroyContext( cryptContext ); if( cryptStatusError( status ) ) { printf( "Attempt to destroy externally-added sig.check key " "returned %d, line %d.\n", status, __LINE__ ); return( FALSE ); } } /* Clean up */ puts( "Enveloping of signed data succeeded.\n" ); return( TRUE ); } int testEnvelopeSign( void ) { if( cryptQueryCapability( CRYPT_ALGO_IDEA, NULL ) == CRYPT_ERROR_NOTAVAIL ) puts( "Skipping raw public-key based signing, which requires the " "IDEA cipher to\nbe enabled.\n" ); else { if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_sign", FALSE, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length, raw key */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_sig", TRUE, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, raw key */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_sig.pgp", TRUE, TRUE, FALSE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format, raw key */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_sigd.pgp", TRUE, TRUE, TRUE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format, raw DSA key */ } if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_csgn", FALSE, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Indefinite length, certificate */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_csg", TRUE, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, certificate */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_csgs", TRUE, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_SMIME ) ) return( FALSE ); /* Datasize, certificate, S/MIME semantics */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_csg", TRUE, FALSE, FALSE, FALSE, TRUE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, certificate, sigcheck key supplied */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_csg.pgp", TRUE, FALSE, FALSE, FALSE, FALSE, CRYPT_FORMAT_PGP ) ) return( FALSE ); /* PGP format, certificate */ if( !envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_hsg", TRUE, FALSE, FALSE, TRUE, FALSE, CRYPT_FORMAT_CRYPTLIB ) ) return( FALSE ); /* Datasize, cert, externally-suppl.hash */ return( envelopeSign( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, "env_csg", TRUE, FALSE, FALSE, FALSE, TRUE, CRYPT_FORMAT_CRYPTLIB ) ); } /* Externally-supplied key, to test isolation of sig.check key */ /* Test signed envelope with forced envelope buffer overflow */ static int envelopeSignOverflow( const void *data, const int dataLength, const char *dumpFileName, const CRYPT_FORMAT_TYPE formatType, const char *description ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_CONTEXT cryptContext; BYTE localBuffer[ 8192 + 4096 ]; const BOOLEAN forceOverflow = ( dataLength <= 8192 ) ? TRUE : FALSE; int localBufPos, bytesIn, bytesOut, status; if( !keyReadOK ) { puts( "Couldn't find key files, skipping test of signed " "enveloping..." ); return( TRUE ); } printf( "Testing %ssigned enveloping with forced overflow in %s...\n", ( formatType == CRYPT_FORMAT_PGP ) ? "PGP " : \ ( formatType == CRYPT_FORMAT_SMIME ) ? "S/MIME " : "", description ); /* Get the private key */ status = getPrivateKey( &cryptContext, USER_PRIVKEY_FILE, USER_PRIVKEY_LABEL, TEST_PRIVKEY_PASSWORD ); if( cryptStatusError( status ) ) { printf( "Read of private key from key file failed, cannot test " "enveloping, line %d.\n", __LINE__ ); return( FALSE ); } /* Create the envelope and push in the signing key and any extra information */ if( !createEnvelope( &cryptEnvelope, formatType ) ) return( FALSE ); if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, cryptContext ) ) return( FALSE ); cryptDestroyContext( cryptContext ); status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, dataLength ); if( cryptStatusOK( status ) && forceOverflow ) /* Set an artificially-small buffer to force an overflow */ status = cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, 8192 ); if( cryptStatusError( status ) ) { printf( "Couldn't set envelope parameters to force overflow, line " "%d.\n", __LINE__ ); return( FALSE ); } /* Push in the data to sign. Since we're forcing an overflow, we can't do this via the usual pushData() but have to do it manually to handle the restart once the overflow occurs */ status = cryptPushData( cryptEnvelope, data, dataLength, &bytesIn ); if( cryptStatusError( status ) || bytesIn != dataLength ) { printf( "cryptPushData() failed with status %d, copied %d of %d " "bytes, line %d.\n", status, bytesIn, dataLength, __LINE__ ); return( FALSE ); } status = cryptFlushData( cryptEnvelope ); if( forceOverflow && status != CRYPT_ERROR_OVERFLOW ) { printf( "cryptFlushData() returned status %d, should have been " "CRYPT_ERROR_OVERFLOW,\n line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptPopData( cryptEnvelope, localBuffer, 8192 + 4096, &bytesOut ); if( cryptStatusError( status ) ) { printf( "cryptPopData() #1 failed with status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } localBufPos = bytesOut; status = cryptFlushData( cryptEnvelope ); if( cryptStatusError( status ) ) { printf( "cryptFlushData() failed with error code %d, line %d.\n", status, __LINE__ ); printErrorAttributeInfo( cryptEnvelope ); return( FALSE ); } status = cryptPopData( cryptEnvelope, localBuffer + localBufPos, 8192 + 4096 - localBufPos, &bytesOut ); if( cryptStatusError( status ) ) { printf( "cryptPopData() #2 failed with status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } localBufPos += bytesOut; if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", localBufPos ); debugDump( dumpFileName, localBuffer, localBufPos ); /* De-envelope the data and make sure that the result matches what we pushed */ bytesOut = envelopeSigCheck( localBuffer, localBufPos, CRYPT_UNUSED, CRYPT_UNUSED, FALSE, FALSE, FALSE, formatType ); if( bytesOut <= 0 ) return( FALSE ); if( bytesOut != dataLength || memcmp( localBuffer, data, dataLength ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ puts( "Enveloping of signed data succeeded.\n" ); return( TRUE ); } int testEnvelopeSignOverflow( void ) { BYTE buffer[ 8192 + 1024 ]; /* Push in just the right amount of data to force an overflow when we generate the signature, to check overflow handling in the enveloping code. For PGP it's almost impossible to invoke overflow handling since the enveloping code is set up to either emit the signature directly into the buffer or, via an over-conservative estimation of buffer space, ensure that the user leaves enough space in the buffer for the entire sig. For an estimated space requirement of 256 bytes, 8192 - 280 will force the sig into the auxBuffer, but since this is an over- conservative estimate it'll then be flushed straight into the envelope buffer. The only way to actually force overflow handling would be to use the longest possible key size and a cert with a large issuerAndSerialNumber. (In addition to the envelope buffer-overflow check, we also try enveloping data with a length at the boundary where PGP switches from 2-byte to 4-byte lengths, 8384 bytes, to verify that this works OK). For CMS, we can cause an overflow in one of two locations. The first, with 8192 - 1152 bytes of data, causes an overflow when emitting the signing certs. This is fairly straightforward, the enveloping code always requires enough room for the signing certs, so all that happens is that the user pops some data and tries again. The second overflow is with 8192 - 1280 bytes of data, which causes an overflow on signing. */ memset( buffer, '*', 8192 + 1024 ); if( !envelopeSignOverflow( buffer, 8192 - 280, "env_sigo.pgp", CRYPT_FORMAT_PGP, "signature" ) ) return( FALSE ); /* PGP format, raw key */ if( !envelopeSignOverflow( buffer, 8384 - 6, "env_sigo2.pgp", CRYPT_FORMAT_PGP, "seg.boundary" ) ) return( FALSE ); /* PGP format, raw key */ if( !envelopeSignOverflow( buffer, 8192 - 1152, "env_csgo1", CRYPT_FORMAT_SMIME, "signing certs" ) ) return( FALSE ); /* Datasize, certificate, S/MIME semantics */ return( envelopeSignOverflow( buffer, 8192 - 1280, "env_csgo2", CRYPT_FORMAT_SMIME, "signature" ) ); } /* Datasize, certificate, S/MIME semantics */ /* Test authenticated (MACd) enveloping */ static int envelopeAuthent( const void *data, const int dataLength, const BOOLEAN useDatasize ) { CRYPT_ENVELOPE cryptEnvelope; int count; printf( "Testing authenticated enveloping" ); if( useDatasize ) printf( " with datasize hint" ); puts( "..." ); /* Create the envelope and push in the password after telling the enveloping code we want to MAC rather than encrypt */ if( !createEnvelope( &cryptEnvelope, CRYPT_FORMAT_CRYPTLIB ) || \ !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_MAC, TRUE ) || \ !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, TEXT( "Password" ), paramStrlen( TEXT( "Password" ) ) ) ) return( FALSE ); /* Push in the data, pop the enveloped result, and destroy the envelope */ if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, dataLength ); count = pushData( cryptEnvelope, data, dataLength, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( useDatasize ? "env_mac" : "env_macn", globalBuffer, count ); /* Create the envelope */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); /* Push in the data */ count = pushData( cryptEnvelope, globalBuffer, count, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); /* Determine the result of the MAC check */ if( !getSigCheckResult( cryptEnvelope, CRYPT_UNUSED, TRUE ) || \ !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Make sure that the result matches what we pushed */ if( count != dataLength || memcmp( globalBuffer, data, dataLength ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ puts( "Enveloping of authenticated data succeeded.\n" ); return( TRUE ); } int testEnvelopeAuthenticate( void ) { /* As of mid 2003 there are no known implementations of this CMS mechanism, any attempt to use it will trigger an assertion in the enveloping code intended to catch things like this so that we don't try and exercise it */ return( TRUE ); if( !envelopeAuthent( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, FALSE ) ) return( FALSE ); return( envelopeAuthent( ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, TRUE ) ); } /**************************************************************************** * * * CMS Enveloping Test Routines * * * ****************************************************************************/ /* Test CMS signature generation/checking */ static int displaySigResult( const CRYPT_ENVELOPE cryptEnvelope, const CRYPT_CONTEXT sigCheckContext, const BOOLEAN firstSig ) { CRYPT_CERTIFICATE signerInfo; BOOLEAN sigStatus; int status; /* Determine the result of the signature check. We only display the attributes for the first sig since this operation walks the attribute list,which moves the attribute cursor */ sigStatus = getSigCheckResult( cryptEnvelope, sigCheckContext, firstSig ); if( sigCheckContext != CRYPT_UNUSED ) /* If the sig.check key is provided externally (which in practice we only do for PGP sigs), there's no signer info or extra data present */ return( sigStatus ); /* Report on the signer and signature info. We continue even if the sig. status is bad since we can still try and display signing info even if the check fails */ status = cryptGetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, &signerInfo ); if( cryptStatusError( status ) && sigStatus ) { printf( "Cannot retrieve signer information from CMS signature, " "status = %d, line %d.\n", status, __LINE__ ); return( FALSE ); } if( cryptStatusOK( status ) ) { puts( "Signer information is:" ); if( !printCertInfo( signerInfo ) ) return( FALSE ); cryptDestroyCert( signerInfo ); } status = cryptGetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE_EXTRADATA, &signerInfo ); if( cryptStatusError( status ) && sigStatus && \ status != CRYPT_ERROR_NOTFOUND ) { printf( "Cannot retrieve signature information from CMS signature, " "status = %d, line %d.\n", status, __LINE__ ); return( FALSE ); } if( cryptStatusOK( status ) ) { puts( "Signature information is:" ); if( !printCertInfo( signerInfo ) ) return( FALSE ); cryptDestroyCert( signerInfo ); } return( sigStatus ); } static int cmsEnvelopeSigCheck( const void *signedData, const int signedDataLength, const CRYPT_CONTEXT sigCheckContext, const CRYPT_CONTEXT hashContext, const BOOLEAN detachedSig, const BOOLEAN hasTimestamp, const BOOLEAN checkData ) { CRYPT_ENVELOPE cryptEnvelope; int count, status; /* Create the (de-)envelope and push in the data. Since this is a CMS signature that carries its certs with it, there's no need to push in a sig.check keyset. If it has a detached sig, we need to push two lots of data, first the signature to set the envelope state, then the data, however if the hash is being supplied externally we just set the hash attribute. In addition if it's a detached sig, there's nothing to be unwrapped so we don't pop any data */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); if( detachedSig && hashContext != CRYPT_UNUSED ) { /* The hash value is being supplied externally, add it to the envelope before we add the signature data */ status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_HASH, hashContext ); if( cryptStatusError( status ) ) { printf( "Couldn't add externally-generated hash value to " "envelope, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } } count = pushData( cryptEnvelope, signedData, signedDataLength, NULL, 0 ); if( count == CRYPT_ERROR_NOTAVAIL && \ !( detachedSig || hasTimestamp || checkData ) ) { /* Some old signed data uses deprecated or now-broken algorithms which will produce a CRYPT_ERROR_NOTAVAIL if we try and verify the signature, treat this as a special case */ puts( "Warning: The hash/signature algorithm required to verify " "the signed data\n isn't enabled in this build of " "cryptlib, can't verify the\n signature." ); destroyEnvelope( cryptEnvelope ); return( TRUE ); } if( !cryptStatusError( count ) ) { if( detachedSig ) { if( hashContext == CRYPT_UNUSED ) count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); } else count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); } if( cryptStatusError( count ) ) return( FALSE ); /* Display the details of the envelope signature and check whether there's more information such as a timestamp or a second signature present */ status = displaySigResult( cryptEnvelope, sigCheckContext, TRUE ); if( status == TRUE && hasTimestamp ) { CRYPT_ENVELOPE cryptTimestamp; int contentType; /* Try and get the timestamp info. We can't safely use displaySigResult() on this because many timestamps are stripped- down minimal-size CMS messages with no additional sig-checking info present, so we just read the CMS content-type to make sure that everything's OK */ printf( "Envelope contains a timestamp..." ); status = cryptGetAttribute( cryptEnvelope, CRYPT_ENVINFO_TIMESTAMP, &cryptTimestamp ); if( cryptStatusError( status ) ) { printf( "\nCouldn't read timestamp from envelope, status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptGetAttribute( cryptTimestamp, CRYPT_ENVINFO_CONTENTTYPE, &contentType ); if( cryptStatusError( status ) || \ contentType != CRYPT_CONTENT_TSTINFO ) { printf( "\nTimestamp data envelope doesn't appear to contain a " "timestamp, line %d.\n", __LINE__ ); return( FALSE ); } printf( " timestamp data appears OK.\n" ); cryptDestroyEnvelope( cryptTimestamp ); status = TRUE; } if( status == TRUE && cryptStatusOK( \ cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_CURRENT_GROUP, CRYPT_CURSOR_NEXT ) ) ) { puts( "Data has a second signature:" ); status = displaySigResult( cryptEnvelope, CRYPT_UNUSED, FALSE ); } if( status == TRUE && cryptStatusOK( \ cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_CURRENT_GROUP, CRYPT_CURSOR_NEXT ) ) ) { /* We can have two, but not three */ puts( "Data appears to have (nonexistent) third signature." ); return( FALSE ); } /* Make sure that the result matches what we pushed */ if( !detachedSig && checkData && ( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } /* Clean up */ if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); return( status ); } static int cmsEnvelopeSign( const BOOLEAN useDatasize, const BOOLEAN useAttributes, const BOOLEAN useExtAttributes, const BOOLEAN detachedSig, const BOOLEAN useExternalHash, const BOOLEAN useTimestamp, const BOOLEAN useNonDataContent, const BOOLEAN dualSig, const CRYPT_CONTEXT externalSignContext, const CRYPT_FORMAT_TYPE formatType ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_CONTEXT cryptContext, cryptContext2, hashContext = CRYPT_UNUSED; BOOLEAN isPGP = ( formatType == CRYPT_FORMAT_PGP ) ? TRUE : FALSE; int count, status = CRYPT_OK; if( !keyReadOK ) { puts( "Couldn't find key files, skipping test of CMS signed " "enveloping..." ); return( TRUE ); } printf( "Testing %s %s%s", isPGP ? "PGP" : "CMS", useExtAttributes ? "extended " : "", detachedSig ? "detached signature" : \ dualSig ? "dual signature" : "signed enveloping" ); if( useNonDataContent ) printf( " of non-data content" ); if( useExternalHash ) printf( " with externally-supplied hash" ); if( !useAttributes ) printf( " without signing attributes" ); if( useDatasize && \ !( useNonDataContent || useAttributes || useExtAttributes || \ detachedSig || useTimestamp ) ) /* Keep the amount of stuff being printed down */ printf( " with datasize hint" ); if( useTimestamp ) printf( " and timestamp" ); puts( "..." ); /* Get the private key. If we're applying two signatures, we also get a second signing key. Since the dual-key file test has created a second signing key, we use that as the most convenient one */ if( externalSignContext != CRYPT_UNUSED ) cryptContext = externalSignContext; else { status = getPrivateKey( &cryptContext, USER_PRIVKEY_FILE, USER_PRIVKEY_LABEL, TEST_PRIVKEY_PASSWORD ); if( cryptStatusError( status ) ) { puts( "Read of private key from key file failed, cannot test " "CMS enveloping." ); return( FALSE ); } } if( dualSig ) { status = getPrivateKey( &cryptContext2, DUAL_PRIVKEY_FILE, DUAL_SIGNKEY_LABEL, TEST_PRIVKEY_PASSWORD ); if( cryptStatusError( status ) ) { puts( "Read of private key from key file failed, cannot test " "CMS enveloping." ); return( FALSE ); } } /* Create the CMS envelope, push in the signing key(s) and data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, formatType ) || \ !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, cryptContext ) ) return( FALSE ); if( dualSig && !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE, cryptContext2 ) ) return( FALSE ); if( ( externalSignContext == CRYPT_UNUSED ) && !isPGP ) cryptDestroyContext( cryptContext ); if( dualSig ) cryptDestroyContext( cryptContext2 ); if( useNonDataContent ) /* Test non-data content type w.automatic attribute handling */ status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_CONTENTTYPE, CRYPT_CONTENT_SIGNEDDATA ); if( cryptStatusOK( status ) && useDatasize ) status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, ENVELOPE_TESTDATA_SIZE ); if( cryptStatusOK( status ) && useExtAttributes ) { CRYPT_CERTIFICATE cmsAttributes; /* Add an ESS security label and signing description as signing attributes */ cryptCreateCert( &cmsAttributes, CRYPT_UNUSED, CRYPT_CERTTYPE_CMS_ATTRIBUTES ); status = cryptSetAttributeString( cmsAttributes, CRYPT_CERTINFO_CMS_SECLABEL_POLICY, TEXT( "1 3 6 1 4 1 9999 1" ), paramStrlen( TEXT( "1 3 6 1 4 1 9999 1" ) ) ); if( cryptStatusOK( status ) ) status = cryptSetAttribute( cmsAttributes, CRYPT_CERTINFO_CMS_SECLABEL_CLASSIFICATION, CRYPT_CLASSIFICATION_SECRET ); if( cryptStatusOK( status ) ) status = cryptSetAttributeString( cmsAttributes, CRYPT_CERTINFO_CMS_SECLABEL_CATTYPE, TEXT( "1 3 6 1 4 1 9999 2" ), paramStrlen( TEXT( "1 3 6 1 4 1 9999 2" ) ) ); if( cryptStatusOK( status ) ) status = cryptSetAttributeString( cmsAttributes, CRYPT_CERTINFO_CMS_SECLABEL_CATVALUE, "\x04\x04\x01\x02\x03\x04", 6 ); if( cryptStatusOK( status ) ) status = cryptSetAttributeString( cmsAttributes, CRYPT_CERTINFO_CMS_SIGNINGDESCRIPTION, "This signature isn't worth the paper it's not " "printed on", 56 ); if( cryptStatusOK( status ) ) status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_SIGNATURE_EXTRADATA, cmsAttributes ); cryptDestroyCert( cmsAttributes ); } if( cryptStatusOK( status ) && detachedSig ) status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DETACHEDSIGNATURE, TRUE ); if( cryptStatusOK( status ) && !useAttributes ) status = cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CMS_DEFAULTATTRIBUTES, FALSE ); if( cryptStatusOK( status ) && useTimestamp ) { CRYPT_SESSION cryptSession; /* Create the TSP session, add the TSA URL, and add it to the envelope */ status = cryptCreateSession( &cryptSession, CRYPT_UNUSED, CRYPT_SESSION_TSP ); if( status == CRYPT_ERROR_PARAM3 ) /* TSP session access not available */ return( CRYPT_ERROR_NOTAVAIL ); status = cryptSetAttributeString( cryptSession, CRYPT_SESSINFO_SERVER_NAME, TSP_DEFAULTSERVER_NAME, paramStrlen( TSP_DEFAULTSERVER_NAME ) ); if( cryptStatusError( status ) ) return( attrErrorExit( cryptSession, "cryptSetAttributeString()", status, __LINE__ ) ); status = cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_TIMESTAMP, cryptSession ); cryptDestroySession( cryptSession ); } if( cryptStatusError( status ) ) { printf( "cryptSetAttribute() failed with error code %d, line %d.\n", status, __LINE__ ); return( FALSE ); } count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); if( !useAttributes ) /* Restore the default attributes setting */ cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CMS_DEFAULTATTRIBUTES, TRUE ); if( cryptStatusError( count ) ) { /* The timestamping can fail for a wide range of (non-fatal) reasons, typically either because this build doesn't have networking enabled or because the TSA can't be contacted, so we don't treat this one as a fatal error */ if( useTimestamp ) { puts( "Envelope timestamping failed due to problems talking to " "TSA, this is a non-\ncritical problem. Continuing...\n" ); cryptDestroyEnvelope( cryptEnvelope ); return( TRUE ); } return( FALSE ); } count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "%s %s has size %d bytes.\n", isPGP ? "PGP" : "CMS", ( detachedSig ) ? "detached signature" : "signed data", count ); debugDump( detachedSig ? ( !isPGP ? \ ( useDatasize ? "smi_dsg" : "smi_dsgn" ) : \ "pgp_dsg.pgp" ) : \ useExtAttributes ? \ ( useDatasize ? "smi_esg" : "smi_esgn" ) : \ useTimestamp ? \ ( useDatasize ? "smi_tsg" : "smi_tsgn" ) : \ useNonDataContent ? \ ( useDatasize ? "smi_ndc" : "smi_ndcn" ) : \ dualSig ? \ ( useDatasize ? "smi_2sg" : "smi_n2sg" ) : \ useDatasize ? "smi_sig" : "smi_sign", globalBuffer, count ); /* If we're supplying the hash value externally, calculate it now */ if( useExternalHash ) { status = cryptCreateContext( &hashContext, CRYPT_UNUSED, CRYPT_ALGO_SHA ); if( cryptStatusOK( status ) ) status = cryptEncrypt( hashContext, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE ); if( cryptStatusOK( status ) && formatType == CRYPT_FORMAT_CMS ) status = cryptEncrypt( hashContext, "", 0 ); if( cryptStatusError( status ) ) { puts( "Couldn't create external hash of data." ); return( FALSE ); } } /* Make sure that the signature is valid */ status = cmsEnvelopeSigCheck( globalBuffer, count, isPGP ? cryptContext : CRYPT_UNUSED, hashContext, detachedSig, FALSE, TRUE ); if( hashContext != CRYPT_UNUSED ) cryptDestroyContext( hashContext ); if( isPGP ) cryptDestroyContext( cryptContext ); if( status <= 0 ) return( FALSE ); if( detachedSig ) printf( "Creation of %s %sdetached signature %ssucceeded.\n\n", isPGP ? "PGP" : "CMS", useExtAttributes ? "extended " : "", ( hashContext != CRYPT_UNUSED ) ? \ "with externally-supplied hash " : "" ); else printf( "Enveloping of CMS %s%ssigned data succeeded.\n\n", useExtAttributes ? "extended " : "", useTimestamp ? "timestamped " : "" ); return( TRUE ); } int testCMSEnvelopeSign( void ) { if( !cmsEnvelopeSign( FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Minimal (no default S/MIME attributes) */ if( !cmsEnvelopeSign( FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Standard (default S/MIME signing attributes) */ if( !cmsEnvelopeSign( TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Datasize and attributes */ if( !cmsEnvelopeSign( FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Extended signing attributes */ if( !cmsEnvelopeSign( TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Datasize and extended attributes */ return( cmsEnvelopeSign( TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ); } /* Signing of non-data content */ int testCMSEnvelopeDualSign( void ) { return( cmsEnvelopeSign( TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ); /* Standard, with two signatures */ } int testCMSEnvelopeDetachedSig( void ) { if( !cmsEnvelopeSign( FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Detached sig and attributes */ if( !cmsEnvelopeSign( FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ) return( FALSE ); /* Detached sig, attributes, externally-suppl.hash */ return( cmsEnvelopeSign( TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_PGP ) ); } /* Detached sig, data size, externally-suppl.hash, PGP format */ int testCMSEnvelopeSignEx( const CRYPT_CONTEXT signContext ) { return( cmsEnvelopeSign( TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, signContext, CRYPT_FORMAT_CMS ) ); } /* Datasize, attributes, external signing context */ int testSessionEnvTSP( void ) { /* This is a pseudo-enveloping test that uses the enveloping functionality but is called as part of the session tests since full testing of the TSP handling requires that it be used to timestamp an S/MIME sig */ return( cmsEnvelopeSign( TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_FORMAT_CMS ) ); } /* Datasize, attributes, timestamp */ static int cmsImportSignedData( const char *fileName, const int fileNo ) { BYTE *bufPtr = globalBuffer; char msgBuffer[ 128 ]; int count, status; /* Read the test data */ count = getFileSize( fileName ) + 10; if( count >= BUFFER_SIZE ) { if( ( bufPtr = malloc( count ) ) == NULL ) { printf( "Couldn't allocate test buffer of %d bytes, line %d.\n", count, __LINE__ ); return( FALSE ); } } sprintf( msgBuffer, "S/MIME SignedData #%d", fileNo ); count = readFileData( fileName, msgBuffer, bufPtr, count ); if( count <= 0 ) { if( bufPtr != globalBuffer ) free( bufPtr ); return( count ); } /* Check the signature on the data */ status = cmsEnvelopeSigCheck( bufPtr, count, CRYPT_UNUSED, CRYPT_UNUSED, FALSE, ( fileNo == 6 ) ? TRUE : FALSE, FALSE ); if( bufPtr != globalBuffer ) free( bufPtr ); if( status ) puts( "S/MIME SignedData import succeeded.\n" ); return( status ); } /* Test CMS enveloping/de-enveloping */ static int cmsEnvelopeDecrypt( const void *envelopedData, const int envelopedDataLength, const CRYPT_HANDLE externalKeyset, const C_STR externalPassword ) { CRYPT_ENVELOPE cryptEnvelope; int count, status; /* Create the envelope and push in the decryption keyset */ if( !createDeenvelope( &cryptEnvelope ) ) return( FALSE ); if( externalKeyset != CRYPT_UNUSED ) status = addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEYSET_DECRYPT, externalKeyset ); else { CRYPT_KEYSET cryptKeyset; status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, USER_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY ); if( cryptStatusOK( status ) ) status = addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEYSET_DECRYPT, cryptKeyset ); cryptKeysetClose( cryptKeyset ); } if( status <= 0 ) return( FALSE ); /* Push in the data */ count = pushData( cryptEnvelope, envelopedData, envelopedDataLength, ( externalPassword == NULL ) ? TEST_PRIVKEY_PASSWORD : externalPassword, 0 ); if( cryptStatusError( count ) ) return( ( externalPassword != NULL ) ? count : FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Make sure that the result matches what we pushed */ if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } return( TRUE ); } static int cmsEnvelopeCrypt( const char *dumpFileName, const BOOLEAN useDatasize, const BOOLEAN useStreamCipher, const BOOLEAN useLargeBlockCipher, const BOOLEAN useExternalRecipientKeyset, const CRYPT_HANDLE externalCryptContext, const CRYPT_HANDLE externalKeyset, const C_STR externalPassword, const C_STR recipientName ) { CRYPT_ENVELOPE cryptEnvelope; CRYPT_HANDLE cryptKey; BOOLEAN isKeyAgreementKey = FALSE; int count, status; if( !keyReadOK ) { puts( "Couldn't find key files, skipping test of CMS encrypted " "enveloping..." ); return( TRUE ); } printf( "Testing CMS public-key encrypted enveloping" ); if( externalKeyset != CRYPT_UNUSED && recipientName != NULL ) { if( useExternalRecipientKeyset ) printf( " with recipient keys in device" ); else printf( " with dual encr./signing certs" ); } else { if( useStreamCipher ) printf( " with stream cipher" ); else { if( useLargeBlockCipher ) printf( " with large block size cipher" ); else if( useDatasize ) printf( " with datasize hint" ); } } puts( "..." ); /* Get the public key. We use assorted variants to make sure that they all work */ if( externalCryptContext != CRYPT_UNUSED ) { int cryptAlgo; status = cryptGetAttribute( externalCryptContext, CRYPT_CTXINFO_ALGO, &cryptAlgo ); if( cryptStatusError( status ) ) { puts( "Couldn't determine algorithm for public key, cannot test " "CMS enveloping." ); return( FALSE ); } if( cryptAlgo == CRYPT_ALGO_KEA ) isKeyAgreementKey = TRUE; cryptKey = externalCryptContext; } else if( recipientName == NULL ) { CRYPT_KEYSET cryptKeyset; /* No recipient name, get the public key */ status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, USER_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY ); if( cryptStatusError( status ) ) { printf( "Recipient keyset open failed with status %d, " "line %d.\n", status, __LINE__ ); return( FALSE ); } status = cryptGetPublicKey( cryptKeyset, &cryptKey, CRYPT_KEYID_NAME, USER_PRIVKEY_LABEL ); cryptKeysetClose( cryptKeyset ); if( cryptStatusError( status ) ) { printf( "Read of public key from key file failed with " "status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } } /* Create the envelope, add the public key and originator key if necessary, push in the data, pop the enveloped result, and destroy the envelope */ if( !createEnvelope( &cryptEnvelope, CRYPT_FORMAT_CMS ) ) return( FALSE ); if( recipientName != NULL ) { CRYPT_KEYSET cryptKeyset = externalKeyset; /* We're using a recipient name, add the recipient keyset and recipient name */ if( !useExternalRecipientKeyset ) { status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, DATABASE_KEYSET_TYPE, DATABASE_KEYSET_NAME, CRYPT_KEYOPT_READONLY ); if( cryptStatusError( status ) ) { printf( "Couldn't open key database, status %d, line %d.\n", status, __LINE__ ); return( FALSE ); } } if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_KEYSET_ENCRYPT, cryptKeyset ) ) return( FALSE ); if( !useExternalRecipientKeyset ) cryptKeysetClose( cryptKeyset ); if( !addEnvInfoString( cryptEnvelope, CRYPT_ENVINFO_RECIPIENT, recipientName, paramStrlen( recipientName ) ) ) return( FALSE ); } else if( !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_PUBLICKEY, cryptKey ) ) return( FALSE ); if( isKeyAgreementKey && \ !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_ORIGINATOR, cryptKey ) ) return( FALSE ); if( externalCryptContext == CRYPT_UNUSED && recipientName == NULL ) cryptDestroyObject( cryptKey ); if( useDatasize ) cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, ENVELOPE_TESTDATA_SIZE ); count = pushData( cryptEnvelope, ENVELOPE_TESTDATA, ENVELOPE_TESTDATA_SIZE, NULL, 0 ); if( cryptStatusError( count ) ) return( FALSE ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); if( cryptStatusError( count ) ) return( FALSE ); if( !destroyEnvelope( cryptEnvelope ) ) return( FALSE ); /* Tell them what happened */ printf( "Enveloped data has size %d bytes.\n", count ); debugDump( dumpFileName, globalBuffer, count ); /* Make sure that the enveloped data is valid */ status = cmsEnvelopeDecrypt( globalBuffer, count, externalKeyset, externalPassword ); if( status <= 0 ) /* Can be FALSE or an error code */ return( status ); /* Clean up */ puts( "Enveloping of CMS public-key encrypted data succeeded.\n" ); return( TRUE ); } static int cmsImportEnvelopedData( const char *fileName, const int fileNo ) { CRYPT_ENVELOPE cryptEnvelope; BYTE *bufPtr = globalBuffer; char msgBuffer[ 128 ]; int count, bytesIn, byteCount = 0, status; /* Read the test data */ count = getFileSize( fileName ) + 10; if( count >= BUFFER_SIZE ) { if( ( bufPtr = malloc( count ) ) == NULL ) { printf( "Couldn't allocate test buffer of %d bytes, lin e%d.\n", count, __LINE__ ); return( FALSE ); } } sprintf( msgBuffer, "S/MIME EnvelopedData #%d", fileNo ); count = readFileData( fileName, msgBuffer, bufPtr, count ); if( count <= 0 ) { if( bufPtr != globalBuffer ) free( bufPtr ); return( count ); } /* Make sure that we can parse the data */ if( !createDeenvelope( &cryptEnvelope ) ) { if( bufPtr != globalBuffer ) free( bufPtr ); return( FALSE ); } status = cryptPushData( cryptEnvelope, bufPtr, count, &bytesIn ); while( status == CRYPT_ERROR_UNDERFLOW ) { byteCount += bytesIn; status = cryptPushData( cryptEnvelope, bufPtr + byteCount, count - byteCount, &bytesIn ); } destroyEnvelope( cryptEnvelope ); if( bufPtr != globalBuffer ) free( bufPtr ); if( status == CRYPT_ENVELOPE_RESOURCE ) /* When we get to the CRYPT_ENVELOPE_RESOURCE stage we know that we've successfully processed all of the recipients, because cryptlib is asking us for a key to continue */ status = CRYPT_OK; if( cryptStatusError( status ) ) { printf( "Couldn't de-envelope data, status = %d, line %d.\n", status, __LINE__ ); return( FALSE ); } puts( "S/MIME EnvelopedData import succeeded.\n" ); return( TRUE ); } int testCMSEnvelopePKCCrypt( void ) { int value, status; if( !cmsEnvelopeCrypt( "smi_pkcn", FALSE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_UNUSED, NULL, NULL ) ) return( FALSE ); /* Standard */ if( !cmsEnvelopeCrypt( "smi_pkc", TRUE, FALSE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_UNUSED, NULL, NULL ) ) return( FALSE ); /* Datasize hint */ /* Test enveloping with an IV-less stream cipher, which bypasses the usual CBC-mode block cipher handling. The alternative way of doing this is to manually add a CRYPT_CTXINFO_SESSIONKEY object, doing it this way is less work */ cryptGetAttribute( CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, &value ); cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, CRYPT_ALGO_RC4 ); status = cmsEnvelopeCrypt( "smi_pkcs", TRUE, TRUE, FALSE, FALSE, CRYPT_UNUSED, CRYPT_UNUSED, NULL, NULL ); cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, value ); if( !status ) /* Datasize and stream cipher */ return( status ); /* Test enveloping with a cipher with a larger-than-usual block size */ cryptGetAttribute( CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, &value ); cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, CRYPT_ALGO_AES ); status = cmsEnvelopeCrypt( "smi_pkcb", TRUE, FALSE, TRUE, FALSE, CRYPT_UNUSED, CRYPT_UNUSED, NULL, NULL ); cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, value ); return( status ); /* Datasize and large blocksize cipher */ } int testCMSEnvelopePKCCryptEx( const CRYPT_HANDLE encryptContext, const CRYPT_HANDLE decryptKeyset, const C_STR password, const C_STR recipient ) { int status; assert( ( encryptContext != CRYPT_UNUSED && recipient == NULL ) || \ ( encryptContext == CRYPT_UNUSED && recipient != NULL ) ); /* We can either provide the recipient's key directly as a pubkey context or indirectly as a recipient name. This is used to test a device's ability to act as a recipient key store */ status = cmsEnvelopeCrypt( "smi_pkcd", TRUE, FALSE, FALSE, \ ( recipient != NULL ) ? TRUE : FALSE, \ encryptContext, decryptKeyset, password, \ recipient ); if( status == CRYPT_ERROR_NOTFOUND ) { /* Datasize, keys in crypto device */ puts( " (This is probably because the public key certificate was " "regenerated after\n the certificate stored with the " "private key was created, so that the\n private key can't " "be identified any more using the public key that was\n " "used for encryption. This can happen when the cryptlib " "self-test is run\n in separate stages, with one stage " "re-using data that was created\n earlier during a " "previous stage)." ); return( FALSE ); } return( status ); } int testCMSEnvelopePKCCryptDoubleCert( void ) { CRYPT_KEYSET cryptKeyset; int status; /* The dual-cert test uses cryptlib's internal key management to read the appropriate cert from a database keyset, if this hasn't been set up then the test will fail so we try and detect the presence of the database keyset here. This isn't perfect since it requires that the database keyset be updated with the certs in the same run as this test, but it's the best we can do */ if( !doubleCertOK ) { puts( "The certificate database wasn't updated with dual encryption/" "signing certs\nduring this test run (either because database " "keysets aren't enabled in this\nbuild of cryptlib or because " "only some portions of the self-tests are being\nrun), " "skipping the test of CMS enveloping with dual certs.\n" ); return( TRUE ); } /* Since we're using certs with the same DN and email address present in multiple certs, we can't use the generic user keyset but have to use one that has been set up to have multiple certs that differ only in keyUsage */ status = cryptKeysetOpen( &cryptKeyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, DUAL_PRIVKEY_FILE, CRYPT_KEYOPT_READONLY ); if( cryptStatusError( status ) ) { puts( "Couldn't find keyset with dual encryption/signature certs " "for test of dual cert\nencryption." ); return( FALSE ); } status = cmsEnvelopeCrypt( "smi_pkcr", TRUE, FALSE, FALSE, FALSE, CRYPT_UNUSED, cryptKeyset, TEST_PRIVKEY_PASSWORD, TEXT( "dave@wetaburgers.com" ) ); cryptKeysetClose( cryptKeyset ); if( status == CRYPT_ERROR_NOTFOUND ) { /* Datasize, recipient */ puts( " (This is probably because the public key certificate was " "regenerated after\n the certificate stored with the " "private key was created, so that the\n private key can't " "be identified any more using the public key that was\n " "used for encryption. This can happen when the cryptlib " "self-test is run\n in separate stages, with one stage " "re-using data that was created\n earlier during a " "previous stage)." ); return( FALSE ); } return( status ); } /**************************************************************************** * * * Test Data Import Routines * * * ****************************************************************************/ /* Import S/MIME signed data */ int testCMSEnvelopeSignedDataImport( void ) { FILE *filePtr; BYTE fileName[ BUFFER_SIZE ]; int i; /* Make sure that the test data is present so that we can return a useful error message */ filenameFromTemplate( fileName, SMIME_SIG_FILE_TEMPLATE, 1 ); if( ( filePtr = fopen( fileName, "rb" ) ) == NULL ) { puts( "Couldn't find S/MIME SignedData file, skipping test of " "SignedData import..." ); return( TRUE ); } fclose( filePtr ); /* There are many encoding variations possible for signed data so we try a representative sample to make sure that the code works in all cases */ for( i = 1; i <= 3; i++ ) { filenameFromTemplate( fileName, SMIME_SIG_FILE_TEMPLATE, i ); if( !cmsImportSignedData( fileName, i ) && i != 2 ) /* AuthentiCode sig check fails for some reason */ return( FALSE ); } puts( "Import of S/MIME SignedData succeeded.\n" ); return( TRUE ); } /* Import S/MIME encrypted data */ int testCMSEnvelopePKCCryptImport( void ) { FILE *filePtr; BYTE fileName[ BUFFER_SIZE ]; int i; /* Make sure that the test data is present so that we can return a useful error message */ filenameFromTemplate( fileName, SMIME_ENV_FILE_TEMPLATE, 1 ); if( ( filePtr = fopen( fileName, "rb" ) ) == NULL ) { puts( "Couldn't find S/MIME EnvelopedData file, skipping test of " "EnvelopedData import..." ); return( TRUE ); } fclose( filePtr ); /* Enveloped data requires decryption keys that aren't generally available, so all that we do in this case is make sure that we can parse the data */ for( i = 1; i <= 1; i++ ) { filenameFromTemplate( fileName, SMIME_ENV_FILE_TEMPLATE, i ); if( !cmsImportEnvelopedData( fileName, i ) ) return( FALSE ); } puts( "Import of S/MIME EnvelopedData succeeded.\n" ); return( TRUE ); } /* Import PGP 2.x and OpenPGP-generated password-encrypted data */ int testPGPEnvelopePasswordCryptImport( void ) { BYTE fileName[ BUFFER_SIZE ]; int count; /* Process the PGP 2.x data */ filenameFromTemplate( fileName, PGP_ENC_FILE_TEMPLATE, 1 ); count = readFileData( fileName, "PGP password-encrypted data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePasswordDecrypt( globalBuffer, count ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of PGP password-encrypted data succeeded." ); /* Process the OpenPGP data */ filenameFromTemplate( fileName, PGP_ENC_FILE_TEMPLATE, 2 ); count = readFileData( fileName, "OpenPGP password-encrypted data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); if( !envelopePasswordDecrypt( globalBuffer, count ) ) return( FALSE ); if( memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } filenameFromTemplate( fileName, PGP_ENC_FILE_TEMPLATE, 3 ); count = readFileData( fileName, "OpenPGP password-encrypted data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); if( !envelopePasswordDecrypt( globalBuffer, count ) ) return( FALSE ); if( memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of OpenPGP password-encrypted data succeeded.\n" ); return( TRUE ); } /* Import PGP 2.x and OpenPGP-generated PKC-encrypted data */ int testPGPEnvelopePKCCryptImport( void ) { BYTE fileName[ BUFFER_SIZE ]; int count; /* Process the PGP 2.x data */ filenameFromTemplate( fileName, PGP_PKE_FILE_TEMPLATE, 1 ); count = readFileData( fileName, "PGP-encrypted data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePKCDecrypt( globalBuffer, count, KEYFILE_PGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } filenameFromTemplate( fileName, PGP_PKE_FILE_TEMPLATE, 2 ); count = readFileData( fileName, "PGP (NAI)-encrypted data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePKCDecrypt( globalBuffer, count, KEYFILE_NAIPGP ); if( count <= 0 ) return( FALSE ); if( globalBuffer[ 0 ] != 0xA3 || globalBuffer[ 1 ] != 0x01 || \ globalBuffer[ 2 ] != 0x5B || globalBuffer[ 3 ] != 0x53 ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of PGP-encrypted data succeeded." ); /* Process the OpenPGP data. The files are: File 1: RSA with 3DES. Create with: pgpo -e +pubring=".\pubring.pgp" +compress=off -o gpg_enc1.pgp -r test test.txt (In theory it could be created with gpg -e -z 0 --homedir . --always-trust -r test test.txt and pubring.pgp renamed to pubring.gpg, however GPG won't allow the use of the RSA key in pubring.pgp no matter what command- line options you specify because it's not self-signed). File 2: Elgamal and AES with MDC. Create with: gpg -e -z 0 --homedir . -o gpg_enc2.pgp -r test1 test.txt File 3: Elgamal and Blowfish with MDC. Create with: gpg -e -z 0 --homedir . --cipher-algo blowfish -o gpg_enc3.pgp -r test1 test.txt File 4: Elgamal and AES with MDC and partial lengths */ filenameFromTemplate( fileName, OPENPGP_PKE_FILE_TEMPLATE, 1 ); count = readFileData( fileName, "OpenPGP (GPG)-encrypted data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePKCDecrypt( globalBuffer, count, KEYFILE_PGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } filenameFromTemplate( fileName, OPENPGP_PKE_FILE_TEMPLATE, 2 ); count = readFileData( fileName, "OpenPGP (GPG)-encrypted data with " "AES + MDC", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePKCDecrypt( globalBuffer, count, KEYFILE_OPENPGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } filenameFromTemplate( fileName, OPENPGP_PKE_FILE_TEMPLATE, 3 ); count = readFileData( fileName, "OpenPGP (GPG)-encrypted data with " "Blowfish + MDC", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePKCDecrypt( globalBuffer, count, KEYFILE_OPENPGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } #if 0 filenameFromTemplate( fileName, OPENPGP_PKE_FILE_TEMPLATE, 4 ); count = readFileData( fileName, "OpenPGP (GPG)-encrypted data with " "partial lengths", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopePKCDecrypt( globalBuffer, count, KEYFILE_OPENPGP_PARTIAL ); if( count <= 0 ) return( FALSE ); count = envelopeDecompress( globalBuffer, BUFFER_SIZE, count ); if( count <= 0 ) return( FALSE ); if( count < 600 || memcmp( globalBuffer, "\n\nVorwort\n", 16 ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } #endif puts( "Import of OpenPGP-encrypted data succeeded.\n" ); return( TRUE ); } /* Import PGP 2.x and OpenPGP-generated signed data */ int testPGPEnvelopeSignedDataImport( void ) { CRYPT_CONTEXT hashContext; BYTE fileName[ BUFFER_SIZE ]; int count, status; /* Process the PGP 2.x data. Create with: pgp -s +secring="secring.pgp" +pubring="pubring.pgp" -u test test.txt */ filenameFromTemplate( fileName, PGP_SIG_FILE_TEMPLATE, 1 ); count = readFileData( fileName, "PGP-signed data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopeSigCheck( globalBuffer, count, CRYPT_UNUSED, CRYPT_UNUSED, TRUE, FALSE, FALSE, CRYPT_FORMAT_PGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of PGP-signed data succeeded." ); #if 0 /* Disabled because it uses a 512-bit sig and there doesn't appear to be any way to create a new file in this format */ /* Process the OpenPGP (actually a weird 2.x/OpenPGP hybrid produced by PGP 5.0) data. Create with: pgpo -s +secring="secring.pgp" +pubring="pubring.pgp" +compress=off -o signed2.pgp -u test test.txt (actually the exact details of how to create this packet are a mystery, it starts with a market packet and then a one-pass sig, which isn't generated by default by any of the PGP 5.x or 6.x versions) */ filenameFromTemplate( fileName, PGP_SIG_FILE_TEMPLATE, 2 ); count = readFileData( fileName, "PGP 2.x/OpenPGP-hybrid-signed data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopeSigCheck( globalBuffer, count, CRYPT_UNUSED, CRYPT_UNUSED, TRUE, FALSE, FALSE, CRYPT_FORMAT_PGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of PGP 2.x/OpenPGP-hybrid-signed data succeeded." ); #endif /* 0 */ /* Process the OpenPGP data. Create with: gpg -s -z 0 --homedir . -o signed3.pgp test.txt */ filenameFromTemplate( fileName, PGP_SIG_FILE_TEMPLATE, 3 ); count = readFileData( fileName, "OpenPGP-signed data", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopeSigCheck( globalBuffer, count, CRYPT_UNUSED, CRYPT_UNUSED, TRUE, TRUE, FALSE, CRYPT_FORMAT_PGP ); if( count <= 0 ) return( FALSE ); if( count != ENVELOPE_TESTDATA_SIZE || \ memcmp( globalBuffer, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of OpenPGP-signed data succeeded." ); /* Process the OpenPGP detached signature data. Create with: gpg -b -z 0 --openpgp --homedir . -o signed4.pgp test.txt (the --openpgp is for another GPG bug, without it it'll generate v3 sigs as if --force-v3-sigs had been specified). The data is provided externally so we have to hash it ourselves. Since PGP hashes further data after hashing the content, we can't complete the hashing but have to use the partially-completed hash */ status = cryptCreateContext( &hashContext, CRYPT_UNUSED, CRYPT_ALGO_SHA ); if( cryptStatusOK( status ) ) status = cryptEncrypt( hashContext, ENVELOPE_PGP_TESTDATA, ENVELOPE_TESTDATA_SIZE ); if( cryptStatusError( status ) ) { puts( "Couldn't create external hash of data." ); return( FALSE ); } filenameFromTemplate( fileName, PGP_SIG_FILE_TEMPLATE, 4 ); count = readFileData( fileName, "OpenPGP-signed data with " "externally-supplied hash", globalBuffer, BUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopeSigCheck( globalBuffer, count, hashContext, CRYPT_UNUSED, TRUE, TRUE, TRUE, CRYPT_FORMAT_PGP ); cryptDestroyContext( hashContext ); if( count <= 0 ) return( FALSE ); puts( "Import of OpenPGP-signed data with externally-supplied hash " "succeeded.\n" ); return( TRUE ); } /* Import PGP 2.x and OpenPGP-generated compressed data */ int testPGPEnvelopeCompressedDataImport( void ) { BYTE fileName[ BUFFER_SIZE ], *bufPtr; int count; /* Since this needs a nontrivial amount of data for the compression, we use a dynamically-allocated buffer */ if( ( bufPtr = malloc( FILEBUFFER_SIZE ) ) == NULL ) { puts( "Couldn't allocate test buffer." ); return( FALSE ); } /* Process the PGP 2.x data */ filenameFromTemplate( fileName, PGP_COPR_FILE_TEMPLATE, 1 ); count = readFileData( fileName, "PGP 2.x compressed data", bufPtr, FILEBUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopeDecompress( bufPtr, FILEBUFFER_SIZE, count ); if( count && memcmp( bufPtr, ENVELOPE_COMPRESSEDDATA, ENVELOPE_COMPRESSEDDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } if( count <= 0 ) return( FALSE ); puts( "Import of PGP 2.x compressed data succeeded.\n" ); /* Process the OpenPGP nested data. Create with: gpg -s --openpgp --homedir . test.c (== endian.c) with the same note as before for GPG bugs and --openpgp */ filenameFromTemplate( fileName, PGP_COPR_FILE_TEMPLATE, 2 ); count = readFileData( fileName, "OpenPGP compressed signed data", bufPtr, FILEBUFFER_SIZE ); if( count <= 0 ) return( FALSE ); count = envelopeDecompress( bufPtr, FILEBUFFER_SIZE, count ); if( count && \ ( bufPtr[ 0 ] != 0x90 || bufPtr[ 1 ] != 0x0D || \ bufPtr[ 2 ] != 0x03 || bufPtr[ 3 ] != 0x00 ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } if( count <= 0 ) return( FALSE ); memcpy( globalBuffer, bufPtr, count ); free( bufPtr ); count = envelopeSigCheck( globalBuffer, count, CRYPT_UNUSED, CRYPT_UNUSED, TRUE, TRUE, FALSE, CRYPT_FORMAT_PGP ); if( count <= 0 ) return( FALSE ); if( count && memcmp( globalBuffer, ENVELOPE_COMPRESSEDDATA, ENVELOPE_COMPRESSEDDATA_SIZE ) ) { puts( "De-enveloped data != original data." ); return( FALSE ); } puts( "Import of OpenPGP compressed signed data succeeded.\n" ); return( TRUE ); } /* Generic test routines used for debugging. These are only meant to be used interactively, and throw exceptions rather than returning status values */ static void dataImport( int count, const BOOLEAN resultBad ) { CRYPT_ENVELOPE cryptEnvelope; createDeenvelope( &cryptEnvelope ); count = pushData( cryptEnvelope, globalBuffer, count, NULL, 0 ); if( resultBad ) { assert( cryptStatusError( count ) ); return; } assert( !cryptStatusError( count ) ); count = popData( cryptEnvelope, globalBuffer, BUFFER_SIZE ); assert( !cryptStatusError( count ) ); destroyEnvelope( cryptEnvelope ); } void xxxDataImport( const char *fileName ) { int count; count = getFileSize( fileName ) + 10; if( count >= BUFFER_SIZE ) assert( 0 ); count = readFileData( fileName, "Generic test data", globalBuffer, count ); assert( count ); dataImport( count, FALSE ); } void xxxEnvTest( void ) { char fileName[ 256 ], text[ 64 ]; int count, i; #if 1 for( i = 1; i <= 4; i++ ) { sprintf( fileName, "/tmp/oct_odd_%d.der", i ); sprintf( text, "odd test file %d", i ); count = readFileData( fileName, text, globalBuffer, BUFFER_SIZE ); assert( count ); dataImport( count, FALSE ); } #endif /* 0 */ #if 1 for( i = 1; i <= 7; i++ ) { sprintf( fileName, "/tmp/oct_bad_%d.der", i ); sprintf( text, "bad test file %d", i ); count = readFileData( fileName, text, globalBuffer, BUFFER_SIZE ); assert( count ); dataImport( count, TRUE ); } #endif /* 0 */ } void xxxSignedDataImport( const char *fileName ) { int count, status; count = getFileSize( fileName ) + 10; if( count >= BUFFER_SIZE ) assert( 0 ); count = readFileData( fileName, "S/MIME test data", globalBuffer, count ); assert( count ); status = cmsEnvelopeSigCheck( globalBuffer, count, CRYPT_UNUSED, CRYPT_UNUSED, FALSE, FALSE, FALSE ); assert( status ); } void xxxEncryptedDataImport( const char *fileName ) { int count, status; count = getFileSize( fileName ) + 10; if( count >= BUFFER_SIZE ) assert( 0 ); count = readFileData( fileName, "S/MIME test data", globalBuffer, count ); assert( count ); status = cmsEnvelopeDecrypt( globalBuffer, count, CRYPT_UNUSED, NULL ); assert( status ); } #endif /* TEST_ENVELOPE || TEST_SESSION */