Title: Win32 base64 and DES
I'm having a problem with DES encryption combined with base64
encoding.
I've built the openssl 0.9.7i static libraries (with ssl) on
Win32 and I've done so on Mac OS X 10.4. On both the encrypt
then decrypt loop fails once the data gets to large.
I'm able to use the code fine on openssl 0.9.7b.
Noticed in the change log that a fix was made to base64, maybe
that is the problem.
--
Changes between 0.9.7b and 0.9.7c [30 Sep
2003]
*) Various fixes to base64 BIO and non blocking I/O. On
write
flushes were not handled properly if
the BIO retried. On read
data was not being buffered properly
and had various logic bugs.
This also affects blocking I/O when
the data being decoded is a
certain size.
[Steve Henson]
--
This looks like a bug to me.
-----------------------------
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/conf.h>
#undef SIZE
#define SIZE (512)
#undef BSIZE
#define BSIZE (8*1024)
#define IFDEBUG(code) code
static int MYENC_IS_INITED = 0;
void initOpenSsl()
{
if(!MYENC_IS_INITED)
{ //only
add the algorithms once
#if defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_WIN16)
|| defined(OPENSSL_SYS_WIN32)
printf("CRYPTO_malloc_init\n");
CRYPTO_malloc_init();
#endif
OpenSSL_add_all_algorithms();
//SSLeay_add_all_algorithms();
MYENC_IS_INITED = 1;
}
}
char* base64DESEncrypt (int enc, const char *pass, const char *data,
size_t *dataSize)
{
char*
ret=NULL;
static const char magic[]="Salted__";
char mbuf[sizeof magic-1];
unsigned char
*buff=NULL;
int
bsize=BSIZE;
int inl;
unsigned
char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
unsigned char
salt[PKCS5_SALT_LEN];
const char *str=pass;
char *hkey=NULL,*hiv=NULL,*hsalt =
NULL;
int
printkey=0,base64=0;
int
debug=0,nosalt=0;
const EVP_CIPHER *cipher=NULL;
EVP_CIPHER_CTX *ctx = NULL;
BIO
*in=NULL,*out=NULL,*b64=NULL,*benc=NULL,*rbio=NULL,*wbio=NULL;
int bytesWritten;
initOpenSsl();
IFDEBUG(BIO *bio_err=NULL);
#if TARGET_OS_WIN32
IFDEBUG(
if((bio_err=BIO_new(BIO_s_file())) != NULL)
BIO_set_fp(bio_err,stdout,BIO_NOCLOSE|BIO_FP_TEXT);
)
#else
IFDEBUG(
if((bio_err=BIO_new(BIO_s_file())) != NULL)
BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
)
#endif
//TARGET_OS_WIN32
cipher=EVP_get_cipherbyname("des");
if(cipher == NULL)
{
IFDEBUG(BIO_printf(bio_err,"%s is an unknown
cipher\n", "des"));
goto
end;
}
nosalt=0;
//use salt
IFDEBUG(debug=1);
base64=1;
buff=(unsigned
char *)OPENSSL_malloc(EVP_ENCODE_LENGTH(bsize));
if(buff == NULL)
{
IFDEBUG(BIO_printf(bio_err,"OPENSSL_malloc failure
%ld\n",(long)EVP_ENCODE_LENGTH(bsize)));
goto
end;
}
in=BIO_new(BIO_s_mem());
bytesWritten
= BIO_write(in, data, *dataSize);
if(BIO_flush(in) != 1)
{
IFDEBUG(BIO_printf(bio_err, "error flush in
data\n");)
goto end;
}
out=BIO_new(BIO_s_mem());
if ((in == NULL)
|| (out == NULL))
{
IFDEBUG(ERR_print_errors(bio_err));
goto
end;
}
if (debug)
{
BIO_set_callback(in,BIO_debug_callback);
BIO_set_callback(out,BIO_debug_callback);
BIO_set_callback_arg(in,bio_err);
BIO_set_callback_arg(out,bio_err);
}
rbio=in;
wbio=out;
if(base64)
{
if((b64=BIO_new(BIO_f_base64())) == NULL)
goto
end;
if
(debug)
{
BIO_set_callback(b64,BIO_debug_callback);
BIO_set_callback_arg(b64,bio_err);
}
if(enc)
wbio=BIO_push(b64,wbio);
else
rbio=BIO_push(b64,rbio);
}
if (cipher !=
NULL)
{
/*
Note that str is NULL if a key was passed on the command
*
line, so we get no salt in that case. Is this a bug?
*/
if
(str != NULL)
{
/*
Salt handling: if encrypting generate a salt and
*
write to output BIO. If decrypting read salt from
*
input BIO.
*/
unsigned char *sptr;
if(nosalt)
sptr
= NULL;
else
{
if(enc)
{
if(hsalt)
{
if(!set_hex(hsalt,salt,sizeof salt))
{
IFDEBUG(BIO_printf(bio_err, "invalid hex salt
value\n"));
goto
end;
}
}
else
if (RAND_pseudo_bytes(salt, sizeof salt) < 0)
goto
end;
/*
If -P option then don't bother writing */
if((printkey != 2)
&& (BIO_write(wbio,magic,
sizeof magic-1) != sizeof magic-1
||
BIO_write(wbio,
(char *)salt,
sizeof salt) != sizeof salt))
{
IFDEBUG(BIO_printf(bio_err,"error writing output
file\n"));
goto
end;
}
}
else
if(BIO_read(rbio,mbuf,sizeof mbuf) != sizeof mbuf
||
BIO_read(rbio,
(unsigned char *)salt,
sizeof salt) != sizeof salt)
{
IFDEBUG(BIO_printf(bio_err,"error reading input
file\n"));
goto
end;
}
else
if(memcmp(mbuf,magic,sizeof magic-1))
{
IFDEBUG(BIO_printf(bio_err,"bad magic
number\n"));
goto
end;
}
sptr
= salt;
}
EVP_BytesToKey(cipher,
EVP_md5(),
sptr,
(unsigned char *)str,
strlen(str),
1,
key,
iv);
}
if
((hiv != NULL) && !set_hex(hiv,iv,sizeof iv))
{
IFDEBUG(BIO_printf(bio_err,"invalid hex iv
value\n"));
goto
end;
}
if
((hiv == NULL) && (str == NULL))
{
/*
No IV was explicitly set and no IV was generated
*
during EVP_BytesToKey. Hence the IV is undefined,
*
making correct decryption impossible. */
IFDEBUG(BIO_printf(bio_err, "iv undefined\n"));
goto
end;
}
if
((hkey != NULL) && !set_hex(hkey,key,sizeof key))
{
IFDEBUG(BIO_printf(bio_err,"invalid hex key
value\n"));
goto
end;
}
if
((benc=BIO_new(BIO_f_cipher())) == NULL)
goto
end;
/*
Since we may be changing parameters work on the encryption
*
context rather than calling BIO_set_cipher().
*/
//old way
//BIO_set_cipher(benc,cipher,key,iv,enc);
//new way
BIO_get_cipher_ctx(benc, &ctx);
if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL,
enc))
{
IFDEBUG(BIO_printf(bio_err, "Error setting cipher
%s\n",
EVP_CIPHER_name(cipher)));
IFDEBUG(ERR_print_errors(bio_err));
goto
end;
}
if
(!EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, enc))
{
BIO_printf(bio_err, "Error setting cipher %s\n",
EVP_CIPHER_name(cipher));
ERR_print_errors(bio_err);
goto
end;
}
if(debug)
{
BIO_set_callback(benc,BIO_debug_callback);
BIO_set_callback_arg(benc,bio_err);
}
}
/* Only
encrypt/decrypt as we write the file */
if (benc != NULL)
wbio=BIO_push(benc,wbio);
for (;;)
{
inl=BIO_read(rbio,(char *)buff,bsize);
if (inl <= 0) break;
if
(BIO_write(wbio,(char *)buff,inl) != inl)
{
IFDEBUG(BIO_printf(bio_err,"error writing output
file\n"));
goto
end;
}
}
if
(!BIO_flush(wbio))
{
IFDEBUG(BIO_printf(bio_err,"bad decrypt\n"));
goto
end;
}
char *theBuf;
*dataSize = BIO_get_mem_data(wbio,
&theBuf);
char *returnBuf =
(char*)malloc((*dataSize)+1);
returnBuf[*dataSize] = NULL;
memcpy(returnBuf, theBuf,
*dataSize);
ret=returnBuf;
end:
IFDEBUG(ERR_print_errors(bio_err));
if (buff != NULL)
OPENSSL_free(buff);
if (in != NULL) BIO_free(in);
if (out != NULL) BIO_free_all(out);
if (benc != NULL)
BIO_free(benc);
if (b64 != NULL)
BIO_free(b64);
IFDEBUG(printf("base64DESEncrypt
end\n"));
return ret;
}
void doTest_helper(int size)
{
char* key =
"xxx";
char *buf =
(char*)malloc(sizeof(char)*(size+1));
int i; for(i = 0;
i < size; i++)
buf[i] = ('a'+(i % 26));
buf[size] =
(char)NULL;
size_t dataSize = strlen(buf);
char* encryptedStr = base64DESEncrypt(
myenc_ENCRYPT,
key,
buf,
&dataSize);
char* decryptedStr =
base64DESEncrypt(
myenc_DECRYPT,
key,
encryptedStr,
&dataSize);
printf("decryptedStr:\n'%s'", decryptedStr);
printf("\n\n=============================\n\n");
}
void doTest()
{
int i; for(i = 736; i < 2000; i+=10)
{
doTest_helper(i);
}
}