>       From: owner-openssl-us...@openssl.org On Behalf Of Vinay Kumar L
>       Sent: Tuesday, 01 March, 2011 23:42

>       Thanks for your reply, but OpenSSL Base64 decoding api returns NULL 
> on passing Base64 encoded data. The code snippet is as follows:

I very much doubt it returns NULL. NULL in C is a null POINTER 
(pedantically, a null pointer CONSTANT). Your code will return 
a string of zero characters, i.e. an empty string.
In general this is sometimes called a null STRING but in C 
that's confusing because a null string is not a null pointer.
(And FWIW in SQL a NULL value is not an empty string either,
although some user tools will DISPLAY it as such.)

Also, the byte that terminates a C (narrow) string is a null 
character or null byte, sometimes called NUL (note 3 letters).
But this character is not IN the string, it is AFTER the string.
        
>       int main(int argc, char **argv)
>       {
>               char *output = unbase64("dGVzdGVuY29kaW5nCg==",
strlen("dGVzdGVuY29kaW5nCg=="));

Your real problem is that the ENCODING should have terminating newline.
See below. (The encoded/decoded DATA can include a newline or not, 
which as you have already seen changes the encoding, but often 
isn't even characters so the concept of newline doesn't apply.)

>               printf("Unbase64: %s\n", output);
>               free(output);
>       }

In C99 or C++ you must have at least a declaration of the function 
before the call. (You can have the definition which is also a 
declaration, by arranging your code 'bottom up'.)

But in C89, the implicit declaration is 'returns int'. You should 
be unable to assign it to a char* without at least a warning.
And even if you add a cast, it still won't work on some systems 
because the calling sequence is actually different. (Pedantically, 
initialization isn't assignment but it's sufficiently like.)

In C (both C89 and C99) you can choose whether to use a prototype 
declaration (with parameter types) or a nonprototype aka "K&R1"
declaration without. Prototypes are Better(tm).

>       char *unbase64(unsigned char *input, int length)

With a nonprototype (including implicit) declaration this is wrong.
strlen() returns size_t, not int. On SOME systems size_t and int are 
actually the same size (and passed compatibly) and this 'accidentally' 
works. On some systems it doesn't work at all. (With a prototype 
it will be converted as long as the value is in range. If you have 
data long enough its length fits in size_t but not int, use size_t.)

Technically unsigned-char* is not the same type as plain-char*, 
which is the value of the string literal above -- even on systems 
where plain-char is unsigned. In practice this will always work. 
However, since (valid) b64 data is always in a limited character set 
that is a subset of the 'basic execution' set, it usually makes sense 
to store it in array of plain-char, and pass it as pointer to same.

OTOH the data encoded into and decoded from b64 is often binary 
(although your example isn't) so in general treating it as 
array of and pointer to unsigned-char is usually better.

>       {
>               BIO *b64, *bmem;
>       
>               char *buffer = (char *)malloc(length);

Should check for failed allocation (returned null pointer) before using,
but I'll assume this is only test/example code. The cast is not needed 
in C if you have #include'd <stdlib.h> as required; without that correct 
declaration the cast doesn't actually solve the problem on some systems, 
it just silences the warning because you lied to the compiler.

In C++ the cast is needed if you use malloc but you shouldn't use malloc.

>               memset(buffer, 0, length);

Don't need this if you add just one null terminator in the right place.
If you actually do need zero-fill, calloc() may be less inefficient.


>               b64 = BIO_new(BIO_f_base64());
>               bmem = BIO_new_mem_buf(input, length);
>               bmem = BIO_push(b64, bmem);
>               BIO_read(bmem, buffer, length);

You should use the return value of BIO_read.
For the data above, the return value is zero, because by default 
b64BIO requires input to have the line terminators specified 
by PEM (always at the end, plus in the middle if 'too long') 
and similarly inserts them on output (which you don't have here).
You can change this with 
  BIO_set_flags (b64,BIO_FLAGS_BASE64_NO_NL)

When successful, the return value is the number of bytes decoded, 
which is convenient for a number of things; in your case you want 
to treat the data as a null-terminated string, so that's the 
right place to insert a single null-character terminator.

>               BIO_free_all(bmem);
>               return buffer;
>       }
        


______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    openssl-users@openssl.org
Automated List Manager                           majord...@openssl.org

Reply via email to