Web safe Base64 Encode/Decode in C

Posted on Mo 29 Oktober 2012 in Programming

A short while ago I needed to implement a little web safe base64 en/decoder and couldn't find any good small example in the width of the internet, so I decided to do my own dirty one. I hope I help somebody with this  little demonstration code...

I used Pelles C Compiler to build this program, but I am optimistic that it works on every common C Compiler, since it's quite close to the C11 standard.

#include 
#include 
#include 
#include

#define MAX_B64_PADDING 0x2
#define B64_PAD_CHAR "="

char * Base64Encode(char *input, unsigned int inputLen);
char * Base64Decode(char *input, unsigned int inputLen);
static unsigned char GetIndexByChar(unsigned char c);

static char *b64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

int main(int argc, char **argv) {

    if (argc != 2) {
        printf("Usage: %s StringToEncode\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    printf("String \"%s\" to: " ,argv[1]);
    printf("%s\n", Base64Encode(argv[1], strlen(argv[1])));

    exit(EXIT_SUCCESS);
}

/* Caller has to free the returned base64 encoded string ! */
char *
Base64Encode(char *input, unsigned int inputLen)
{
    char *encodedBuf;
    int fillBytes, i, k, base64StrLen;
    unsigned char a0, a1, a2, a3;
    /* Make sure there is no overflow. RAM is cheap :) */
    base64StrLen = inputLen + (int)(inputLen * 0.45);

    encodedBuf = calloc(base64StrLen, sizeof(char));
    if (encodedBuf == NULL) {
        printf("calloc() failed with error %d\n", errno);
        return NULL;
    }

    fillBytes = 3 - (inputLen % 3); /* Pad until dividable by 3 ! */

    k = 0;
    /* Walk in 3 byte steps*/
    for (i = 0; i < inputLen; i += 3) {

        a0 = (unsigned char)(((input[i+0] & 0xFC) >> 2));
        a1 = (unsigned char)(((input[i+0] & 0x3) << 4) + ((input[i+1] & 0xF0) >> 4));
        a2 = (unsigned char)(((input[i+1] & 0xF) << 2) + ((input[i+2] & 0xC0) >> 6));
        a3 = (unsigned char)((input[i+2] & 0x3F));

        encodedBuf[k+0] = b64alphabet[a0];
        encodedBuf[k+1] = b64alphabet[a1];
        encodedBuf[k+2] = b64alphabet[a2];
        encodedBuf[k+3] = b64alphabet[a3];

        /* Prevents buffer overflow */
        if (i + (3 - fillBytes) == inputLen) { /* Check if we pad */
            /* fill byte is either 0, 1 or 2 */
            switch (fillBytes) {
                case 0: // do nothing
                    break;
                case 1: // last encoded byte becomes pad value
                    encodedBuf[k+3] = *B64_PAD_CHAR;
                    break;
                case 2: // last two encoded bytes become pad value
                    encodedBuf[k+2] = *B64_PAD_CHAR;
                    encodedBuf[k+3] = *B64_PAD_CHAR;
                    break;
            }
        }
        k += 4;
    }
    return encodedBuf;
}

/* Caller has to free the returned decoded ascii buffer */
char * 
Base64Decode(char *input, unsigned int inputLen)
{
    char * decodedBuf;
    char a0, a1, a2, a3;
    int i, k, decodedLen;

    decodedLen = (int)(inputLen * 0.8); // 20 % less big than b64 encoded should be more than enough

    decodedBuf = calloc(decodedLen, sizeof(char));
    if (decodedBuf == NULL) {
        printf("calloc() failed with error %d\n", errno);
        return NULL;
    }

    k = 0;
    for (i = 0; i < inputLen; i += 4) {
        if ((i + 4) <= inputLen) {
            a0 = GetIndexByChar(input[i+0]);
            a1 = GetIndexByChar(input[i+1]);
            a2 = GetIndexByChar(input[i+2]);
            a3 = GetIndexByChar(input[i+3]);

            decodedBuf[k+0] = (char)((a0 << 2) + ((a1 & 0x30) >> 4));
            decodedBuf[k+1] = (char)(((a1 & 0xF) << 4) + ((a2 & 0x3C) >> 2));
            decodedBuf[k+2] = (char)(((a2 & 0x3) << 6) + (a3));

            /* Strip pad bytes. Ugly, but working solution... */
            if (a0 == 100) {
                decodedBuf[k+0] = '\0';
                break;
            } else if (a1 == 100) {
                decodedBuf[k+0] = '\0';
                break;
            } else if (a2 == 100) {
                decodedBuf[k+1] = '\0';
                break;
            } else if (a3 == 100) {
                decodedBuf[k+2] = '\0';
                break;
            }
            k += 3;
        }
    }
    return decodedBuf;
}

static unsigned char
GetIndexByChar(unsigned char c)
{
    int i;
    for (i = 0; i < 64; i++) {
        if (b64alphabet[i] == c)
            return (unsigned char)i;
    }
    return 100; /* indicates an error */
}