Add base64 encoder & decoder
I prefer to have these independent of the crypto suite that's being used.pull/100/head
parent
4705c0cfcc
commit
a02f578f9e
|
@ -0,0 +1,25 @@
|
|||
/* Copyright (c) 2023 Andri Yngvason
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BASE64_ENCODED_SIZE(x) ((((x) + 2) / 3) * 4 + 1)
|
||||
#define BASE64_DECODED_MAX_SIZE(x) ((((x) + 3) / 4) * 3)
|
||||
|
||||
void base64_encode(char* dst, const uint8_t* src, size_t src_len);
|
||||
ssize_t base64_decode(uint8_t* dst, const char* src);
|
|
@ -0,0 +1,155 @@
|
|||
/* Copyright (c) 2023 Andri Yngvason
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char base64_enc_lut[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
static const uint8_t base64_validation_lut[256] = {
|
||||
['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1,
|
||||
['E'] = 1, ['F'] = 1, ['G'] = 1, ['H'] = 1,
|
||||
['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
|
||||
['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1,
|
||||
['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1,
|
||||
['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
|
||||
['Y'] = 1, ['Z'] = 1, ['a'] = 1, ['b'] = 1,
|
||||
['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1,
|
||||
['g'] = 1, ['h'] = 1, ['i'] = 1, ['j'] = 1,
|
||||
['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1,
|
||||
['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1,
|
||||
['s'] = 1, ['t'] = 1, ['u'] = 1, ['v'] = 1,
|
||||
['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1,
|
||||
['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1,
|
||||
['4'] = 1, ['5'] = 1, ['6'] = 1, ['7'] = 1,
|
||||
['8'] = 1, ['9'] = 1, ['+'] = 1, ['/'] = 1,
|
||||
['-'] = 1, ['_'] = 1, ['='] = 1,
|
||||
};
|
||||
|
||||
static const uint8_t base64_dec_lut[256] = {
|
||||
['A'] = 0x00, ['B'] = 0x01, ['C'] = 0x02, ['D'] = 0x03,
|
||||
['E'] = 0x04, ['F'] = 0x05, ['G'] = 0x06, ['H'] = 0x07,
|
||||
['I'] = 0x08, ['J'] = 0x09, ['K'] = 0x0a, ['L'] = 0x0b,
|
||||
['M'] = 0x0c, ['N'] = 0x0d, ['O'] = 0x0e, ['P'] = 0x0f,
|
||||
['Q'] = 0x10, ['R'] = 0x11, ['S'] = 0x12, ['T'] = 0x13,
|
||||
['U'] = 0x14, ['V'] = 0x15, ['W'] = 0x16, ['X'] = 0x17,
|
||||
['Y'] = 0x18, ['Z'] = 0x19, ['a'] = 0x1a, ['b'] = 0x1b,
|
||||
['c'] = 0x1c, ['d'] = 0x1d, ['e'] = 0x1e, ['f'] = 0x1f,
|
||||
['g'] = 0x20, ['h'] = 0x21, ['i'] = 0x22, ['j'] = 0x23,
|
||||
['k'] = 0x24, ['l'] = 0x25, ['m'] = 0x26, ['n'] = 0x27,
|
||||
['o'] = 0x28, ['p'] = 0x29, ['q'] = 0x2a, ['r'] = 0x2b,
|
||||
['s'] = 0x2c, ['t'] = 0x2d, ['u'] = 0x2e, ['v'] = 0x2f,
|
||||
['w'] = 0x30, ['x'] = 0x31, ['y'] = 0x32, ['z'] = 0x33,
|
||||
['0'] = 0x34, ['1'] = 0x35, ['2'] = 0x36, ['3'] = 0x37,
|
||||
['4'] = 0x38, ['5'] = 0x39, ['6'] = 0x3a, ['7'] = 0x3b,
|
||||
['8'] = 0x3c, ['9'] = 0x3d, ['+'] = 0x3e, ['/'] = 0x3f,
|
||||
['-'] = 0x3e, ['_'] = 0x3f,
|
||||
};
|
||||
|
||||
void base64_encode(char* dst, const uint8_t* src, size_t src_len)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < src_len / 3; ++i) {
|
||||
uint32_t tmp = 0;
|
||||
tmp |= (uint32_t)src[i * 3 + 0] << 16;
|
||||
tmp |= (uint32_t)src[i * 3 + 1] << 8;
|
||||
tmp |= (uint32_t)src[i * 3 + 2];
|
||||
|
||||
dst[i * 4 + 0] = base64_enc_lut[tmp >> 18];
|
||||
dst[i * 4 + 1] = base64_enc_lut[(tmp >> 12) & 0x3f];
|
||||
dst[i * 4 + 2] = base64_enc_lut[(tmp >> 6) & 0x3f];
|
||||
dst[i * 4 + 3] = base64_enc_lut[tmp & 0x3f];
|
||||
}
|
||||
|
||||
size_t rem = src_len % 3;
|
||||
if (rem == 0) {
|
||||
dst[i * 4] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t tmp = 0;
|
||||
for (size_t r = 0; r < rem; ++r) {
|
||||
size_t s = (2 - r) * 8;
|
||||
tmp |= (uint32_t)src[i * 3 + r] << s;
|
||||
}
|
||||
|
||||
size_t di = 0;
|
||||
for (; di < rem + 1; ++di) {
|
||||
size_t s = (3 - di) * 6;
|
||||
dst[i * 4 + di] = base64_enc_lut[(tmp >> s) & 0x3f];
|
||||
}
|
||||
|
||||
for (; di < 4; ++di) {
|
||||
dst[i * 4 + di] = '=';
|
||||
}
|
||||
|
||||
dst[i * 4 + di] = '\0';
|
||||
|
||||
}
|
||||
|
||||
static bool base64_is_valid(const char* src)
|
||||
{
|
||||
for (int i = 0; src[i]; i++)
|
||||
if (!base64_validation_lut[(uint8_t)src[i]])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t base64_decode(uint8_t* dst, const char* src)
|
||||
{
|
||||
if (!base64_is_valid(src))
|
||||
return -1;
|
||||
|
||||
size_t src_len = strcspn(src, "=");
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < src_len / 4; ++i) {
|
||||
uint32_t tmp = 0;
|
||||
tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 0]] << 18;
|
||||
tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 1]] << 12;
|
||||
tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 2]] << 6;
|
||||
tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + 3]];
|
||||
|
||||
dst[i * 3 + 0] = tmp >> 16;
|
||||
dst[i * 3 + 1] = (tmp >> 8) & 0xff;
|
||||
dst[i * 3 + 2] = tmp & 0xff;
|
||||
}
|
||||
|
||||
size_t rem = src_len % 4;
|
||||
if (rem == 0)
|
||||
return i * 3;
|
||||
|
||||
size_t di = 0;
|
||||
|
||||
uint32_t tmp = 0;
|
||||
for (size_t r = 0; r < rem; ++r) {
|
||||
size_t s = (3 - r) * 6;
|
||||
tmp |= (uint32_t)base64_dec_lut[(uint8_t)src[i * 4 + r]] << s;
|
||||
}
|
||||
|
||||
for (; di < (rem * 3) / 4; ++di) {
|
||||
size_t s = (2 - di) * 8;
|
||||
dst[i * 3 + di] = (tmp >> s) & 0xff;
|
||||
}
|
||||
|
||||
return i * 3 + di;
|
||||
}
|
|
@ -10,3 +10,12 @@ pixels = executable('pixels',
|
|||
],
|
||||
)
|
||||
test('pixels', pixels)
|
||||
|
||||
base64 = executable('base64',
|
||||
[
|
||||
'test-base64.c',
|
||||
'../src/base64.c',
|
||||
],
|
||||
include_directories: inc,
|
||||
)
|
||||
test('base64', base64)
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
#include "base64.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool test_encode_0(void)
|
||||
{
|
||||
char buf[1] = {};
|
||||
base64_encode(buf, (const uint8_t*)"", 0);
|
||||
return strlen(buf) == 0;
|
||||
}
|
||||
|
||||
static bool test_encode_1(void)
|
||||
{
|
||||
static const char input[] = "a";
|
||||
char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {};
|
||||
base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1);
|
||||
return strcmp(buf, "YQ==") == 0;
|
||||
}
|
||||
|
||||
static bool test_encode_2(void)
|
||||
{
|
||||
static const char input[] = "ab";
|
||||
char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {};
|
||||
base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1);
|
||||
return strcmp(buf, "YWI=") == 0;
|
||||
}
|
||||
|
||||
static bool test_encode_3(void)
|
||||
{
|
||||
static const char input[] = "abc";
|
||||
char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {};
|
||||
base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1);
|
||||
return strcmp(buf, "YWJj") == 0;
|
||||
}
|
||||
|
||||
static bool test_encode_4(void)
|
||||
{
|
||||
static const char input[] = "abcd";
|
||||
char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {};
|
||||
base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1);
|
||||
return strcmp(buf, "YWJjZA==") == 0;
|
||||
}
|
||||
|
||||
static bool test_encode_5(void)
|
||||
{
|
||||
static const char input[] = "abcde";
|
||||
char buf[BASE64_ENCODED_SIZE(sizeof(input) - 1)] = {};
|
||||
base64_encode(buf, (const uint8_t*)input, sizeof(input) - 1);
|
||||
return strcmp(buf, "YWJjZGU=") == 0;
|
||||
}
|
||||
|
||||
static bool test_decode_0(void)
|
||||
{
|
||||
uint8_t buf[1] = {};
|
||||
ssize_t r = base64_decode(buf, "");
|
||||
return r == 0 && buf[0] == 0;
|
||||
}
|
||||
|
||||
static bool test_decode_1(void)
|
||||
{
|
||||
static const char input[] = "YQ==";
|
||||
uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {};
|
||||
ssize_t r = base64_decode(buf, input);
|
||||
return r == 1 && memcmp(buf, "a", r) == 0;
|
||||
}
|
||||
|
||||
static bool test_decode_2(void)
|
||||
{
|
||||
static const char input[] = "YWI=";
|
||||
uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {};
|
||||
ssize_t r = base64_decode(buf, input);
|
||||
return r == 2 && memcmp(buf, "ab", r) == 0;
|
||||
}
|
||||
|
||||
static bool test_decode_3(void)
|
||||
{
|
||||
static const char input[] = "YWJj";
|
||||
uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {};
|
||||
ssize_t r = base64_decode(buf, input);
|
||||
return r == 3 && memcmp(buf, "abc", r) == 0;
|
||||
}
|
||||
|
||||
static bool test_decode_4(void)
|
||||
{
|
||||
static const char input[] = "YWJjZA==";
|
||||
uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {};
|
||||
ssize_t r = base64_decode(buf, input);
|
||||
return r == 4 && memcmp(buf, "abcd", r) == 0;
|
||||
}
|
||||
|
||||
static bool test_decode_5(void)
|
||||
{
|
||||
static const char input[] = "YWJjZGU=";
|
||||
uint8_t buf[BASE64_DECODED_MAX_SIZE(sizeof(input) - 1)] = {};
|
||||
ssize_t r = base64_decode(buf, input);
|
||||
return r == 5 && memcmp(buf, "abcde", r) == 0;
|
||||
}
|
||||
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
|
||||
#define RUN_TEST(name) ({ \
|
||||
bool ok = test_ ## name(); \
|
||||
printf("[%s] %s\n", ok ? " OK " : "FAIL", XSTR(name)); \
|
||||
ok; \
|
||||
})
|
||||
|
||||
int main()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
ok &= RUN_TEST(encode_0);
|
||||
ok &= RUN_TEST(encode_1);
|
||||
ok &= RUN_TEST(encode_2);
|
||||
ok &= RUN_TEST(encode_3);
|
||||
ok &= RUN_TEST(encode_4);
|
||||
ok &= RUN_TEST(encode_5);
|
||||
|
||||
ok &= RUN_TEST(decode_0);
|
||||
ok &= RUN_TEST(decode_1);
|
||||
ok &= RUN_TEST(decode_2);
|
||||
ok &= RUN_TEST(decode_3);
|
||||
ok &= RUN_TEST(decode_4);
|
||||
ok &= RUN_TEST(decode_5);
|
||||
|
||||
return ok ? 0 : 1;
|
||||
}
|
Loading…
Reference in New Issue