views:

132

answers:

5

Alright, this one's been puzzling me for a bit.

the following function encodes a string into base 64

void Base64Enc(const unsigned char *src, int srclen, unsigned char *dest)
{
    static const unsigned char enc[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    unsigned char *cp;
    int i;

    cp = dest;
    for(i = 0; i < srclen; i += 3) 
    {
      *(cp++) = enc[((src[i + 0] >> 2))];
      *(cp++) = enc[((src[i + 0] << 4) & 0x30)
        | ((src[i + 1] >> 4) & 0x0f)];
      *(cp++) = enc[((src[i + 1] << 2) & 0x3c)
        | ((src[i + 2] >> 6) & 0x03)];
      *(cp++) = enc[((src[i + 2]     ) & 0x3f)];
    }
    *cp = '\0';
    while (i-- > srclen)
      *(--cp) = '=';

    return;
}

Now, on the function calling Base64Enc() I have:

unsigned char *B64Encoded;

Which is the argument I pass onto unsigned char *dest in the base 64 encoding function. I've tried different initializations from mallocs to NULL to other initialization. No matter what I do I alway get an exception and if I don't initialize it then the compiler (VS2005 C compiler) throws a warning telling me that it hasn't been initialized. If I run this code with the un-initialized variable sometimes it works and some other it doesn't. How do I initialized that pointer and pass it to the function?

+1  A: 

you need to allocate buffer big enough to contain the encoded result. Either allocate it on the stack, like this:

unsigned char B64Encoded[256]; // the number here needs to be big enough to hold all possible variations of the argument

But it is easy to cause stack buffer overflow by allocating too little space using this approach. It would be much better if you allocate it in dynamic memory:

int cbEncodedSize = srclen * 4 / 3 + 1;  // cbEncodedSize is calculated from the length of the source string
unsigned char *B64Encoded = (unsigned char*)malloc(cbEncodedSize);

Don't forget to free() the allocated buffer after you're done.

Rom
Your first example (unsigned char* B64Encoded[256];) is wrong - it should be: (unsigned char B64Encoded[256]) - You're creating an array of 256 char ptrs, not 256 chars.
Dave Rigby
yep, thank you! fixed it
Rom
+1  A: 

It looks like you would want to use something like this:

// allocate 4/3 bytes per source character, plus one for the null terminator
unsigned char *B64Encoded = malloc(srclen*4/3+1);

Base64Enc(src, srclen, B64Encoded);
1800 INFORMATION
(srclen + 1) is not enough for base64 encoding. The example above will cause buffer overrun inside the Base64Enc() function
Rom
Yeah my bad sorry
1800 INFORMATION
+1  A: 

It would help if you provided the error.

I can, with your function above, to this successfully:

int main() {
    unsigned char *B64Encoded;
    B64Encoded = (unsigned char *) malloc (1000);
    unsigned char *src = "ABC";
    Base64Enc(src, 3, B64Encoded);

}

You definitely need to malloc space for the data. You also need to malloc more space than src (1/4 more I believe).

Jamie Love
+1  A: 

A base64 encoded string has four bytes per three bytes in-data string, so if srclen is 300 bytes (or characters), the length for the base64 encoded string is 400.

Wikipedia has a brief but quite good article about it.

So, rounding up srclen to the nearest tuple of three, divided by three, times four should be exactly enough memory.

Magnus Skog
+1  A: 

I see a problem in your code in the fact that it may access the byte after the trailing null char, for instance if the string length is one char. The behavior is then undefined and may result in a thrown exception if buffer boundary checking is activated.

This may explain the message related to accessing uninitialized memory.

You should then change your code so that you handle the trailing chars separately.

int len = (scrlen/3)*3;
for( int i = 0; i < len; i += 3 )
{
  // your current code here, it is ok with this loop condition.
}

// Handle 0 bits padding if required
if( len != srclen )
{
   // add new code here
}

...

PS: Here is a wikipedia page describing Base64 encoding.

chmike