Note: openssl-dev is inappropriate, as it is meant for the
_development_ _of_ OpenSSL. Your inquiry is about the _use_ of
OpenSSL, making openssl-users much more appropriate. I'm therefore
redirecting it there.

In message on Wed, 12 Mar 2008 18:50:10 +0800, "Xu, Qiang (FXSGSC)" said:

Qiang.Xu> But how to explain that passwords like "$dlkins02",
Qiang.Xu> "$flkins02", and "$Elkins02" can be decrypted correctly?
Qiang.Xu> Only "$elkins02" is decrypted into empty string.

Sheer freaking luck!

You're doing three mistakes:

1. you're assuming the EVP routines treat your data as character
strings. That's incorrect, it treats them as binary blobs. Any of
the bytes in that blob can be zero, and apparently, you're getting
a result that starts with a zero byte.
2. you're not using the resulting length from the EVP routines. Doing
so will give you correct answer, strlen() is not guaranteed to do
that (it will only give you the correct answer if you have no zero
byte anywhere in the result).
3. you're also not checking the returned result from the EVP routines.

Qiang.Xu> To be more specific, this is the code to decrypt:
Qiang.Xu> ================================================== ==========
Qiang.Xu> bool_t esscrypto_decryptString(unsigned char *toDecrypt,
Qiang.Xu> unsigned char *passPhrase,
Qiang.Xu> int sizeOfStrToDecrypt,
Qiang.Xu> int maxDecryptedStringSize,
Qiang.Xu> int *sizeOfDecryptedString,
Qiang.Xu> unsigned char **decryptedString)
Qiang.Xu> {
Qiang.Xu> EVP_CIPHER_CTX openSSLDecryptionStructure;
Qiang.Xu> int tempOutputLength = 0;
Qiang.Xu> int i = 0;
Qiang.Xu> int tmpOutputBufferPosition = 0;
Qiang.Xu> int tmpOffset = 0;
Qiang.Xu> char *tempPassPhrase = NULL;
Qiang.Xu> int currentDecryptedStrLength = 0;
Qiang.Xu> char *tempPtr = NULL;
Qiang.Xu>
Qiang.Xu> LOGLIBENTER(__FUNCTION__);
Qiang.Xu>
Qiang.Xu> tempPtr = ess_crypto_get_hex_string_from_string(toDecrypt);
Qiang.Xu> if (tempPtr != NULL)
Qiang.Xu> {
Qiang.Xu> LOGFORCE("Decrypting string (in hex) [%s]", tempPtr);
Qiang.Xu> free(tempPtr);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* initialize return values */
Qiang.Xu> *sizeOfDecryptedString = 0;
Qiang.Xu> *decryptedString = NULL;
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * Paranoia, if we get "really" big strings to decrypt, we should reject
Qiang.Xu> * them in favor of a buffered string decryption method (like what is
Qiang.Xu> * done for files). So spit out a warning and return false.
Qiang.Xu> ******/
Qiang.Xu> if ( sizeOfStrToDecrypt > ESS_CRYPTO_MAX_TO_DECRYPT_STR_SIZE )
Qiang.Xu> {
Qiang.Xu> LOGWARN("Decrypting string of size [%d] is too big to decrypt",
Qiang.Xu> sizeOfStrToDecrypt);
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return (FALSE);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> tempPassPhrase = essCrypto_setupPassPhrase(passPhrase);
Qiang.Xu>
Qiang.Xu> if (tempPassPhrase == NULL)
Qiang.Xu> {
Qiang.Xu> LOGWARN("PassPhrase is null");
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return(FALSE);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> EVP_DecryptInit(&openSSLDecryptionStructure, EVP_des_cbc(), tempPassPhrase,
Qiang.Xu> (char *) NULL);
Qiang.Xu>
Qiang.Xu> /****
Qiang.Xu> * Since we are using DES, we don't need to set the key length always
Qiang.Xu> set to 64 bits, but if we use other ciphers, we need to do the following:
Qiang.Xu> EVP_CIPHER_CTX_set_key_length(&openSSLDecryptionStructure, );
Qiang.Xu> // re-init
Qiang.Xu> EVP_DecryptInit(&openSSLDecryptionStructure, NULL, passPhrase,
Qiang.Xu> (char *)NULL);
Qiang.Xu> ****/
Qiang.Xu> tempOutputLength = sizeOfStrToDecrypt +
Qiang.Xu> EVP_CIPHER_CTX_block_size(&openSSLDecryptionStructure) + 1;
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * If the caller put in a non-negative or zero value for the max decrypted
Qiang.Xu> * size, then the caller cares about how big of a string will be returned
Qiang.Xu> * to it. So check to make sure it can handle the size of string that will
Qiang.Xu> * be returned for decrypted data.
Qiang.Xu> ******/
Qiang.Xu> if (maxDecryptedStringSize > 0)
Qiang.Xu> {
Qiang.Xu> /*
Qiang.Xu> * If we are going to need more memory than the caller wants to use for
Qiang.Xu> * the decrypted string, spit out an error and return false (since
Qiang.Xu> * we cannot decrypt the string to the callers limitations).
Qiang.Xu> ******/
Qiang.Xu> if (tempOutputLength > maxDecryptedStringSize)
Qiang.Xu> {
Qiang.Xu> LOGERROR(
Qiang.Xu> "Cannot decrypt data, decrypted Size [%d] is going to be too big!",
Qiang.Xu> tempOutputLength);
Qiang.Xu> essCrypto_freePassPhrase(tempPassPhrase);
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return(FALSE);
Qiang.Xu> }
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* malloc memory for the decrypted string */
Qiang.Xu> *decryptedString = malloc(tempOutputLength);
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * Make sure we successfully malloced memory for the decryptedString string
Qiang.Xu> ******/
Qiang.Xu> if (*decryptedString == NULL)
Qiang.Xu> {
Qiang.Xu> LOGERROR("Unable to malloc memory for decryption");
Qiang.Xu> essCrypto_freePassPhrase(tempPassPhrase);
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return (FALSE);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* clean out the memory for the returned string */
Qiang.Xu> memset(*decryptedString, 0, tempOutputLength);
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * Loop around decrypting the string with the buffer allocated for
Qiang.Xu> * decryption till we cannot fill up the buffer with data anymore.
Qiang.Xu> ******/
Qiang.Xu> for (i = 0; i < sizeOfStrToDecrypt/ ESS_CRYPTO_CRYPT_BUFFER_SIZE; i++)
Qiang.Xu> {
Qiang.Xu> /* decrypt the contents of the buffer */
Qiang.Xu> EVP_DecryptUpdate(&openSSLDecryptionStructure,
Qiang.Xu> &((*decryptedString)[tmpOutputBufferPosition]),
Qiang.Xu> &tmpOffset,
Qiang.Xu> &toDecrypt[tmpOutputBufferPosition],
Qiang.Xu> ESS_CRYPTO_CRYPT_BUFFER_SIZE);
Qiang.Xu>
Qiang.Xu> /* Increment the position we are at in decrypting the string */
Qiang.Xu> tmpOutputBufferPosition = tmpOutputBufferPosition + tmpOffset;
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * if there is data left to decrypt that did not fit exactly within
Qiang.Xu> * the buffer, decrypt that remaining bit.
Qiang.Xu> ******/
Qiang.Xu> if ( sizeOfStrToDecrypt % ESS_CRYPTO_CRYPT_BUFFER_SIZE)
Qiang.Xu> {
Qiang.Xu> /* decrypt the contents of the buffer */
Qiang.Xu> EVP_DecryptUpdate(&openSSLDecryptionStructure,
Qiang.Xu> &((*decryptedString)[tmpOutputBufferPosition]),
Qiang.Xu> &tmpOffset,
Qiang.Xu> &toDecrypt[tmpOutputBufferPosition],
Qiang.Xu> sizeOfStrToDecrypt % ESS_CRYPTO_CRYPT_BUFFER_SIZE);
Qiang.Xu>
Qiang.Xu> /* Increment the position we are at in decrypting the string */
Qiang.Xu> tmpOutputBufferPosition = tmpOutputBufferPosition + tmpOffset;
Qiang.Xu>
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* finalize the results from decryption (check CRC) */
Qiang.Xu> EVP_DecryptFinal(&openSSLDecryptionStructure,
Qiang.Xu> &((*decryptedString)[tmpOutputBufferPosition]),
Qiang.Xu> &tmpOffset);
Qiang.Xu>
Qiang.Xu> /* set the final length of the decrypted string */
Qiang.Xu> *sizeOfDecryptedString = tmpOutputBufferPosition + tmpOffset;
Qiang.Xu>
Qiang.Xu> /* see what the length is according to strlen */
Qiang.Xu> currentDecryptedStrLength = strlen(*decryptedString);
Qiang.Xu> LOGFORCE("lenght of decrypted string is %d", currentDecryptedStrLength); //xq
Qiang.Xu>
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * If we have the room in memory, NULL terminate the string (just in case
Qiang.Xu> * it has not already been done). Even thought we null out the entire
Qiang.Xu> * string when memory was malloc'ed for it.
Qiang.Xu> ******/
Qiang.Xu> if (*sizeOfDecryptedString < tempOutputLength)
Qiang.Xu> {
Qiang.Xu> (*decryptedString)[*sizeOfDecryptedString] = (char) 0;
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * If the OpenSSL length (sizeOfDecryptedString is less than the determined
Qiang.Xu> * strlen, it means OpenSSL didn't clean up after itself and it left the
Qiang.Xu> * encryption "salt" in the string. If this is the case, remove the
Qiang.Xu> * salt from memory (since it could contain sensitive information).
Qiang.Xu> ******/
Qiang.Xu> if (*sizeOfDecryptedString < currentDecryptedStrLength)
Qiang.Xu> {
Qiang.Xu> LOGDEBUG("Cleaning up Salt in Decrypted String");
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * loop around and null out each "salt" character in the string
Qiang.Xu> ******/
Qiang.Xu> for (i = *sizeOfDecryptedString; i < currentDecryptedStrLength; i++)
Qiang.Xu> {
Qiang.Xu> (*decryptedString)[i] = (char) 0;
Qiang.Xu> }
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> LOGREALLYSECURE("Decrypted String is [%s]", *decryptedString);
Qiang.Xu>
Qiang.Xu> essCrypto_freePassPhrase(tempPassPhrase);
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return (TRUE);
Qiang.Xu> }
Qiang.Xu> ================================================== ==========
Qiang.Xu> Here, the decrypted byte array is re-arranged into a character string representing the decrypted string. As far as I can see, EVP_DecryptInit(), EVP_DecryptUpdate(), and EVP_DecryptFinal() are all library functions in OpenSSL (in crypto library?).
Qiang.Xu>
Qiang.Xu>
Qiang.Xu> The code to encrypt is:
Qiang.Xu> ================================================== ==========
Qiang.Xu> bool_t esscrypto_encryptString(unsigned char *toEncrypt,
Qiang.Xu> unsigned char *passPhrase,
Qiang.Xu> int sizeOfStrToEncrypt,
Qiang.Xu> int maxEncryptedStringSize,
Qiang.Xu> int *sizeofEncryptedString,
Qiang.Xu> unsigned char **encryptedString)
Qiang.Xu> {
Qiang.Xu> EVP_CIPHER_CTX openSSLEncryptionStructure;
Qiang.Xu> int tempOutputLength = 0;
Qiang.Xu> int i = 0;
Qiang.Xu> int tmpOutputBufferPosition = 0;
Qiang.Xu> int tmpOffset = 0;
Qiang.Xu> char *tempPassPhrase = NULL;
Qiang.Xu> char *tempStrPtr = NULL;
Qiang.Xu>
Qiang.Xu> LOGLIBENTER(__FUNCTION__);
Qiang.Xu>
Qiang.Xu> LOGFORCE("Encrypting [%s] ", toEncrypt);
Qiang.Xu>
Qiang.Xu> /* initialize return values */
Qiang.Xu> *sizeofEncryptedString = 0;
Qiang.Xu> *encryptedString = NULL;
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * Paranoia, if we get "really" big strings to encrypt, we should reject
Qiang.Xu> * them in favor of a buffered string encryption method (like what is
Qiang.Xu> * done for files). So spit out a warning and return false.
Qiang.Xu> ******/
Qiang.Xu> if ( sizeOfStrToEncrypt > ESS_CRYPTO_MAX_TO_ENCRYPT_STR_SIZE )
Qiang.Xu> {
Qiang.Xu> LOGWARN("Encrypting string of size [%d] is too big to encrypt",
Qiang.Xu> sizeOfStrToEncrypt);
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return (FALSE);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* format the password to be OpenSSL friendly */
Qiang.Xu> tempPassPhrase = essCrypto_setupPassPhrase(passPhrase);
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * If we have a null password, something is not right, spit out a warning
Qiang.Xu> * and return false.
Qiang.Xu> ******/
Qiang.Xu> if (tempPassPhrase == NULL)
Qiang.Xu> {
Qiang.Xu> LOGWARN("PassPhrase is NULL");
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return(FALSE);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* Initialize OpenSSL for DES encryption */
Qiang.Xu>
Qiang.Xu> EVP_EncryptInit(&openSSLEncryptionStructure, EVP_des_cbc(), tempPassPhrase,
Qiang.Xu> (char *)NULL);
Qiang.Xu>
Qiang.Xu> /****
Qiang.Xu> * Since we are using DES, we don't need to set the key length always
Qiang.Xu> set to 64 bits, but if we use other ciphers, we need to do the following:
Qiang.Xu> EVP_CIPHER_CTX_set_key_length(&openSSLEncryptionStructure, );
Qiang.Xu> // re-init
Qiang.Xu> EVP_EncryptInit(&openSSLEncryptionStructure, NULL, passPhrase,
Qiang.Xu> (char *)NULL);
Qiang.Xu> ****/
Qiang.Xu>
Qiang.Xu>
Qiang.Xu> /* Get potential size of the output data with a null terminator*/
Qiang.Xu> tempOutputLength = sizeOfStrToEncrypt +
Qiang.Xu> EVP_CIPHER_CTX_block_size(&openSSLEncryptionStructure) + 1;
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * If the caller put in a non-negative or zero value for the max encrypted
Qiang.Xu> * size, then the caller cares about how big of a string will be returned
Qiang.Xu> * to it. So check to make sure it can handle the size of string that will
Qiang.Xu> * be returned for encrypted data.
Qiang.Xu> ******/
Qiang.Xu> if (maxEncryptedStringSize > 0)
Qiang.Xu> {
Qiang.Xu> /*
Qiang.Xu> * If we are going to need more memory than the caller wants to use for
Qiang.Xu> * the encrypted string, spit out an error and return false (since
Qiang.Xu> * we cannot encrypt the string to the callers limitations).
Qiang.Xu> ******/
Qiang.Xu> if (tempOutputLength > maxEncryptedStringSize)
Qiang.Xu> {
Qiang.Xu> LOGERROR(
Qiang.Xu> "Cannot Encrypt data, Encrypted Size is going to be too big!");
Qiang.Xu> essCrypto_freePassPhrase(tempPassPhrase);
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return(FALSE);
Qiang.Xu> }
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* malloc memory for the encrypted string */
Qiang.Xu> *encryptedString = malloc(tempOutputLength);
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * Make sure we successfully malloced memory for the encrypted string
Qiang.Xu> ******/
Qiang.Xu> if (*encryptedString == NULL)
Qiang.Xu> {
Qiang.Xu> LOGERROR("Unable to malloc memory for encryption");
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> essCrypto_freePassPhrase(tempPassPhrase);
Qiang.Xu> return (FALSE);
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* clean out the memory for the returned string */
Qiang.Xu> memset(*encryptedString, 0, tempOutputLength);
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * Loop around encrypting the string with the buffer allocated for
Qiang.Xu> * encryption till we cannot fill up the buffer with data anymore.
Qiang.Xu> ******/
Qiang.Xu> for (i = 0; i < sizeOfStrToEncrypt/ ESS_CRYPTO_CRYPT_BUFFER_SIZE; i++)
Qiang.Xu> {
Qiang.Xu> /* encrypt the buffer contents) */
Qiang.Xu> EVP_EncryptUpdate(&openSSLEncryptionStructure,
Qiang.Xu> &((*encryptedString)[tmpOutputBufferPosition]),
Qiang.Xu> &tmpOffset,
Qiang.Xu> &toEncrypt[tmpOutputBufferPosition],
Qiang.Xu> ESS_CRYPTO_CRYPT_BUFFER_SIZE);
Qiang.Xu>
Qiang.Xu> /* Increment the position we are at in encrypting the string */
Qiang.Xu> tmpOutputBufferPosition = tmpOutputBufferPosition + tmpOffset;
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /*
Qiang.Xu> * if there is data left to encrypt that did not fit exactly within
Qiang.Xu> * the buffer, encrypt that remaining bit.
Qiang.Xu> ******/
Qiang.Xu> if ( sizeOfStrToEncrypt % ESS_CRYPTO_CRYPT_BUFFER_SIZE)
Qiang.Xu> {
Qiang.Xu> /* encrypt the buffer contents) */
Qiang.Xu> EVP_EncryptUpdate(&openSSLEncryptionStructure,
Qiang.Xu> &((*encryptedString)[tmpOutputBufferPosition]),
Qiang.Xu> &tmpOffset,
Qiang.Xu> &toEncrypt[tmpOutputBufferPosition],
Qiang.Xu> sizeOfStrToEncrypt % ESS_CRYPTO_CRYPT_BUFFER_SIZE);
Qiang.Xu>
Qiang.Xu> /* Increment the position we are at in encrypting the string */
Qiang.Xu> tmpOutputBufferPosition = tmpOutputBufferPosition + tmpOffset;
Qiang.Xu>
Qiang.Xu> }
Qiang.Xu>
Qiang.Xu> /* Finalize the encryption results (generate CRC for example) */
Qiang.Xu> EVP_EncryptFinal(&openSSLEncryptionStructure,
Qiang.Xu> &((*encryptedString)[tmpOutputBufferPosition]),
Qiang.Xu> &tmpOffset);
Qiang.Xu>
Qiang.Xu> /* set the total size of the string encrypted */
Qiang.Xu> *sizeofEncryptedString = tmpOutputBufferPosition + tmpOffset;
Qiang.Xu>
Qiang.Xu> tempStrPtr = ess_crypto_get_hex_string_from_string(*encryptedSt ring);
Qiang.Xu>
Qiang.Xu> if (tempStrPtr != NULL)
Qiang.Xu> {
Qiang.Xu> LOGFORCE("Encrypted String (in HEX) is [%s]", tempStrPtr);
Qiang.Xu> free(tempStrPtr);
Qiang.Xu> }
Qiang.Xu> essCrypto_freePassPhrase(tempPassPhrase);
Qiang.Xu>
Qiang.Xu> LOGLIBRETURN(__FUNCTION__);
Qiang.Xu> return (TRUE);
Qiang.Xu> }
Qiang.Xu> ================================================== ==========
Qiang.Xu> I am not sure whether some algorithm in these functions
Qiang.Xu> would go awry when dealing with the password "$elkins02".

They probably don't, but you need to treat them right.

Cheers,
Richard

--
Richard Levitte richard@levitte.org
http://richard.levitte.org/

"When I became a man I put away childish things, including
the fear of childishness and the desire to be very grown up."
-- C.S. Lewis
__________________________________________________ ____________________
OpenSSL Project http://www.openssl.org
User Support Mailing List openssl-users@openssl.org
Automated List Manager majordomo@openssl.org