Does not rely on third-party libraries, native C code for BASE64 encoding and decoding

To encode and decode BASE64 using C language code, you can use the related functions of the third-party library OpenSSL, but when the compiled program runs, it relies on the LIBEAY32.DLL or libcrypto-3-x64.dll file. The disadvantages are obvious: the compiled program It cannot run properly on systems lacking these .DLL files. Or use static compilation and add: -static -lssl -lcrypto -lz -lpthread -lgdi32 to the compilation parameters, so that the generated exe program is portable and can run normally in other people's Windows systems, but the size of the program increases Quite a few.

I am looking for a native code that does not rely on third-party libraries and only uses C language's own libraries to encode and decode BASE64. So I found this online:

Base64 Encode and Decode in C

Author: John, publication date: November 18, 2017

This code can be compiled successfully with gcc or clang, and it seems to run without any problems.

But the author only demonstrated encoding and decoding English characters. When I tried to encode and decode Chinese characters, I found that there was no problem with the encoding, but there was a problem with the decoding, and there was an extra unrecognizable character:

And the extra characters at the end of the decoded string are different with each run.

So it was suspected that there was a problem in allocating memory when decoding the string.

The author commented on the line declaring the out_len variable in the code: +1 is to leave one more byte of space to add the "\0" terminator.

	// +1 for the NULL terminator.
	out_len = b64_decoded_size(enc)+1;

I try to remove the "+1" at the end of this line of code and it becomes: out_len = b64_decoded_size(enc);

In this way, the decoded string will be just right in the allocated memory, there will be no extra space, and there will be no strange characters at the end of the decoded string.

The complete code is as follows:

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

const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int b64invs[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58,
	59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
	6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
	21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
	29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
	43, 44, 45, 46, 47, 48, 49, 50, 51 };


size_t b64_encoded_size(size_t inlen) {
	size_t ret;

	ret = inlen;
	if (inlen % 3 != 0)
		ret += 3 - (inlen % 3);
	ret /= 3;
	ret *= 4;
	return ret;
}

char *b64_encode(const unsigned char *in, size_t len) {
	char   *out;
	size_t  elen;
	size_t  i;
	size_t  j;
	size_t  v;

	if (in == NULL || len == 0)
		return NULL;

	elen = b64_encoded_size(len);
	out  = malloc(elen+1);
	out[elen] = '\0';

	for (i=0, j=0; i<len; i+=3, j+=4) {
		v = in[i];
		v = i+1 < len ? v << 8 | in[i+1] : v << 8;
		v = i+2 < len ? v << 8 | in[i+2] : v << 8;

		out[j]   = b64chars[(v >> 18) & 0x3F];
		out[j+1] = b64chars[(v >> 12) & 0x3F];
		if (i+1 < len) {
			out[j+2] = b64chars[(v >> 6) & 0x3F];
		} else {
			out[j+2] = '=';
		}
		if (i+2 < len) {
			out[j+3] = b64chars[v & 0x3F];
		} else {
			out[j+3] = '=';
		}
	}
	return out;
}

size_t b64_decoded_size(const char *in) {
	size_t len;
	size_t ret;
	size_t i;

	if (in == NULL)
		return 0;

	len = strlen(in);
	ret = len / 4 * 3;

	for (i=len; i-->0; ) {
		if (in[i] == '=') {
			ret--;
		} else {
			break;
		}
	}
	return ret;
}

void b64_generate_decode_table(){
	int    inv[80];
	size_t i;

	memset(inv, -1, sizeof(inv));
	for (i=0; i<sizeof(b64chars)-1; i++) {
		inv[b64chars[i]-43] = i;
	}
}

int b64_isvalidchar(char c){
	if (c >= '0' && c <= '9')
		return 1;
	if (c >= 'A' && c <= 'Z')
		return 1;
	if (c >= 'a' && c <= 'z')
		return 1;
	if (c == '+' || c == '/' || c == '=')
		return 1;
	return 0;
}

int b64_decode(const char *in, unsigned char *out, size_t outlen) {
	size_t len;
	size_t i;
	size_t j;
	int    v;

	if (in == NULL || out == NULL)
		return 0;

	len = strlen(in);
	if (outlen < b64_decoded_size(in) || len % 4 != 0)
		return 0;

	for (i=0; i<len; i++) {
		if (!b64_isvalidchar(in[i])) {
			return 0;
		}
	}

	for (i=0, j=0; i<len; i+=4, j+=3) {
		v = b64invs[in[i]-43];
		v = (v << 6) | b64invs[in[i+1]-43];
		v = in[i+2]=='=' ? v << 6 : (v << 6) | b64invs[in[i+2]-43];
		v = in[i+3]=='=' ? v << 6 : (v << 6) | b64invs[in[i+3]-43];

		out[j] = (v >> 16) & 0xFF;
		if (in[i+2] != '=')
			out[j+1] = (v >> 8) & 0xFF;
		if (in[i+3] != '=')
			out[j+2] = v & 0xFF;
	}
	return 1;
}


int main(int argc, char **argv)
{
    // 自动切换至UTF-8环境输出
    if (GetConsoleOutputCP() != CP_UTF8) SetConsoleOutputCP(CP_UTF8); 

	const char *data = "一二三四五六七八九零壹贰叁肆伍陆柒捌玖0123456789";
	char       *enc;
	char       *out;
	size_t      out_len;

	printf("Original str:\t%s\n", data);

	enc = b64_encode((const unsigned char *)data, strlen(data));
	printf("BASE64 encoded:\t%s\n", enc);

	printf("dec size %s data size\n", b64_decoded_size(enc) == strlen(data) ? "==" : "!=");

	// +1 for the NULL terminator.
	// out_len = b64_decoded_size(enc)+1;
	out_len = b64_decoded_size(enc);
	out = malloc(out_len);

	if (!b64_decode(enc, (unsigned char *)out, out_len)) {
		printf("Decode Failure\n");
		return 1;
	}
	out[out_len] = '\0';

	printf("BASE64 decoded:\t%s\n", out);
	printf("data %s dec\n", strcmp(data, out) == 0 ? "==" : "!=");
	free(out);

	return 0;
}

operation result:

Original str:	一二三四五六七八九零壹贰叁肆伍陆柒捌玖0123456789
BASE64 encoded:	5LiA5LqM5LiJ5Zub5LqU5YWt5LiD5YWr5Lmd6Zu25aO56LSw5Y+B6IKG5LyN6ZmG5p+S5o2M546WMDEyMzQ1Njc4OQ==
dec size == data size
BASE64 decoded:	一二三四五六七八九零壹贰叁肆伍陆柒捌玖0123456789
data == dec

The .exe file compiled with clang is only 23.5KB and does not rely on third-party DLL files. Although the code in C language is relatively lengthy, the compiled result is small and exquisite.

What if you use the OpenSSL library to write base64 encoding and decoding?

The following is a sample code using the OpenSSL library:

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <openssl/evp.h>
#include <openssl/bio.h>

char* base64_encode(const char* input, size_t input_len)
{
    BIO *bio, *b64;
    FILE* stream;
    int encoded_size;
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, input, input_len);
    BIO_flush(bio);
    BIO_get_mem_data(bio, &stream);
    encoded_size = BIO_get_mem_data(bio, &stream);
    char* encoded = (char*)malloc(encoded_size + 1);
    memcpy(encoded, stream, encoded_size);
    encoded[encoded_size] = '\0';
    BIO_free_all(bio);
    return encoded;
}

char* base64_decode(const char* input, size_t input_len, size_t* output_len)
{
    BIO *bio, *b64;
    int decoded_size;
     char* decoded = NULL;
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new_mem_buf(input, input_len);
    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    decoded_size = (input_len * 3) / 4;
    decoded = (char*)malloc(decoded_size);
    *output_len = BIO_read(bio, decoded, input_len);
    BIO_free_all(bio);
    return decoded;
}


int main()
{
    if (GetConsoleOutputCP() != CP_UTF8) SetConsoleOutputCP(CP_UTF8); 
    const char *text1 = "一二三四五六七八九零壹贰叁肆伍陆柒捌玖0123456789";
    char *text2 = base64_encode(text1, strlen(text1));
    printf("Original str:\t%s\nBASE64 encoded:\t%s\n", text1, text2);

    size_t output_len=0;
    text2 = base64_decode(text2, strlen(text2), &output_len);
    printf("BASE64 decoded:\t%s\n", text2);
    free(text2);
    return 0;
}

clang compilation parameters plus: -lcrypto -lz

operation result:

Original str:   一二三四五六七八九零壹贰叁肆伍陆柒捌玖0123456789
BASE64 encoded: 5LiA5LqM5LiJ5Zub5LqU5YWt5LiD5YWr5Lmd6Zu25aO56LSw5Y+B6IKG5LyN6ZmG5p+S5o2M546WMDEyMzQ1Njc4OQ==
BASE64 decoded: 一二三四五六七八九零壹贰叁肆伍陆柒捌玖0123456789

The compiled executable file is about 24KB, but this is a dynamic link compilation. I pressed the F3 key in Total Commander to view the compiled executable file and clicked "DLL Dependencies". It can be seen that it depends on LIBEAY32.DLL, and this .DLL file is not All systems have it.

If static compilation is used and the compilation parameters are added: -static -lcryto -lz, the compiled executable file will grow to 233KB. 

Guess you like

Origin blog.csdn.net/Scott0902/article/details/134011444