Compare commits
	
		
			2 Commits
		
	
	
		
			2035cf0ad8
			...
			b9cd9198d7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b9cd9198d7 | |||
| 5aa063cafd | 
@@ -7,3 +7,8 @@ If it is set, the lookup table will be moved to the section specified by the def
 | 
			
		||||
``#define BASE64_LOOKUP_TABLE_SECTION ".bss"``
 | 
			
		||||
 | 
			
		||||
This project is licensed under GPLv2.
 | 
			
		||||
 | 
			
		||||
## Build Instructions
 | 
			
		||||
 | 
			
		||||
* Type ``make`` to build the static library object.
 | 
			
		||||
* Type ``make execute-base64-test`` to build and run the tests
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,56 @@
 | 
			
		||||
#ifndef _BASE64_LIB_H_
 | 
			
		||||
#define _BASE64_LIB_H_
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @defgroup base64-lib Base64 Encoding / Decoding Library
 | 
			
		||||
 * @addtogroup base64-lib
 | 
			
		||||
 * @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Encode a byte sequence to base 64
 | 
			
		||||
 * @param src The source sequence
 | 
			
		||||
 * @param dest The destination to write the base64 encoded data to.
 | 
			
		||||
 * @param src_size Source size
 | 
			
		||||
 * @param dest_size Available destination space
 | 
			
		||||
 * @param output_length Actual count of written data
 | 
			
		||||
 * @note The output is not null-terminated
 | 
			
		||||
 * @return 0 if successful, -1000 if parameter error, -1 if destination too small,
 | 
			
		||||
 */
 | 
			
		||||
int base64_encode(const char *src, char *dest, size_t src_size, size_t dest_size, size_t *output_length);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Calcualte encoded size of \p unencoded_size amount of unencoded bytes
 | 
			
		||||
 * @param unencoded_size Unencoded size
 | 
			
		||||
 * @return Encoded size
 | 
			
		||||
 */
 | 
			
		||||
size_t base64_calculate_encoded_size(size_t unencoded_size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Calculate the maximum decoded size.
 | 
			
		||||
 *
 | 
			
		||||
 * The true size may be up to 2 bytes smaller, due to possible padding.
 | 
			
		||||
 *
 | 
			
		||||
 * @param encoded_size Encoded size
 | 
			
		||||
 * @return Decoded size
 | 
			
		||||
 */
 | 
			
		||||
size_t base64_calculate_maximum_decoded_size(size_t encoded_size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Decode a base64 encoded string
 | 
			
		||||
 * @param base64_src Source data
 | 
			
		||||
 * @param dest Destination buffer
 | 
			
		||||
 * @param src_size Source size
 | 
			
		||||
 * @param dest_size Destination size
 | 
			
		||||
 * @param output_written Actual number of written bytes
 | 
			
		||||
 * @return 0 if successful, -1 if input length does not match a base64 string,
 | 
			
		||||
 * -2 if destination size is too small, -3 and -4 if a malformed character
 | 
			
		||||
 * is found.
 | 
			
		||||
 */
 | 
			
		||||
int base64_decode(const char *base64_src, char *dest, size_t src_size, size_t dest_size, size_t *output_written);
 | 
			
		||||
 | 
			
		||||
/** @} */
 | 
			
		||||
 | 
			
		||||
#endif /* _BASE64_LIB_H_ */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								src/base64-lib.c
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								src/base64-lib.c
									
									
									
									
									
								
							@@ -11,6 +11,7 @@
 | 
			
		||||
static bool base64_lib_initialized = false;
 | 
			
		||||
 | 
			
		||||
static char LOOKUP_SECTION_ATTR base64_encode_table[64];
 | 
			
		||||
static char LOOKUP_SECTION_ATTR base64_decode_table[256];
 | 
			
		||||
 | 
			
		||||
static void initialize_encode_lookup_table(void)
 | 
			
		||||
{
 | 
			
		||||
@@ -27,12 +28,23 @@ static void initialize_encode_lookup_table(void)
 | 
			
		||||
	base64_encode_table[63] = '/';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void initialize_decode_lookup_table(void)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 64; i++) {
 | 
			
		||||
		base64_decode_table[(unsigned int)base64_encode_table[i]] = (char)i;
 | 
			
		||||
	}
 | 
			
		||||
	base64_decode_table[(unsigned int)'='] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void base64_init(void)
 | 
			
		||||
{
 | 
			
		||||
	if (base64_lib_initialized)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	initialize_encode_lookup_table();
 | 
			
		||||
	initialize_decode_lookup_table();
 | 
			
		||||
 | 
			
		||||
	base64_lib_initialized = true;
 | 
			
		||||
}
 | 
			
		||||
@@ -103,3 +115,119 @@ size_t base64_calculate_maximum_decoded_size(size_t encoded_size)
 | 
			
		||||
		return 0;
 | 
			
		||||
	return encoded_size / 4 * 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This function checks, if a char is a valid character for base64 encoded data
 | 
			
		||||
 * @note This function treats the padding '=' as an invalid character
 | 
			
		||||
 */
 | 
			
		||||
static bool is_valid_base64_character(char c)
 | 
			
		||||
{
 | 
			
		||||
	bool ret;
 | 
			
		||||
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case 'A' ... 'Z': /* FALLTHRU */
 | 
			
		||||
	case 'a' ... 'z': /* FALLTHRU */
 | 
			
		||||
	case '0' ... '9': /* FALLTHRU */
 | 
			
		||||
	case '+': /* FALLTHRU */
 | 
			
		||||
	case '/':
 | 
			
		||||
		ret = true;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ret = false;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint32_t base64_chars_to_three_bytes(char a, char b, char c, char d)
 | 
			
		||||
{
 | 
			
		||||
	return	((((uint32_t)base64_decode_table[(unsigned int)a] & 0x3F) << 18) ) |
 | 
			
		||||
			((((uint32_t)base64_decode_table[(unsigned int)b] & 0x3F) << 12)) |
 | 
			
		||||
			((((uint32_t)base64_decode_table[(unsigned int)c] & 0x3F) << 6)) |
 | 
			
		||||
			((((uint32_t)base64_decode_table[(unsigned int)d] & 0x3F)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int base64_decode(const char *base64_src, char *dest, size_t src_size, size_t dest_size, size_t *output_written)
 | 
			
		||||
{
 | 
			
		||||
	size_t output_len;
 | 
			
		||||
	size_t idx;
 | 
			
		||||
	char first, second, third, fourth;
 | 
			
		||||
	uint32_t three_bytes;
 | 
			
		||||
	bool padded = false;
 | 
			
		||||
	size_t written = 0;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	if (!base64_src || !dest || !src_size || !dest_size)
 | 
			
		||||
		return -1000;
 | 
			
		||||
 | 
			
		||||
	output_len = base64_calculate_maximum_decoded_size(src_size);
 | 
			
		||||
	if (output_len == 0) {
 | 
			
		||||
		ret = -1;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	base64_init();
 | 
			
		||||
 | 
			
		||||
	/* Check paddings and correct output length */
 | 
			
		||||
	if (base64_src[src_size-1] == '=') {
 | 
			
		||||
		padded = true;
 | 
			
		||||
		output_len--;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (base64_src[src_size-2] == '=') {
 | 
			
		||||
		output_len--;
 | 
			
		||||
		padded = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dest_size < output_len) {
 | 
			
		||||
		ret = -2;
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Convert all full quartetts */
 | 
			
		||||
	for (idx = 0; idx < src_size - (padded ? 4 : 0);) {
 | 
			
		||||
		/* Due to the call to base64_calculate_maximum_decoded_size(), it is guaranteed, that src_size is
 | 
			
		||||
		 * a multiple of four characters */
 | 
			
		||||
		first = base64_src[idx++];
 | 
			
		||||
		second = base64_src[idx++];
 | 
			
		||||
		third = base64_src[idx++];
 | 
			
		||||
		fourth = base64_src[idx++];
 | 
			
		||||
		if (is_valid_base64_character(first) &&
 | 
			
		||||
		is_valid_base64_character(second) &&
 | 
			
		||||
		is_valid_base64_character(third) &&
 | 
			
		||||
		is_valid_base64_character(fourth)) {
 | 
			
		||||
			three_bytes = base64_chars_to_three_bytes(first, second, third, fourth);
 | 
			
		||||
			dest[written++] = (three_bytes >> 16) & 0xFF;
 | 
			
		||||
			dest[written++] = (three_bytes >> 8) & 0xFF;
 | 
			
		||||
			dest[written++] = (three_bytes) & 0xFF;
 | 
			
		||||
		} else {
 | 
			
		||||
			ret = -3;
 | 
			
		||||
			goto exit;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (padded) {
 | 
			
		||||
		first = base64_src[idx++];
 | 
			
		||||
		second = base64_src[idx++];
 | 
			
		||||
		third = base64_src[idx++];
 | 
			
		||||
		fourth = base64_src[idx++];
 | 
			
		||||
		if (!is_valid_base64_character(first) || !is_valid_base64_character(second)) {
 | 
			
		||||
			ret = -4;
 | 
			
		||||
			goto exit;
 | 
			
		||||
		}
 | 
			
		||||
		three_bytes = base64_chars_to_three_bytes(first, second, third, fourth);
 | 
			
		||||
		dest[written++] = (three_bytes >> 16) & 0xFF;
 | 
			
		||||
		if (third != '=')
 | 
			
		||||
			dest[written++] = (three_bytes >> 8) & 0xFF;
 | 
			
		||||
		if (fourth != '=')
 | 
			
		||||
			dest[written++] = (three_bytes) & 0xFF;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	if (output_written)
 | 
			
		||||
		*output_written = written;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
project(base64-test)
 | 
			
		||||
 | 
			
		||||
add_custom_target(test "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}" "-r compact" "-s" DEPENDS ${PROJECT_NAME})
 | 
			
		||||
add_custom_target("execute-${PROJECT_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}" DEPENDS ${PROJECT_NAME})
 | 
			
		||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/catch-framework")
 | 
			
		||||
aux_source_directory("src" TEST_SOURCES)
 | 
			
		||||
 | 
			
		||||
add_executable(${PROJECT_NAME} ${TEST_SOURCES})
 | 
			
		||||
add_executable(${PROJECT_NAME} EXCLUDE_FROM_ALL ${TEST_SOURCES})
 | 
			
		||||
target_link_libraries(${PROJECT_NAME} base64-lib)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -73,3 +73,56 @@ TEST_CASE("base64/base64_calculate_maximum_decoded_size", "[DECODE]")
 | 
			
		||||
	REQUIRE(base64_calculate_maximum_decoded_size(4) == 3);
 | 
			
		||||
	REQUIRE(base64_calculate_maximum_decoded_size(160) == 120);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("base64/base64_decode", "[DECODE]")
 | 
			
		||||
{
 | 
			
		||||
	char decoded[2048];
 | 
			
		||||
	size_t written;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = base64_decode(base64_reference.c_str(), decoded, base64_reference.length(), lorem_ipsum.length(), &written);
 | 
			
		||||
	REQUIRE(ret == 0);
 | 
			
		||||
 | 
			
		||||
	REQUIRE(written == lorem_ipsum.length());
 | 
			
		||||
 | 
			
		||||
	if (!ret) {
 | 
			
		||||
		decoded[written] = 0;
 | 
			
		||||
		REQUIRE(std::string(decoded) == lorem_ipsum);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("base64/base64_decode/error_src_to_short", "[DECODE]")
 | 
			
		||||
{
 | 
			
		||||
	char decoded[2048];
 | 
			
		||||
	size_t written;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = base64_decode(base64_reference.c_str(), decoded, base64_reference.length() -1, sizeof(decoded), &written);
 | 
			
		||||
	REQUIRE(ret == -1);
 | 
			
		||||
	REQUIRE(written == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("base64/base64_decode/error_dest_to_short", "[DECODE]")
 | 
			
		||||
{
 | 
			
		||||
	char decoded[2048];
 | 
			
		||||
	size_t written;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = base64_decode(base64_reference.c_str(), decoded, base64_reference.length(), 12, &written);
 | 
			
		||||
	REQUIRE(ret == -2);
 | 
			
		||||
	REQUIRE(written == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("base64/base64_decode/invalid_char", "[DECODE]")
 | 
			
		||||
{
 | 
			
		||||
	char decoded[2048];
 | 
			
		||||
	size_t written;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	std::string ref = base64_reference;
 | 
			
		||||
	ref[12] = '!';
 | 
			
		||||
 | 
			
		||||
	ret = base64_decode(ref.c_str(), decoded, ref.length(), 2048, &written);
 | 
			
		||||
	REQUIRE(ret == -3);
 | 
			
		||||
	REQUIRE(written == 9);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user