aboutsummaryrefslogtreecommitdiff
path: root/src/lib/b64_decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/b64_decode.c')
-rw-r--r--src/lib/b64_decode.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/lib/b64_decode.c b/src/lib/b64_decode.c
new file mode 100644
index 0000000000..85f43c0747
--- /dev/null
+++ b/src/lib/b64_decode.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#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 interim = 0;
+ size_t output_size = 0;
+ /* count of processed input bits, modulo log2(64) */
+ unsigned 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 exausted. */
+ 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;
+}