xref: /aosp_15_r20/external/coreboot/src/lib/b64_decode.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <b64_decode.h>
4 #include <console/console.h>
5 
6 /*
7  * Translation Table to decode base64 ASCII stream into binary. Borrowed from
8  *
9  * http://base64.sourceforge.net/b64.c.
10  *
11  */
12 static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMN"
13 	"OPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
14 
15 struct buffer_descriptor {
16 	const uint8_t *input_buffer;
17 	size_t data_size;
18 	size_t input_index;
19 };
20 
21 #define isalnum(c) ((((c) >= 'a') && ((c) <= 'z')) || \
22 		    (((c) >= 'A') && ((c) <= 'Z')) || \
23 		    (((c) >= '0') && ((c) <= '9')))
24 
25 /*
26  * On each invocation this function returns the next valid base64 character
27  * from the encoded message, ignoring padding and line breaks.
28  *
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.
32  */
get_next_char(struct buffer_descriptor * bd)33 static int get_next_char(struct buffer_descriptor *bd)
34 {
35 	uint8_t c;
36 
37 	/*
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.
43 	 */
44 	while (bd->input_index < bd->data_size) {
45 		c = bd->input_buffer[bd->input_index++];
46 
47 		switch (c) {
48 		case '=':
49 		case 0xa:
50 		case 0xd:
51 			continue;
52 
53 		default:
54 			break;
55 		}
56 
57 		if (!isalnum(c) && (c != '+') && (c != '/'))
58 			return -1;
59 
60 		return c;
61 	}
62 
63 	return 0;
64 }
65 
66 /*
67 ** decode
68 **
69 ** decode a base64 encoded stream discarding padding and line breaks.
70 */
b64_decode(const uint8_t * input_data,size_t input_length,uint8_t * output_data)71 size_t b64_decode(const uint8_t *input_data,
72 		  size_t input_length,
73 		  uint8_t *output_data)
74 {
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;
80 
81 	/*
82 	 * Keep the context on the stack to make things easier if this needs
83 	 * to run with CAR.
84 	 */
85 	bd.input_buffer = input_data;
86 	bd.data_size = input_length;
87 	bd.input_index = 0;
88 
89 	while (1) { /* Until input is exhausted. */
90 		int v = get_next_char(&bd);
91 
92 		if (v < 0) {
93 			printk(BIOS_ERR,
94 			       "Incompatible character at offset %zd.\n",
95 			       bd.input_index);
96 			return 0;
97 		}
98 
99 		if (!v)
100 			break;
101 
102 		/*
103 		 * v is guaranteed to be in the proper range for cd64, the
104 		 * result is a 6 bit number.
105 		 */
106 		v = cd64[v - 43] - 62;
107 
108 		if (bit_count >= 2) {
109 			/*
110 			 * Once 6 more bits are added to the output, there is
111 			 * going to be at least a full byte.
112 			 *
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.
116 			 */
117 			int remaining_bits = 8 - bit_count;
118 
119 			interim <<= remaining_bits;
120 			interim |= v >> (6 - remaining_bits);
121 
122 			/* Pass the new full byte to the output. */
123 			output_data[output_size++] = interim & 0xff;
124 
125 			interim = v;
126 			bit_count -= 2;
127 		} else {
128 			interim <<= 6;
129 			interim |= v;
130 			bit_count += 6;
131 		}
132 	}
133 
134 	return output_size;
135 }
136