blob: b4dd3f8c0a1c29f998d937fbfa5eeb6e70516658 [file] [log] [blame]
Patrick Georgiac959032020-05-05 22:49:26 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Vadim Bendebury243c6142015-03-27 16:08:04 -07002
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 */
12static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMN"
13 "OPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
14
15struct 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 */
33static 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*/
71size_t b64_decode(const uint8_t *input_data,
72 size_t input_length,
73 uint8_t *output_data)
74{
75 struct buffer_descriptor bd;
Lee Leahy75b85992017-03-08 16:34:12 -080076 unsigned int interim = 0;
Vadim Bendebury243c6142015-03-27 16:08:04 -070077 size_t output_size = 0;
78 /* count of processed input bits, modulo log2(64) */
Lee Leahy75b85992017-03-08 16:34:12 -080079 unsigned int bit_count = 0;
Vadim Bendebury243c6142015-03-27 16:08:04 -070080
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
Elyes HAOUAS0bc9f0b2019-12-06 18:07:33 +010089 while (1) { /* Until input is exhausted. */
Vadim Bendebury243c6142015-03-27 16:08:04 -070090 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}