1 /** 2 * Base64 Encoding and Decoding 3 * 4 * Copyright: 5 * (C) 2010 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.codec.base64; 12 import memutils.vector; 13 import botan.codec.base64; 14 import botan.utils.mem_ops; 15 import botan.utils.rounding; 16 import botan.utils.types; 17 import std.exception; 18 // import string; 19 20 /** 21 * Perform base64 encoding 22 * Params: 23 * output = an array of at least input_length*4/3 bytes 24 * input = is some binary data 25 * input_length = length of input in bytes 26 * input_consumed = is an output parameter which says how many 27 * bytes of input were actually consumed. If less than 28 * input_length, then the range input[consumed:length] 29 * should be passed in later along with more input. 30 * final_inputs = true iff this is the last input, in which case 31 padding chars will be applied if needed 32 * Returns: number of bytes written to output 33 */ 34 size_t base64Encode(char* output, 35 const(ubyte)* input, 36 size_t input_length, 37 ref size_t input_consumed, 38 bool final_inputs) 39 { 40 if (input_length == 0) return 0; 41 input_consumed = 0; 42 43 size_t input_remaining = input_length; 44 size_t output_produced = 0; 45 46 while (input_remaining >= 3) 47 { 48 doBase64Encode((output + output_produced)[0..4], (input + input_consumed)[0..3]); 49 50 input_consumed += 3; 51 output_produced += 4; 52 input_remaining -= 3; 53 } 54 55 if (final_inputs && input_remaining) 56 { 57 ubyte[3] remainder; 58 foreach (size_t i; 0 .. input_remaining) 59 remainder[i] = input[input_consumed + i]; 60 61 doBase64Encode((output + output_produced)[0..4], remainder); 62 63 size_t empty_bits = 8 * (3 - input_remaining); 64 size_t index = output_produced + 4 - 1; 65 while (empty_bits >= 8) 66 { 67 output[index--] = '='; 68 empty_bits -= 6; 69 } 70 71 input_consumed += input_remaining; 72 output_produced += 4; 73 } 74 75 return output_produced; 76 } 77 78 /** 79 * Perform base64 encoding 80 * Params: 81 * input = some input 82 * input_length = length of input in bytes 83 * Returns: base64adecimal representation of input 84 */ 85 86 string base64Encode(const(ubyte)* input, 87 size_t input_length) 88 { 89 import std.conv : to; 90 char[] output; 91 output.length = roundUp!size_t(input_length, 3) / 3 * 4; 92 93 size_t consumed = 0; 94 size_t produced = base64Encode(output.ptr, 95 input, input_length, 96 consumed, true); 97 98 assert(consumed == input_length, "Consumed the entire input"); 99 assert(produced == output.length, "Produced expected size"); 100 101 return cast(string)output; 102 } 103 104 /** 105 * Perform base64 encoding 106 * Params: 107 * input = some input 108 * Returns: base64adecimal representation of input 109 */ 110 string base64Encode(Alloc)(auto const ref Vector!( ubyte, Alloc ) input) 111 { 112 return base64Encode(input.ptr, input.length); 113 } 114 115 /** 116 * Perform base64 decoding 117 * Params: 118 * output = an array of at least input_length*3/4 bytes 119 * input = some base64 input 120 * input_length = length of input in bytes 121 * input_consumed = is an output parameter which says how many 122 * bytes of input were actually consumed. If less than 123 * input_length, then the range input[consumed:length] 124 * should be passed in later along with more input. 125 * final_inputs = true iff this is the last input, in which case 126 padding is allowed 127 * ignore_ws = ignore whitespace on input; if false, throw new an 128 exception if whitespace is encountered 129 * Returns: number of bytes written to output 130 */ 131 size_t base64Decode(ubyte* output, 132 const(char)* input, size_t input_length, 133 ref size_t input_consumed, 134 bool final_inputs, 135 bool ignore_ws = true) 136 { 137 if (input_length == 0) return 0; 138 /* 139 * Base64 Decoder Lookup Table 140 * Warning: assumes ASCII encodings 141 */ 142 __gshared immutable ubyte[256] BASE64_TO_BIN = [ 143 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 144 0x80, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 145 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 146 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 147 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, 0x34, 0x35, 148 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 149 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 150 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 151 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 152 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x1B, 0x1C, 153 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 154 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 155 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 156 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 157 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 158 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 159 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 160 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 161 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 162 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 163 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 164 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 165 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 166 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 167 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 168 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ]; 169 170 ubyte* out_ptr = output; 171 ubyte[4] decode_buf; 172 size_t decode_buf_pos = 0; 173 size_t final_truncate = 0; 174 175 clearMem(output, input_length * 3 / 4); 176 177 foreach (size_t i; 0 .. input_length) 178 { 179 const ubyte bin = BASE64_TO_BIN[cast(ubyte)(input[i])]; 180 181 if (bin <= 0x3F) 182 { 183 decode_buf[decode_buf_pos] = bin; 184 decode_buf_pos += 1; 185 } 186 else if (!(bin == 0x81 || (bin == 0x80 && ignore_ws))) 187 { 188 string bad_char; 189 if (input[i] == '\t') 190 bad_char = "\\t"; 191 else if (input[i] == '\n') 192 bad_char = "\\n"; 193 else if (input[i] == '\r') 194 bad_char = "\\r"; 195 else 196 bad_char = input[i].to!string; 197 198 throw new InvalidArgument("base64Decode: invalid base64 character (ASCII '" ~ (cast(ubyte) bad_char[0]).to!string ~ "') in " ~ cast(string)(input[0 .. input_length])); 199 } 200 201 /* 202 * If we're at the end of the input, pad with 0s and truncate 203 */ 204 if (final_inputs && (i == input_length - 1)) 205 { 206 if (decode_buf_pos) 207 { 208 foreach (size_t j; decode_buf_pos .. 4) 209 decode_buf[j] = 0; 210 final_truncate = (4 - decode_buf_pos); 211 decode_buf_pos = 4; 212 } 213 } 214 215 if (decode_buf_pos == 4) 216 { 217 out_ptr[0] = cast(ubyte) ((decode_buf[0] << 2) | (decode_buf[1] >> 4)); 218 out_ptr[1] = cast(ubyte) ((decode_buf[1] << 4) | (decode_buf[2] >> 2)); 219 out_ptr[2] = cast(ubyte) ((decode_buf[2] << 6) | decode_buf[3]); 220 221 out_ptr += 3; 222 decode_buf_pos = 0; 223 input_consumed = i+1; 224 } 225 } 226 227 while (input_consumed < input_length && BASE64_TO_BIN[cast(ubyte)(input[input_consumed])] == 0x80) 228 { 229 ++input_consumed; 230 } 231 232 size_t written = (out_ptr - output) - final_truncate; 233 234 return written; 235 } 236 237 238 /** 239 * Perform base64 decoding 240 * Params: 241 * output = an array of at least input_length*3/4 bytes 242 * input = some base64 input 243 * input_length = length of input in bytes 244 * ignore_ws = ignore whitespace on input; if false, throw new an 245 exception if whitespace is encountered 246 * Returns: number of bytes written to output 247 */ 248 size_t base64Decode(ubyte* output, const(char)* input, size_t input_length, bool ignore_ws = true) 249 { 250 size_t consumed = 0; 251 size_t written = base64Decode(output, input, input_length, consumed, true, ignore_ws); 252 253 if (consumed != input_length) 254 throw new InvalidArgument("base64Decode: input did not have full bytes"); 255 256 return written; 257 } 258 259 /** 260 * Perform base64 decoding 261 * Params: 262 * output = an array of at least input_length/3*4 bytes 263 * input = some base64 input 264 * ignore_ws = ignore whitespace on input; if false, throw new an 265 exception if whitespace is encountered 266 * Returns: number of bytes written to output 267 */ 268 size_t base64Decode(ubyte* output, in string input, bool ignore_ws = true) 269 { 270 return base64Decode(output, input, ignore_ws); 271 } 272 273 274 /** 275 * Perform base64 decoding 276 * Params: 277 * input = some base64 input string 278 * ignore_ws = ignore whitespace on input; if false, throw new an 279 exception if whitespace is encountered 280 * Returns: decoded base64 output 281 */ 282 SecureVector!ubyte base64Decode(in string input, bool ignore_ws = true) 283 { 284 SecureVector!ubyte bin; 285 bin.resize((roundUp!size_t(input.length, 4) * 3) / 4); 286 287 size_t written = base64Decode(bin.ptr, input.ptr, input.length, ignore_ws); 288 bin.resize(written); 289 return bin; 290 } 291 292 293 /** 294 * Perform base64 decoding 295 * Params: 296 * input = some base64 input 297 * ignore_ws = ignore whitespace on input; if false, throw new an 298 exception if whitespace is encountered 299 * Returns: decoded base64 output 300 */ 301 SecureVector!ubyte base64Decode(const ref Vector!char input, bool ignore_ws = true) 302 { 303 return base64Decode(cast(string)input[], ignore_ws); 304 } 305 306 307 308 package: 309 310 __gshared immutable ubyte[64] BIN_TO_BASE64 = [ 311 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 312 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 313 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 314 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 315 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' 316 ]; 317 318 void doBase64Encode(ref char[4] output, in ubyte[3] input) 319 { 320 output[0] = BIN_TO_BASE64[((input[0] & 0xFC) >> 2)]; 321 output[1] = BIN_TO_BASE64[((input[0] & 0x03) << 4) | (input[1] >> 4)]; 322 output[2] = BIN_TO_BASE64[((input[1] & 0x0F) << 2) | (input[2] >> 6)]; 323 output[3] = BIN_TO_BASE64[((input[2] & 0x3F) )]; 324 }