treewide: replace GPLv2 long form headers with SPDX header
[coreboot.git] / src / lib / b64_decode.c
blobb4dd3f8c0a1c29f998d937fbfa5eeb6e70516658
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <b64_decode.h>
4 #include <console/console.h>
6 /*
7 * Translation Table to decode base64 ASCII stream into binary. Borrowed from
9 * http://base64.sourceforge.net/b64.c.
12 static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMN"
13 "OPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
15 struct buffer_descriptor {
16 const uint8_t *input_buffer;
17 size_t data_size;
18 size_t input_index;
21 #define isalnum(c) ((((c) >= 'a') && ((c) <= 'z')) || \
22 (((c) >= 'A') && ((c) <= 'Z')) || \
23 (((c) >= '0') && ((c) <= '9')))
26 * On each invocation this function returns the next valid base64 character
27 * from the encoded message, ignoring padding and line breaks.
29 * Once all input is consumed, 0 is returned on all following invocations. In
30 * case any other than expected characters is found in the encoded message, -1
31 * is returned for error.
33 static int get_next_char(struct buffer_descriptor *bd)
35 uint8_t c;
38 * The canonical base64 encoded messages include the following
39 * characters:
40 * - '0..9A..Za..z+/' to represent 64 values
41 * - '=' for padding
42 * - '<CR><LF>' to split the message into lines.
44 while (bd->input_index < bd->data_size) {
45 c = bd->input_buffer[bd->input_index++];
47 switch (c) {
48 case '=':
49 case 0xa:
50 case 0xd:
51 continue;
53 default:
54 break;
57 if (!isalnum(c) && (c != '+') && (c != '/'))
58 return -1;
60 return c;
63 return 0;
67 ** decode
69 ** decode a base64 encoded stream discarding padding and line breaks.
71 size_t b64_decode(const uint8_t *input_data,
72 size_t input_length,
73 uint8_t *output_data)
75 struct buffer_descriptor bd;
76 unsigned int interim = 0;
77 size_t output_size = 0;
78 /* count of processed input bits, modulo log2(64) */
79 unsigned int bit_count = 0;
82 * Keep the context on the stack to make things easier if this needs
83 * to run with CAR.
85 bd.input_buffer = input_data;
86 bd.data_size = input_length;
87 bd.input_index = 0;
89 while (1) { /* Until input is exhausted. */
90 int v = get_next_char(&bd);
92 if (v < 0) {
93 printk(BIOS_ERR,
94 "Incompatible character at offset %zd.\n",
95 bd.input_index);
96 return 0;
99 if (!v)
100 break;
103 * v is guaranteed to be in the proper range for cd64, the
104 * result is a 6 bit number.
106 v = cd64[v - 43] - 62;
108 if (bit_count >= 2) {
110 * Once 6 more bits are added to the output, there is
111 * going to be at least a full byte.
113 * 'remaining_bits' is the exact number of bits which
114 * need to be added to the output to have another full
115 * byte ready.
117 int remaining_bits = 8 - bit_count;
119 interim <<= remaining_bits;
120 interim |= v >> (6 - remaining_bits);
122 /* Pass the new full byte to the output. */
123 output_data[output_size++] = interim & 0xff;
125 interim = v;
126 bit_count -= 2;
127 } else {
128 interim <<= 6;
129 interim |= v;
130 bit_count += 6;
134 return output_size;