blob: 85f43c07474590bac9fefec3e017402b7be0cefd [file] [log] [blame]
Vadim Bendebury243c6142015-03-27 16:08:04 -07001/*
2 * Copyright (C) 2015 Google, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <b64_decode.h>
15#include <console/console.h>
16
17/*
18 * Translation Table to decode base64 ASCII stream into binary. Borrowed from
19 *
20 * http://base64.sourceforge.net/b64.c.
21 *
22 */
23static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMN"
24 "OPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
25
26struct buffer_descriptor {
27 const uint8_t *input_buffer;
28 size_t data_size;
29 size_t input_index;
30};
31
32#define isalnum(c) ((((c) >= 'a') && ((c) <= 'z')) || \
33 (((c) >= 'A') && ((c) <= 'Z')) || \
34 (((c) >= '0') && ((c) <= '9')))
35
36/*
37 * On each invocation this function returns the next valid base64 character
38 * from the encoded message, ignoring padding and line breaks.
39 *
40 * Once all input is consumed, 0 is returned on all following invocations. In
41 * case any other than expected characters is found in the encoded message, -1
42 * is returned for error.
43 */
44static int get_next_char(struct buffer_descriptor *bd)
45{
46 uint8_t c;
47
48 /*
49 * The canonical base64 encoded messages include the following
50 * characters:
51 * - '0..9A..Za..z+/' to represent 64 values
52 * - '=' for padding
53 * - '<CR><LF>' to split the message into lines.
54 */
55 while (bd->input_index < bd->data_size) {
56 c = bd->input_buffer[bd->input_index++];
57
58 switch (c) {
59 case '=':
60 case 0xa:
61 case 0xd:
62 continue;
63
64 default:
65 break;
66 }
67
68 if (!isalnum(c) && (c != '+') && (c != '/'))
69 return -1;
70
71 return c;
72 }
73
74 return 0;
75}
76
77/*
78** decode
79**
80** decode a base64 encoded stream discarding padding and line breaks.
81*/
82size_t b64_decode(const uint8_t *input_data,
83 size_t input_length,
84 uint8_t *output_data)
85{
86 struct buffer_descriptor bd;
87 unsigned interim = 0;
88 size_t output_size = 0;
89 /* count of processed input bits, modulo log2(64) */
90 unsigned bit_count = 0;
91
92 /*
93 * Keep the context on the stack to make things easier if this needs
94 * to run with CAR.
95 */
96 bd.input_buffer = input_data;
97 bd.data_size = input_length;
98 bd.input_index = 0;
99
100 while (1) { /* Until input is exausted. */
101 int v = get_next_char(&bd);
102
103 if (v < 0) {
104 printk(BIOS_ERR,
105 "Incompatible character at offset %zd.\n",
106 bd.input_index);
107 return 0;
108 }
109
110 if (!v)
111 break;
112
113 /*
114 * v is guaranteed to be in the proper range for cd64, the
115 * result is a 6 bit number.
116 */
117 v = cd64[v - 43] - 62;
118
119 if (bit_count >= 2) {
120 /*
121 * Once 6 more bits are added to the output, there is
122 * going to be at least a full byte.
123 *
124 * 'remaining_bits' is the exact number of bits which
125 * need to be added to the output to have another full
126 * byte ready.
127 */
128 int remaining_bits = 8 - bit_count;
129
130 interim <<= remaining_bits;
131 interim |= v >> (6 - remaining_bits);
132
133 /* Pass the new full byte to the output. */
134 output_data[output_size++] = interim & 0xff;
135
136 interim = v;
137 bit_count -= 2;
138 } else {
139 interim <<= 6;
140 interim |= v;
141 bit_count += 6;
142 }
143 }
144
145 return output_size;
146}