coreboot/src/lib/b64_decode.c

136 lines
2.9 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#include <b64_decode.h>
#include <console/console.h>
/*
* Translation Table to decode base64 ASCII stream into binary. Borrowed from
*
* http://base64.sourceforge.net/b64.c.
*
*/
static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMN"
"OPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
struct buffer_descriptor {
const uint8_t *input_buffer;
size_t data_size;
size_t input_index;
};
#define isalnum(c) ((((c) >= 'a') && ((c) <= 'z')) || \
(((c) >= 'A') && ((c) <= 'Z')) || \
(((c) >= '0') && ((c) <= '9')))
/*
* On each invocation this function returns the next valid base64 character
* from the encoded message, ignoring padding and line breaks.
*
* Once all input is consumed, 0 is returned on all following invocations. In
* case any other than expected characters is found in the encoded message, -1
* is returned for error.
*/
static int get_next_char(struct buffer_descriptor *bd)
{
uint8_t c;
/*
* The canonical base64 encoded messages include the following
* characters:
* - '0..9A..Za..z+/' to represent 64 values
* - '=' for padding
* - '<CR><LF>' to split the message into lines.
*/
while (bd->input_index < bd->data_size) {
c = bd->input_buffer[bd->input_index++];
switch (c) {
case '=':
case 0xa:
case 0xd:
continue;
default:
break;
}
if (!isalnum(c) && (c != '+') && (c != '/'))
return -1;
return c;
}
return 0;
}
/*
** decode
**
** decode a base64 encoded stream discarding padding and line breaks.
*/
size_t b64_decode(const uint8_t *input_data,
size_t input_length,
uint8_t *output_data)
{
struct buffer_descriptor bd;
unsigned int interim = 0;
size_t output_size = 0;
/* count of processed input bits, modulo log2(64) */
unsigned int bit_count = 0;
/*
* Keep the context on the stack to make things easier if this needs
* to run with CAR.
*/
bd.input_buffer = input_data;
bd.data_size = input_length;
bd.input_index = 0;
while (1) { /* Until input is exhausted. */
int v = get_next_char(&bd);
if (v < 0) {
printk(BIOS_ERR,
"Incompatible character at offset %zd.\n",
bd.input_index);
return 0;
}
if (!v)
break;
/*
* v is guaranteed to be in the proper range for cd64, the
* result is a 6 bit number.
*/
v = cd64[v - 43] - 62;
if (bit_count >= 2) {
/*
* Once 6 more bits are added to the output, there is
* going to be at least a full byte.
*
* 'remaining_bits' is the exact number of bits which
* need to be added to the output to have another full
* byte ready.
*/
int remaining_bits = 8 - bit_count;
interim <<= remaining_bits;
interim |= v >> (6 - remaining_bits);
/* Pass the new full byte to the output. */
output_data[output_size++] = interim & 0xff;
interim = v;
bit_count -= 2;
} else {
interim <<= 6;
interim |= v;
bit_count += 6;
}
}
return output_size;
}