Patrick Georgi | ac95903 | 2020-05-05 22:49:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Vadim Bendebury | 243c614 | 2015-03-27 16:08:04 -0700 | [diff] [blame] | 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 | */ |
| 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 | */ |
| 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; |
Lee Leahy | 75b8599 | 2017-03-08 16:34:12 -0800 | [diff] [blame] | 76 | unsigned int interim = 0; |
Vadim Bendebury | 243c614 | 2015-03-27 16:08:04 -0700 | [diff] [blame] | 77 | size_t output_size = 0; |
| 78 | /* count of processed input bits, modulo log2(64) */ |
Lee Leahy | 75b8599 | 2017-03-08 16:34:12 -0800 | [diff] [blame] | 79 | unsigned int bit_count = 0; |
Vadim Bendebury | 243c614 | 2015-03-27 16:08:04 -0700 | [diff] [blame] | 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 | |
Elyes HAOUAS | 0bc9f0b | 2019-12-06 18:07:33 +0100 | [diff] [blame] | 89 | while (1) { /* Until input is exhausted. */ |
Vadim Bendebury | 243c614 | 2015-03-27 16:08:04 -0700 | [diff] [blame] | 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 | } |