Decode base64 string in C

Viewed 63

I need to decode a base64 string into it's standard ascii format (not the numbers). This is what I have so far:

char base_alphabet[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3', 
    '4', '5', '6', '7', '8', '9', '+', '/'
};

void decode_base64(int * size, char * str_b64, char * alphabet) {
    printf("size: %ls", size);
    int end_size = size;
    char ascii_out[end_size];
    int out_idx = 0;
    int in_idx = 0;

    while(in_idx < size){
        ascii_out[out_idx]   = str_b64[in_idx+0] << 2 | str_b64[in_idx+1] >> 4;
        ascii_out[out_idx+1] = str_b64[in_idx+1] << 4 | str_b64[in_idx+2] >> 2;
        ascii_out[out_idx+2] = str_b64[in_idx+2] << 6 | str_b64[in_idx+3];
        out_idx += 3;
        in_idx += 4;
    }   

    for(int i=0; i < end_size; i++){
        uint8_t count = ascii_out[i];
        printf("%s\n", alphabet[count]);
    }
}

(Includes/headers are all correct etc.)

I would just use a library but the microcontroller it is being programmed on has very limited memory. Using a test string of SGVsbG8gd29ybGQA which should output Hello World when decoded, I get values, that are negative and positive, and they are numbers. would the printf not print char's from the array? I am fairly new to C so any help is appreciated :)

1 Answers

Trying out your code after familiarizing myself with the "Base 64" encoding and decoding algorithm, I saw that you are missing a step in the decoding process. The program cannot directly perform the bit shifting against the characters in the encoded string. Rather, each character needs to be cross-referenced to the "base64" array and the index value of the character needs to be shifted.

Following is the tweaked code with the extra stage of acquiring the "base64" index value for each encoded character.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char uint8_t;

char base_alphabet[64] =
{
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '+', '/'
};

void decode_base64(int size, char * str_b64, char * alphabet)
{
    int end_size = size;
    char ascii_out[end_size];
    int out_idx = 0;
    int in_idx = 0;
    unsigned char a1 = 0, a2 = 0, a3 = 0, a4 = 0;   /* Work characters used to convert the four characters back to three characters */

    for (int i = 0; i < size; i++)
    {
        ascii_out[i] = '\0';
    }

    while(in_idx < size)
    {
        for (int i = 0; i < 64; i++)                /* Get the position value of the character to be used in the shifts */
        {
            if (str_b64[in_idx] == alphabet[i])
            {
                a1 = i;
            }
            if (str_b64[in_idx + 1] == alphabet[i])
            {
                a2 = i;
            }
            if (str_b64[in_idx + 2] == alphabet[i])
            {
                a3 = i;
            }
            if (str_b64[in_idx + 3] == alphabet[i])
            {
                a4 = i;
            }

        }
        ascii_out[out_idx]   = (a1 << 2) | (a2 >> 4);
        ascii_out[out_idx+1] = (a2 << 4) | (a3 >> 2);
        ascii_out[out_idx+2] = (a3 << 6) | (a4);
        out_idx += 3;
        in_idx += 4;
    }

    /*for(int i=0; i < end_size; i++)
    {
        //uint8_t count = ascii_out[i];
        printf("ASCII value: %d, Character: %c\n", ascii_out[i], ascii_out[i]);
    } */
    printf("Encoded string: %s\nDecoded string: %s\n", str_b64, ascii_out);
}

int main(int argc, char* argv[])
{
    char encoded[256];
    int l = 0;
    if (argc < 2)
    {
        strcpy(encoded, "SGVsbG8gd29ybGQA"); /* Test case */
    }
    else
    {
        strcpy(encoded, argv[1]);
    }

    l = (int)strlen(encoded);

    decode_base64(l, encoded, base_alphabet);

    return 0;
}

Testing out the revised code nets the desired decoded string.

@Una:~/C_Programs/Console/Decoder/bin/Release$ ./Decoder 
Encoded string: SGVsbG8gd29ybGQA
Decoded string: Hello world
@Una:~/C_Programs/Console/Decoder/bin/Release$ ./Decoder "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCAuLi4="
Encoded string: TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCAuLi4=
Decoded string: Man is distinguished, not only by his reason, but ....

So, the take-away here was there needed to be an intermediate lookup of the encoded characters against the "base 64" array prior to performing the bit shifting.

Give that a try.

Related