115 lines
2.9 KiB
C
115 lines
2.9 KiB
C
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
// See LICENSE.md for license information.
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
// toolBase64.c - base64 decoding for the JoeyLib host tools
|
|
// (toolBase64.h).
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "toolBase64.h"
|
|
|
|
#define BASE64_PAD '='
|
|
#define BASE64_NONE 64u
|
|
#define QUAD 4u
|
|
#define LOW2_MASK 0x03u
|
|
#define LOW4_MASK 0x0Fu
|
|
#define SHIFT2 2u
|
|
#define SHIFT4 4u
|
|
#define SHIFT6 6u
|
|
// Where each run of the alphabet starts: A-Z, a-z, 0-9, then + and /.
|
|
#define AT_UPPER 0u
|
|
#define AT_LOWER 26u
|
|
#define AT_DIGIT 52u
|
|
#define AT_PLUS 62u
|
|
#define AT_SLASH 63u
|
|
|
|
static uint32_t sextet(char c);
|
|
|
|
|
|
// A base64 character's six bits, or BASE64_NONE for any other character.
|
|
static uint32_t sextet(char c) {
|
|
if (c >= 'A' && c <= 'Z') {
|
|
return (uint32_t)(c - 'A') + AT_UPPER;
|
|
}
|
|
if (c >= 'a' && c <= 'z') {
|
|
return (uint32_t)(c - 'a') + AT_LOWER;
|
|
}
|
|
if (c >= '0' && c <= '9') {
|
|
return (uint32_t)(c - '0') + AT_DIGIT;
|
|
}
|
|
if (c == '+') {
|
|
return AT_PLUS;
|
|
}
|
|
if (c == '/') {
|
|
return AT_SLASH;
|
|
}
|
|
return BASE64_NONE;
|
|
}
|
|
|
|
|
|
bool toolBase64Decode(const char *text, size_t len, uint8_t **out, size_t *outLen) {
|
|
uint8_t *data;
|
|
size_t n;
|
|
size_t i;
|
|
uint32_t quadPos;
|
|
uint32_t pads;
|
|
uint32_t left;
|
|
uint32_t v;
|
|
|
|
data = (uint8_t *)malloc(len / QUAD * 3u + 3u);
|
|
if (data == NULL) {
|
|
return false;
|
|
}
|
|
n = 0u;
|
|
quadPos = 0u;
|
|
pads = 0u;
|
|
left = 0u;
|
|
for (i = 0; i < len; i++) {
|
|
if (text[i] == BASE64_PAD) {
|
|
// Padding that fills a group of two or three characters out to
|
|
// four ends it, and the data; any other is skipped.
|
|
if (quadPos >= 2u) {
|
|
pads++;
|
|
if (quadPos + pads >= QUAD) {
|
|
quadPos = 0u;
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
v = sextet(text[i]);
|
|
if (v == BASE64_NONE) {
|
|
continue;
|
|
}
|
|
pads = 0u;
|
|
switch (quadPos) {
|
|
case 0:
|
|
quadPos = 1u;
|
|
left = v;
|
|
break;
|
|
case 1:
|
|
quadPos = 2u;
|
|
data[n++] = (uint8_t)((left << SHIFT2) | (v >> SHIFT4));
|
|
left = v & LOW4_MASK;
|
|
break;
|
|
case 2:
|
|
quadPos = 3u;
|
|
data[n++] = (uint8_t)((left << SHIFT4) | (v >> SHIFT2));
|
|
left = v & LOW2_MASK;
|
|
break;
|
|
default:
|
|
quadPos = 0u;
|
|
data[n++] = (uint8_t)((left << SHIFT6) | v);
|
|
left = 0u;
|
|
break;
|
|
}
|
|
}
|
|
if (quadPos != 0u) {
|
|
free(data);
|
|
return false;
|
|
}
|
|
*out = data;
|
|
*outLen = n;
|
|
return true;
|
|
}
|