Exemplo n.º 1
0
C_RET cryptQueryObject( C_IN void C_PTR objectData,
						C_IN int objectDataLength,
						C_OUT CRYPT_OBJECT_INFO C_PTR cryptObjectInfo )
	{
	QUERY_INFO queryInfo = DUMMY_INIT_STRUCT;	/* If USE_PGP undef'd */
	STREAM stream;
	int value, length = objectDataLength, status;

	/* Perform basic error checking and clear the return value */
	if( objectDataLength <= MIN_CRYPT_OBJECTSIZE || \
		objectDataLength >= MAX_INTLENGTH )
		return( CRYPT_ERROR_PARAM2 );
	if( !isReadPtr( objectData, objectDataLength ) )
		return( CRYPT_ERROR_PARAM1 );
	if( !isWritePtrConst( cryptObjectInfo, sizeof( CRYPT_OBJECT_INFO ) ) )
		return( CRYPT_ERROR_PARAM3 );
	memset( cryptObjectInfo, 0, sizeof( CRYPT_OBJECT_INFO ) );

	/* Query the object.  This is just a wrapper for the lower-level object-
	   query functions.  Note that we use sPeek() rather than peekTag() 
	   because we want to continue processing (or at least checking for) PGP 
	   data if it's no ASN.1 */
	sMemConnect( &stream, ( void * ) objectData, length );
	status = value = sPeek( &stream );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( value == BER_SEQUENCE || value == MAKE_CTAG( CTAG_RI_PWRI ) )
		status = queryAsn1Object( &stream, &queryInfo );
	else
		{
#ifdef USE_PGP
		status = queryPgpObject( &stream, &queryInfo );
#else
		status = CRYPT_ERROR_BADDATA;
#endif /* USE_PGP */
		}
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Copy the externally-visible fields across */
	cryptObjectInfo->objectType = queryInfo.type;
	cryptObjectInfo->cryptAlgo = queryInfo.cryptAlgo;
	cryptObjectInfo->cryptMode = queryInfo.cryptMode;
	if( queryInfo.type == CRYPT_OBJECT_SIGNATURE )
		cryptObjectInfo->hashAlgo = queryInfo.hashAlgo;
	if( queryInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY && \
		queryInfo.saltLength > 0 )
		{
		memcpy( cryptObjectInfo->salt, queryInfo.salt, queryInfo.saltLength );
		cryptObjectInfo->saltSize = queryInfo.saltLength;
		if( queryInfo.keySetupAlgo != CRYPT_ALGO_NONE )
			cryptObjectInfo->hashAlgo = queryInfo.keySetupAlgo;
		}

	return( CRYPT_OK );
	}
Exemplo n.º 2
0
static int getItem( INOUT STREAM *stream, OUT ASN1_ITEM *item )
	{
	const long offset = stell( stream );
	long length;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( item, sizeof( ASN1_ITEM ) ) );

	REQUIRES_EXT( ( offset >= 0 && offset < MAX_INTLENGTH ), \
				  ASN1_STATE_ERROR );

	/* Clear return value */
	memset( item, 0, sizeof( ASN1_ITEM ) );

	/* Read the tag.  We can't use peekTag() for this since we may be 
	   reading EOC octets, which would be rejected by peekTag() */
	status = item->tag = sPeek( stream );
	if( cryptStatusError( status ) )
		return( ASN1_STATE_ERROR );
	if( item->tag == BER_EOC )
		{
		/* It looks like EOC octets, make sure that they're in order */
		status = checkEOC( stream );
		if( cryptStatusError( status ) )
			return( ASN1_STATE_ERROR );
		if( status == TRUE )
			{
			item->headerSize = 2;
			return( ASN1_STATE_NONE );
			}
		}

	/* Make sure that it's at least vaguely valid before we try the
	   readLongGenericHole() */
	if( item->tag <= 0 || item->tag >= MAX_TAG )
		return( ASN1_STATE_ERROR );

	/* It's not an EOC, read the tag and length as a generic hole */
	status = readLongGenericHole( stream, &length, item->tag );
	if( cryptStatusError( status ) )
		return( ASN1_STATE_ERROR );
	item->headerSize = stell( stream ) - offset;
	if( length == CRYPT_UNUSED )
		item->indefinite = TRUE;
	else
		{
		/* If the length that we've just read is larger than the object 
		   itself, it's an error */
		if( length >= MAX_BUFFER_SIZE - 1 ) 
			return( ASN1_STATE_ERROR );

		item->length = length;
		}
	return( ASN1_STATE_NONE );
	}
Exemplo n.º 3
0
static int readKeyDerivationInfo( INOUT STREAM *stream, 
								  OUT QUERY_INFO *queryInfo )
	{
	long endPos, value;
	int length, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) );

	/* Clear return value */
	memset( queryInfo, 0, sizeof( QUERY_INFO ) );

	/* Read the outer wrapper and key derivation algorithm OID */
	readConstructed( stream, NULL, CTAG_KK_DA );
	status = readFixedOID( stream, OID_PBKDF2, sizeofOID( OID_PBKDF2 ) );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the PBKDF2 parameters, limiting the salt and iteration count to
	   sane values */
	status = readSequence( stream, &length );
	if( cryptStatusError( status ) )
		return( status );
	endPos = stell( stream ) + length;
	readOctetString( stream, queryInfo->salt, &queryInfo->saltLength, 
					 2, CRYPT_MAX_HASHSIZE );
	status = readShortInteger( stream, &value );
	if( cryptStatusError( status ) )
		return( status );
	if( value < 1 || value > MAX_KEYSETUP_ITERATIONS )
		return( CRYPT_ERROR_BADDATA );
	queryInfo->keySetupIterations = ( int ) value;
	queryInfo->keySetupAlgo = CRYPT_ALGO_HMAC_SHA1;
	if( stell( stream ) < endPos && \
		sPeek( stream ) == BER_INTEGER )
		{
		/* There's an optional key length that may override the default 
		   key size present, read it.  Note that we compare the upper
		   bound to MAX_WORKING_KEYSIZE rather than CRYPT_MAX_KEYSIZE,
		   since this is a key used directly with an encryption algorithm
		   rather than a generic keying value that may be hashed down to 
		   size */
		status = readShortInteger( stream, &value );
		if( cryptStatusError( status ) )
			return( status );
		if( value < MIN_KEYSIZE || value > MAX_WORKING_KEYSIZE )
			return( CRYPT_ERROR_BADDATA );
		queryInfo->keySize = ( int ) value;
		}
	if( stell( stream ) < endPos )
		{
		CRYPT_ALGO_TYPE prfAlgo;
	
		/* There's a non-default hash algorithm ID present, read it */
		status = readAlgoID( stream, &prfAlgo, ALGOID_CLASS_HASH );
		if( cryptStatusError( status ) )
			return( status );
		queryInfo->keySetupAlgo = prfAlgo;
		}

	return( CRYPT_OK );
	}
Exemplo n.º 4
0
static int scanPacketGroup( INOUT STREAM *stream,
							OUT_LENGTH_SHORT_Z int *packetGroupLength,
							const BOOLEAN isLastGroup )
	{
	BOOLEAN firstPacket = TRUE, skipPackets = FALSE;
	int endPos = 0, iterationCount, status = CRYPT_OK;

	assert( isReadPtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( packetGroupLength, sizeof( int ) ) );

	/* Clear return value */
	*packetGroupLength = 0;

	for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_LARGE;
		 iterationCount++ )
		{
		long length;
		int ctb, type;

		/* Get the next CTB.  If it's the start of another packet group,
		   we're done */
		ctb = status = sPeek( stream );
		if( cryptStatusOK( status ) && !( pgpIsCTB( ctb ) ) )
			status = CRYPT_ERROR_BADDATA;
		if( cryptStatusError( status ) )
			{
			/* If we ran out of input data let the caller know, however if 
			   this is the last packet group then running out of data isn't
			   an error */
			if( status == CRYPT_ERROR_UNDERFLOW )
				{
				if( !isLastGroup )
					skipPackets = TRUE;
				break;
				}
			return( status );
			}
		type = pgpGetPacketType( ctb );
		if( firstPacket )
			{
			/* If the packet group doesn't start with the expected packet
			   type, skip packets to try and resync */
			if( type != PGP_PACKET_PUBKEY && type != PGP_PACKET_SECKEY )
				skipPackets = TRUE;
			firstPacket = FALSE;
			}
		else
			{
			/* If we've found the start of a new packet group, we're done */
			if( type == PGP_PACKET_PUBKEY || type == PGP_PACKET_SECKEY )
				break;
			}

		/* Skip the current packet in the buffer */
		status = pgpReadPacketHeader( stream, NULL, &length, \
									  getMinPacketSize( type ) );
		if( cryptStatusOK( status ) )
			{
			endPos = stell( stream ) + length;
			status = sSkip( stream, length );
			}
		if( cryptStatusError( status ) )
			{
			/* If we ran out of input data let the caller know */
			if( status == CRYPT_ERROR_UNDERFLOW )
				{
				skipPackets = TRUE;
				break;
				}
			return( status );
			}
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );

	/* Remember where the current packet group ends.  If we skipped packets 
	   or consumed all of the input in the buffer and there's more present 
	   beyond that, tell the caller to discard the data and try again */
	*packetGroupLength = endPos;
	return( skipPackets ? OK_SPECIAL : CRYPT_OK );
	}
Exemplo n.º 5
0
static int readKey( INOUT STREAM *stream, 
					INOUT PGP_INFO *pgpInfo, 
					IN_INT_SHORT_Z const int keyGroupNo,
					INOUT ERROR_INFO *errorInfo )
	{
	PGP_KEYINFO *keyInfo = &pgpInfo->key;
	HASHFUNCTION hashFunction;
	HASHINFO hashInfo;
	BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ], packetHeader[ 64 + 8 ];
	BOOLEAN isPublicKey = TRUE, isPrimaryKey = FALSE;
	void *pubKeyPayload;
	long packetLength;
	int pubKeyPos, pubKeyPayloadPos, endPos, pubKeyPayloadLen;
	int ctb, length, value, hashSize, iterationCount, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) );

	REQUIRES( keyGroupNo >= 0 && keyGroupNo < MAX_INTLENGTH_SHORT );
	REQUIRES( errorInfo != NULL );

	/* Process the CTB and packet length */
	ctb = sPeek( stream );
	if( cryptStatusError( ctb ) )
		{
		/* If there was an error reading the CTB, which is the first byte of 
		   the packet group, it means that we've run out of data so we 
		   return the status as a not-found error rather than the actual
		   stream status */
		return( CRYPT_ERROR_NOTFOUND );
		}
	switch( pgpGetPacketType( ctb ) )
		{
		case PGP_PACKET_SECKEY_SUB:
			keyInfo = &pgpInfo->subKey;
			isPublicKey = FALSE;
			break;

		case PGP_PACKET_SECKEY:
			isPublicKey = FALSE;
			break;

		case PGP_PACKET_PUBKEY_SUB:
			keyInfo = &pgpInfo->subKey;
			break;

		case PGP_PACKET_PUBKEY:
			isPrimaryKey = TRUE;
			break;

		default:
			retExt( CRYPT_ERROR_BADDATA, 
					( CRYPT_ERROR_BADDATA, errorInfo, 
					  "Invalid PGP CTB %02X for key packet group %d", 
					  ctb, keyGroupNo ) );
		}
	status = pgpReadPacketHeader( stream, NULL, &packetLength, 64 );
	if( cryptStatusError( status ) )
		{
		retExt( status,
				( status, errorInfo, 
				  "Invalid PGP key packet header for key packet group %d", 
				  keyGroupNo ) );
		}
	if( packetLength < 64 || packetLength > sMemDataLeft( stream ) )
		{
		retExt( CRYPT_ERROR_BADDATA, 
				( CRYPT_ERROR_BADDATA, errorInfo, 
				  "Invalid PGP key packet length %ld for key packet group %d", 
				  packetLength, keyGroupNo ) );
		}

	/* Since there can (in theory) be arbitrary numbers of subkeys and other 
	   odds and ends attached to a key and the details of what to do with 
	   these things gets a bit vague, we just skip any further subkeys that 
	   may be present */
	if( keyInfo->pkcAlgo != CRYPT_ALGO_NONE )
		{
		status = sSkip( stream, packetLength );
		for( iterationCount = 0; 
			 cryptStatusOK( status ) && \
				iterationCount < FAILSAFE_ITERATIONS_MED; 
			 iterationCount++ )
			{
			status = readUserID( stream, pgpInfo, 
								 isPrimaryKey ? &hashInfo : NULL );
			}
		ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
		if( cryptStatusError( status ) && status != OK_SPECIAL )
			{
			retExt( status, 
					( status, errorInfo, 
					  "Invalid additional PGP subkey information for key "
					  "packet group %d", keyGroupNo ) );
			}

		/* We've skipped the current subkey, we're done */
		return( CRYPT_OK );
		}

	/* Determine which bits make up the public and the private key data.  The
	   public-key data starts at the version number and includes the date,
	   validity, and public-key components.  Since there's no length 
	   information included for this data block we have to record bookmarks
	   and then later retroactively calculate the length based on how much
	   data we've read in the meantime:

		  pubKey pubKeyPayload		 privKey			 endPos
			|		|					|					|
			v		v					v					v
		+---+---------------------------+-------------------+
		|hdr|		|	Public key		|	Private key		|
		+---+---------------------------+-------------------+
			|		|<pubKeyPayloadLen->|					|
			|<----- pubKeyDataLen ----->|<-- privKeyDLen -->| 
			|<--------------- packetLength ---------------->| */
	pubKeyPos = stell( stream );
	endPos = pubKeyPos + packetLength;
	ENSURES( endPos > pubKeyPos && endPos < MAX_BUFFER_SIZE );
	status = value = sgetc( stream );
	if( cryptStatusError( status ) )
		return( status );
	if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \
		value != PGP_VERSION_OPENPGP )
		{
		/* Unknown version number, skip this packet */
		return( OK_SPECIAL );
		}
	pgpInfo->isOpenPGP = ( value == PGP_VERSION_OPENPGP ) ? TRUE : FALSE;

	/* Build the packet header, which is hashed along with the key components
	   to get the OpenPGP keyID.  This is generated anyway when the context
	   is created but we need to generate it here as well in order to locate
	   the key in the first place:

		byte		ctb = 0x99
		byte[2]		length
		byte		version = 4
		byte[4]		key generation time
	  [	byte[2]		validity time - PGP 2.x only ]
		byte[]		key data

	   We can't add the length or key data yet since we have to parse the
	   key data to know how long it is, so we can only build the static part
	   of the header at this point */
	packetHeader[ 0 ] = 0x99;
	packetHeader[ 3 ] = PGP_VERSION_OPENPGP;

	/* Read the timestamp and validity period (for PGP 2.x keys) */
	status = sread( stream, packetHeader + 4, 4 );
	if( !cryptStatusError( status ) && !pgpInfo->isOpenPGP )
		status = sSkip( stream, 2 );
	if( cryptStatusError( status ) )
		return( status );

	/* Read the public key components */
	pubKeyPayloadPos = stell( stream );
	status = readPublicKeyComponents( stream, keyInfo, &length );
	if( cryptStatusError( status ) )
		{
		/* If the error status is OK_SPECIAL then the problem was an
		   unrecognised algorithm or something similar so we just skip the 
		   packet */
		if( status == OK_SPECIAL )
			{
			DEBUG_DIAG(( "Encountered unrecognised algorithm while "
						 "reading key" ));
			assert( DEBUG_WARN );
			return( OK_SPECIAL );
			}
		retExt( status, 
				( status, errorInfo, 
				  "Invalid PGP public-key components for key packet group %d",
				  keyGroupNo ) );
		}

	/* Now that we know where the public key data starts and finishes, we 
	   can set up references to it */
	keyInfo->pubKeyDataLen = stell( stream ) - pubKeyPos;
	status = sMemGetDataBlockAbs( stream, pubKeyPos, &keyInfo->pubKeyData, 
								  keyInfo->pubKeyDataLen );
	if( cryptStatusError( status ) )
		{
		DEBUG_DIAG(( "Couldn't set up reference to key data" ));
		assert( DEBUG_WARN );
		return( status );
		}
	pubKeyPayloadLen = stell( stream ) - pubKeyPayloadPos;
	status = sMemGetDataBlockAbs( stream, pubKeyPayloadPos, &pubKeyPayload, 
								  pubKeyPayloadLen );
	if( cryptStatusError( status ) )
		{
		DEBUG_DIAG(( "Couldn't set up reference to key data" ));
		assert( DEBUG_WARN );
		return( status );
		}

	/* Complete the packet header that we read earlier on by adding the
	   length information */
	packetHeader[ 1 ] = intToByte( ( ( 1 + 4 + length ) >> 8 ) & 0xFF );
	packetHeader[ 2 ] = intToByte( ( 1 + 4 + length ) & 0xFF );

	/* Hash the data needed to generate the OpenPGP keyID */
	getHashParameters( CRYPT_ALGO_SHA1, 0, &hashFunction, &hashSize );
	hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2 + 1 + 4, 
				  HASH_STATE_START );
	hashFunction( hashInfo, hash, CRYPT_MAX_HASHSIZE, 
				  pubKeyPayload, pubKeyPayloadLen, HASH_STATE_END );
	memcpy( keyInfo->openPGPkeyID, hash + hashSize - PGP_KEYID_SIZE,
			PGP_KEYID_SIZE );

	/* If it's a private keyring, process the private key components */
	if( !isPublicKey )
		{
		/* Handle decryption information for private-key components if 
		   necessary */
		status = readPrivateKeyDecryptionInfo( stream, keyInfo );
		if( cryptStatusError( status ) )
			{
			/* If the error status is OK_SPECIAL then the problem was an
			   unrecognised algorithm or something similar so we just skip
			   the packet */
			if( status == OK_SPECIAL )
				{
				DEBUG_DIAG(( "Encountered unrecognised algorithm while "
							 "reading key" ));
				assert( DEBUG_WARN );
				return( OK_SPECIAL );
				}
			retExt( status, 
					( status, errorInfo, 
					  "Invalid PGP private-key decryption information for "
					  "key packet group %d", keyGroupNo ) );
			}

		/* What's left is the private-key data */
		keyInfo->privKeyDataLen = endPos - stell( stream );
		status = sMemGetDataBlock( stream, &keyInfo->privKeyData, 
								   keyInfo->privKeyDataLen );
		if( cryptStatusOK( status ) )
			status = sSkip( stream, keyInfo->privKeyDataLen );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* If it's the primary key, start hashing it in preparation for 
	   performing signature checks on subpackets */
	if( isPrimaryKey )
		{
		packetHeader[ 0 ] = 0x99;
		packetHeader[ 1 ] = intToByte( ( keyInfo->pubKeyDataLen >> 8 ) & 0xFF );
		packetHeader[ 2 ] = intToByte( keyInfo->pubKeyDataLen & 0xFF );	
		hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2, 
					  HASH_STATE_START );
		hashFunction( hashInfo, NULL, 0, keyInfo->pubKeyData, 
					  keyInfo->pubKeyDataLen, HASH_STATE_CONTINUE );
		}

	/* Read any associated subpacket(s), of which the only ones of real 
	   interest are the userID packet(s) */
	for( iterationCount = 0; 
		 cryptStatusOK( status ) && \
			iterationCount < FAILSAFE_ITERATIONS_MED; 
		 iterationCount++ )
		{
		status = readUserID( stream, pgpInfo, 
							 isPrimaryKey ? &hashInfo : NULL );
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
	if( cryptStatusError( status ) && status != OK_SPECIAL )
		{
		retExt( status, 
				( status, errorInfo, 
				  "Invalid PGP userID information for key packet group %d",
				  keyGroupNo ) );
		}

	/* If there's no user ID present, set a generic label */
	if( pgpInfo->lastUserID <= 0 )
		{
		pgpInfo->userID[ 0 ] = "PGP key (no user ID found)";
		pgpInfo->userIDlen[ 0 ] = 26;
		pgpInfo->lastUserID = 1;
		}

	return( CRYPT_OK );
	}
Exemplo n.º 6
0
static int readUserID( INOUT STREAM *stream, 
					   INOUT_OPT PGP_INFO *pgpInfo,
					   INOUT_OPT HASHINFO *hashInfo )
	{
	HASHINFO localHashInfo;
	long packetLength;
	int ctb, packetType = DUMMY_INIT, iterationCount, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( pgpInfo == NULL || \
			isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) );
	assert( hashInfo == NULL || \
			isWritePtr( hashInfo, sizeof( HASHINFO ) ) );

	/* Take a local copy of the hash information from the primary key
	   packet */
	if( hashInfo != NULL )
		memcpy( &localHashInfo, hashInfo, sizeof( HASHINFO ) );

	/* Skip keyring trust packets, signature packets, and any private 
	   packets (GPG uses packet type 61, which might be a DSA self-
	   signature).

	   PGP has two ways of indicating key usage, either directly via the key 
	   type (e.g. PGP_ALGO_RSA_ENCRYPT vs. PGP_ALGO_RSA_SIGN) or in a rather 
	   schizophrenic manner in signature packets by allowing the signer to 
	   specify an X.509-style key usage.  Since it can appear in both self-
	   sigs and certification signatures, the exact usage for a key is 
	   somewhat complex to determine as a certification signer could 
	   indicate that they trust the key when it's used for signing while a 
	   self-signer could indicate that the key should be used for 
	   encryption.  This appears to be a preference indication rather than a 
	   hard limit like the X.509 keyUsage and also contains other odds and 
	   ends as well such as key splitting indicators.  For now we don't make 
	   use of these flags as it's a bit difficult to figure out what's what, 
	   and in any case DSA vs. Elgamal doesn't need any further constraints 
	   since there's only one usage possible */
	for( status = CRYPT_OK, iterationCount = 0; 
		 cryptStatusOK( status ) && iterationCount < FAILSAFE_ITERATIONS_MED;
		 iterationCount++ )
		{
		/* See what we've got.  If we've run out of input or it's a non-key-
		   related packet, we're done */
		status = ctb = sPeek( stream );
		if( cryptStatusError( status ) )
			break;
		packetType = pgpGetPacketType( ctb );
		if( packetType != PGP_PACKET_TRUST && \
			packetType != PGP_PACKET_SIGNATURE && \
			packetType != PGP_PACKET_USERATTR && \
			!pgpIsReservedPacket( packetType ) )
			break;

		/* Skip the packet.  If we get an error at this point we don't 
		   immediately bail out but try and return at least a partial 
		   response */
		status = pgpReadPacketHeader( stream, &ctb, &packetLength, \
									  getMinPacketSize( packetType ) );
		if( cryptStatusError( status ) )
			break;
		status = sSkip( stream, packetLength );
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );

	/* If we've reached the end of the current collection of key packets, 
	   let the caller know that we're done.  Note that running out of input
	   is a valid condition so we don't return a fatal error code at this
	   point */
	if( cryptStatusError( status ) || packetType != PGP_PACKET_USERID )
		return( OK_SPECIAL );

	/* Record the userID (unless we're skipping the packet).  If there are 
	   more userIDs than we can record we silently ignore them.  This 
	   handles keys with weird numbers of userIDs without rejecting them 
	   just because they have, well, a weird number of userIDs */
	status = pgpReadPacketHeader( stream, &ctb, &packetLength, \
								  getMinPacketSize( packetType ) );
	if( cryptStatusError( status ) )
		return( status );
	if( pgpInfo != NULL && pgpInfo->lastUserID < MAX_PGP_USERIDS )
		{
		void *dataPtr;

		status = sMemGetDataBlock( stream, &dataPtr, packetLength );
		if( cryptStatusError( status ) )
			return( status );
		pgpInfo->userID[ pgpInfo->lastUserID ] = dataPtr;
		pgpInfo->userIDlen[ pgpInfo->lastUserID++ ] = ( int ) packetLength;
		}
	return( sSkip( stream, packetLength ) );
	}