/* * 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 int interim = 0; size_t output_size = 0; /* count of processed input bits, modulo log2(64) */ unsigned int 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; }