| 1 | /* |
| 2 | * Copyright 2008-2018 Aerospike, Inc. |
| 3 | * |
| 4 | * Portions may be licensed to Aerospike, Inc. under one or more contributor |
| 5 | * license agreements. |
| 6 | * |
| 7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 8 | * use this file except in compliance with the License. You may obtain a copy of |
| 9 | * the License at http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 14 | * License for the specific language governing permissions and limitations under |
| 15 | * the License. |
| 16 | */ |
| 17 | #include "citrusleaf/cf_b64.h" |
| 18 | |
| 19 | /****************************************************************************** |
| 20 | * CONSTANTS |
| 21 | ******************************************************************************/ |
| 22 | |
| 23 | static const char base64_chars[] = { |
| 24 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', |
| 25 | 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', |
| 26 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', |
| 27 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', |
| 28 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' |
| 29 | }; |
| 30 | |
| 31 | static const bool base64_valid_a[] = { |
| 32 | /*00*/ /*01*/ /*02*/ /*03*/ /*04*/ /*05*/ /*06*/ /*07*/ /*08*/ /*09*/ /*0A*/ /*0B*/ /*0C*/ /*0D*/ /*0E*/ /*0F*/ |
| 33 | /*00*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 34 | /*10*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 35 | /*20*/ false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, true, |
| 36 | /*30*/ true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, |
| 37 | /*40*/ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, |
| 38 | /*50*/ true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, |
| 39 | /*60*/ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, |
| 40 | /*70*/ true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, |
| 41 | /*80*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 42 | /*90*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 43 | /*A0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 44 | /*B0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 45 | /*C0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 46 | /*D0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 47 | /*E0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, |
| 48 | /*F0*/ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false |
| 49 | }; |
| 50 | |
| 51 | static const uint8_t base64_decode_a[] = { |
| 52 | /*00*/ /*01*/ /*02*/ /*03*/ /*04*/ /*05*/ /*06*/ /*07*/ /*08*/ /*09*/ /*0A*/ /*0B*/ /*0C*/ /*0D*/ /*0E*/ /*0F*/ |
| 53 | /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 54 | /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 55 | /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, |
| 56 | /*30*/ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, |
| 57 | /*40*/ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 58 | /*50*/ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, |
| 59 | /*60*/ 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
| 60 | /*70*/ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, |
| 61 | /*80*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 62 | /*90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 63 | /*A0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 64 | /*B0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 65 | /*C0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 66 | /*D0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 67 | /*E0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 68 | /*F0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| 69 | }; |
| 70 | |
| 71 | // Shortcuts: |
| 72 | #define EA base64_chars |
| 73 | #define VA base64_valid_a |
| 74 | #define DA base64_decode_a |
| 75 | |
| 76 | |
| 77 | /****************************************************************************** |
| 78 | * FUNCTIONS |
| 79 | ******************************************************************************/ |
| 80 | |
| 81 | // Must have allocated big enough 'out' - e.g. use cf_b64_encoded_len(in_size). |
| 82 | void |
| 83 | cf_b64_encode(const uint8_t* in, uint32_t in_size, char* out) |
| 84 | { |
| 85 | int i = 0; |
| 86 | int j = 0; |
| 87 | |
| 88 | while (in_size >= 3) { |
| 89 | uint8_t i0 = in[i]; |
| 90 | uint8_t i1 = in[i + 1]; |
| 91 | uint8_t i2 = in[i + 2]; |
| 92 | |
| 93 | out[j] = EA[ (i0 & 0xfc) >> 2]; |
| 94 | out[j + 1] = EA[((i0 & 0x03) << 4) + ((i1 & 0xf0) >> 4)]; |
| 95 | out[j + 2] = EA[((i1 & 0x0f) << 2) + ((i2 & 0xc0) >> 6)]; |
| 96 | out[j + 3] = EA[ i2 & 0x3f]; |
| 97 | |
| 98 | i += 3; |
| 99 | j += 4; |
| 100 | |
| 101 | in_size -= 3; |
| 102 | } |
| 103 | |
| 104 | if (in_size == 1) { |
| 105 | uint8_t i0 = in[i]; |
| 106 | |
| 107 | out[j] = EA[(i0 & 0xfc) >> 2]; |
| 108 | out[j + 1] = EA[(i0 & 0x03) << 4]; |
| 109 | out[j + 2] = '='; |
| 110 | out[j + 3] = '='; |
| 111 | } |
| 112 | else if (in_size == 2) { |
| 113 | uint8_t i0 = in[i]; |
| 114 | uint8_t i1 = in[i + 1]; |
| 115 | |
| 116 | out[j] = EA[ (i0 & 0xfc) >> 2]; |
| 117 | out[j + 1] = EA[((i0 & 0x03) << 4) + ((i1 & 0xf0) >> 4)]; |
| 118 | out[j + 2] = EA[ (i1 & 0x0f) << 2]; |
| 119 | out[j + 3] = '='; |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | // Must have allocated big enough 'out' - e.g. use cf_b64_decoded_buf_size(). |
| 124 | // Note that 'in_len' must be a padded encoded size - an exact multiple of 4. |
| 125 | // 'out_size' returns the decoded size after padding has been accounted for - |
| 126 | // i.e. it may be 1 or 2 less than the 'out' buffer size. Also, 'in_len' = 0 is |
| 127 | // handled, and gives 'out_size' = 0. |
| 128 | void |
| 129 | cf_b64_decode(const char* in, uint32_t in_len, uint8_t* out, uint32_t* out_size) |
| 130 | { |
| 131 | uint32_t i = 0; |
| 132 | uint32_t j = 0; |
| 133 | |
| 134 | while (i < in_len) { |
| 135 | uint8_t i0 = (uint8_t)in[i]; |
| 136 | uint8_t i1 = (uint8_t)in[i + 1]; |
| 137 | uint8_t i2 = (uint8_t)in[i + 2]; |
| 138 | uint8_t i3 = (uint8_t)in[i + 3]; |
| 139 | |
| 140 | out[j] = (DA[i0] << 2) | (DA[i1] >> 4); |
| 141 | out[j + 1] = (DA[i1] << 4) | (DA[i2] >> 2); |
| 142 | out[j + 2] = (DA[i2] << 6) | DA[i3]; |
| 143 | |
| 144 | i += 4; |
| 145 | j += 3; |
| 146 | } |
| 147 | |
| 148 | if (out_size) { |
| 149 | if (i != 0) { |
| 150 | if (in[i - 1] == '=') { |
| 151 | j--; |
| 152 | } |
| 153 | |
| 154 | if (in[i - 2] == '=') { |
| 155 | j--; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | *out_size = j; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | // Same as cf_b64_decode() but 'in' and 'out' are the same buffer. |
| 164 | void |
| 165 | cf_b64_decode_in_place(uint8_t* in_out, uint32_t in_len, uint32_t* out_size) |
| 166 | { |
| 167 | uint32_t i = 0; |
| 168 | uint32_t j = 0; |
| 169 | |
| 170 | uint32_t d = 0; |
| 171 | |
| 172 | if (out_size && in_len != 0) { |
| 173 | if (in_out[in_len - 1] == '=') { |
| 174 | d++; |
| 175 | } |
| 176 | |
| 177 | if (in_out[in_len - 2] == '=') { |
| 178 | d++; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | while (i < in_len) { |
| 183 | uint8_t i0 = in_out[i]; |
| 184 | uint8_t i1 = in_out[i + 1]; |
| 185 | uint8_t i2 = in_out[i + 2]; |
| 186 | uint8_t i3 = in_out[i + 3]; |
| 187 | |
| 188 | uint8_t b0 = (DA[i0] << 2) | (DA[i1] >> 4); |
| 189 | uint8_t b1 = (DA[i1] << 4) | (DA[i2] >> 2); |
| 190 | uint8_t b2 = (DA[i2] << 6) | DA[i3]; |
| 191 | |
| 192 | in_out[j] = b0; |
| 193 | in_out[j + 1] = b1; |
| 194 | in_out[j + 2] = b2; |
| 195 | |
| 196 | i += 4; |
| 197 | j += 3; |
| 198 | } |
| 199 | |
| 200 | if (out_size) { |
| 201 | *out_size = j - d; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // Make sure the length is ok and all characters are valid base64 characters, |
| 206 | // including any '=' padding at the end. Note that 'in_len' = 0 is considered |
| 207 | // invalid. |
| 208 | static bool |
| 209 | is_valid_encoded(const char* in, uint32_t in_len) |
| 210 | { |
| 211 | if (! in || in_len == 0 || (in_len & 3) != 0) { |
| 212 | return false; |
| 213 | } |
| 214 | |
| 215 | const uint8_t* end = (const uint8_t*)in + in_len - 2; |
| 216 | const uint8_t* read = (const uint8_t*)in; |
| 217 | |
| 218 | while (read < end) { |
| 219 | if (! VA[*read++]) { |
| 220 | return false; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | if (*read == '=') { |
| 225 | return *(read + 1) == '='; |
| 226 | } |
| 227 | |
| 228 | if (! VA[*read++]) { |
| 229 | return false; |
| 230 | } |
| 231 | |
| 232 | return *read == '=' || VA[*read]; |
| 233 | } |
| 234 | |
| 235 | // Same as cf_b64_decode() but validates input as ok to decode. |
| 236 | bool |
| 237 | cf_b64_validate_and_decode(const char* in, uint32_t in_len, uint8_t* out, |
| 238 | uint32_t* out_size) |
| 239 | { |
| 240 | if (! is_valid_encoded(in, in_len)) { |
| 241 | return false; |
| 242 | } |
| 243 | |
| 244 | cf_b64_decode(in, in_len, out, out_size); |
| 245 | |
| 246 | return true; |
| 247 | } |
| 248 | |
| 249 | // Same as cf_b64_decode_in_place() but validates input as ok to decode. |
| 250 | bool |
| 251 | cf_b64_validate_and_decode_in_place(uint8_t* in_out, uint32_t in_len, |
| 252 | uint32_t* out_size) |
| 253 | { |
| 254 | if (! is_valid_encoded((const char*)in_out, in_len)) { |
| 255 | return false; |
| 256 | } |
| 257 | |
| 258 | cf_b64_decode_in_place(in_out, in_len, out_size); |
| 259 | |
| 260 | return true; |
| 261 | } |
| 262 | |