pbase64.h (15876B)
1 #ifndef PBASE64_H 2 #define PBASE64_H 3 4 #ifdef __cplusplus 5 extern "C" { 6 #endif 7 8 #include <stdlib.h> 9 10 /* Guarded to allow inclusion of pstdint.h first, if stdint.h is not supported. */ 11 #ifndef UINT8_MAX 12 #include <stdint.h> 13 #endif 14 15 #define BASE64_EOK 0 16 /* 0 or mure full blocks decoded, remaining content may be parsed with fresh buffer. */ 17 #define BASE64_EMORE 1 18 /* The `src_len` argument is required when encoding. */ 19 #define BASE64_EARGS 2 20 /* Unsupported mode, or modifier not supported by mode when encoding. */ 21 #define BASE64_EMODE 3 22 /* Decoding ends at invalid tail length - either by source length or by non-alphabet symbol. */ 23 #define BASE64_ETAIL 4 24 /* Decoding ends at valid tail length but last byte has non-zero bits where it shouldn't have. */ 25 #define BASE64_EDIRTY 5 26 27 static inline const char *base64_strerror(int err); 28 29 /* All codecs are URL safe. Only Crockford allow for non-canocical decoding. */ 30 enum { 31 /* Most common base64 codec, but not url friendly. */ 32 base64_mode_rfc4648 = 0, 33 34 /* URL safe version, '+' -> '-', '/' -> '_'. */ 35 base64_mode_url = 1, 36 37 /* 38 * Skip ' ', '\r', and '\n' - we do not allow tab because common 39 * uses of base64 such as PEM do not allow tab. 40 */ 41 base64_dec_modifier_skipspace = 32, 42 43 /* Padding is excluded by default. Not allowed for zbase64. */ 44 base64_enc_modifier_padding = 128, 45 46 /* For internal use or to decide codec of mode. */ 47 base64_modifier_mask = 32 + 64 + 128, 48 }; 49 50 /* Encoded size with or without padding. */ 51 static inline size_t base64_encoded_size(size_t len, int mode); 52 53 /* 54 * Decoded size assuming no padding. 55 * If `len` does include padding, the actual size may be less 56 * when decoding, but never more. 57 */ 58 static inline size_t base64_decoded_size(size_t len); 59 60 /* 61 * `dst` must hold ceil(len * 4 / 3) bytes. 62 * `src_len` points to length of source and is updated with length of 63 * parse on both success and failure. If `dst_len` is not null 64 * it is used to store resulting output lengt withh length of decoded 65 * output on both success and failure. 66 * If `hyphen` is non-zero a hyphen is encoded every `hyphen` output bytes. 67 * `mode` selects encoding alphabet defaulting to Crockfords base64. 68 * Returns 0 on success. 69 * 70 * A terminal space can be added with `dst[dst_len++] = ' '` after the 71 * encode call. All non-alphabet can be used as terminators except the 72 * padding character '='. The following characters will work as 73 * terminator for all modes: { '\0', '\n', ' ', '\t' }. A terminator is 74 * optional when the source length is given to the decoder. Note that 75 * crockford also reserves a few extra characters for checksum but the 76 * checksum must be separate from the main buffer and is not supported 77 * by this library. 78 */ 79 static inline int base64_encode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode); 80 81 /* 82 * Decodes according to mode while ignoring encoding modifiers. 83 * `src_len` and `dst_len` are optional pointers. If `src_len` is set it 84 * must contain the length of the input, otherwise the input must be 85 * terminated with a non-alphabet character or valid padding (a single 86 * padding character is accepted) - if the src_len output is needed but 87 * not the input due to guaranteed termination, then set it to 88 * (size_t)-1. `dst_len` must contain length of output buffer if present 89 * and parse will fail with BASE64_EMORE after decoding a block multiple 90 * if dst_len is exhausted - the parse can thus be resumed after 91 * draining destination. `src_len` and `dst_len` are updated with parsed 92 * and decoded length, when present, on both success and failure. 93 * Returns 0 on success. Invalid characters are not considered errors - 94 * they simply terminate the parse, however, if the termination is not 95 * at a block multiple or a valid partial block length then BASE64_ETAIL 96 * without output holding the last full block, if any. BASE64_ETAIL is also 97 * returned if the a valid length holds non-zero unused tail bits. 98 */ 99 static inline int base64_decode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode); 100 101 static inline const char *base64_strerror(int err) 102 { 103 switch (err) { 104 case BASE64_EOK: return "ok"; 105 case BASE64_EARGS: return "invalid argument"; 106 case BASE64_EMODE: return "invalid mode"; 107 case BASE64_EMORE: return "destination full"; 108 case BASE64_ETAIL: return "invalid tail length"; 109 case BASE64_EDIRTY: return "invalid tail content"; 110 default: return "unknown error"; 111 } 112 } 113 114 static inline size_t base64_encoded_size(size_t len, int mode) 115 { 116 size_t k = len % 3; 117 size_t n = (len * 4 / 3 + 3) & ~(size_t)3; 118 int pad = mode & base64_enc_modifier_padding; 119 120 if (!pad) { 121 switch (k) { 122 case 2: 123 n -= 1; 124 break; 125 case 1: 126 n -= 2; 127 break; 128 default: 129 break; 130 } 131 } 132 return n; 133 } 134 135 static inline size_t base64_decoded_size(size_t len) 136 { 137 size_t k = len % 4; 138 size_t n = len / 4 * 3; 139 140 switch (k) { 141 case 3: 142 return n + 2; 143 case 2: 144 return n + 1; 145 case 1: /* Not valid without padding. */ 146 case 0: 147 default: 148 return n; 149 } 150 } 151 152 static inline int base64_encode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode) 153 { 154 const uint8_t *rfc4648_alphabet = (const uint8_t *) 155 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 156 const uint8_t *url_alphabet = (const uint8_t *) 157 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 158 159 const uint8_t *T; 160 uint8_t *dst_base = dst; 161 int pad = mode & base64_enc_modifier_padding; 162 size_t len = 0; 163 int ret = BASE64_EMODE; 164 165 if (!src_len) { 166 ret = BASE64_EARGS; 167 goto done; 168 } 169 len = *src_len; 170 mode = mode & ~base64_modifier_mask; 171 switch (mode) { 172 case base64_mode_rfc4648: 173 T = rfc4648_alphabet; 174 break; 175 case base64_mode_url: 176 T = url_alphabet; 177 break; 178 default: 179 /* Invalid mode. */ 180 goto done; 181 } 182 183 ret = BASE64_EOK; 184 185 /* Encodes 4 destination bytes from 3 source bytes. */ 186 while (len >= 3) { 187 dst[0] = T[((src[0] >> 2))]; 188 dst[1] = T[((src[0] << 4) & 0x30) | (src[1] >> 4)]; 189 dst[2] = T[((src[1] << 2) & 0x3c) | (src[2] >> 6)]; 190 dst[3] = T[((src[2] & 0x3f))]; 191 len -= 3; 192 dst += 4; 193 src += 3; 194 } 195 /* Encodes 8 destination bytes from 1 to 4 source bytes, if any. */ 196 switch(len) { 197 case 2: 198 dst[0] = T[((src[0] >> 2))]; 199 dst[1] = T[((src[0] << 4) & 0x30) | (src[1] >> 4)]; 200 dst[2] = T[((src[1] << 2) & 0x3c)]; 201 dst += 3; 202 if (pad) { 203 *dst++ = '='; 204 } 205 break; 206 case 1: 207 dst[0] = T[((src[0] >> 2))]; 208 dst[1] = T[((src[0] << 4) & 0x30)]; 209 dst += 2; 210 if (pad) { 211 *dst++ = '='; 212 *dst++ = '='; 213 } 214 break; 215 default: 216 pad = 0; 217 break; 218 } 219 len = 0; 220 done: 221 if (dst_len) { 222 *dst_len = (size_t)(dst - dst_base); 223 } 224 if (src_len) { 225 *src_len -= len; 226 } 227 return ret; 228 } 229 230 static inline int base64_decode(uint8_t *dst, const uint8_t *src, size_t *dst_len, size_t *src_len, int mode) 231 { 232 static const uint8_t cinvalid = 64; 233 static const uint8_t cignore = 65; 234 static const uint8_t cpadding = 66; 235 236 /* 237 * 0..63: 6-bit encoded value. 238 * 64: flags non-alphabet symbols. 239 * 65: codes for ignored symbols. 240 * 66: codes for pad symbol '='. 241 * All codecs consider padding an optional terminator and if present 242 * consumes as many pad bytes as possible up to block termination, 243 * but does not fail if a block is not full. 244 * 245 * We do not currently have any ignored characters but we might 246 * add spaces as per MIME spec, but assuming spaces only happen 247 * at block boundaries this is probalby better handled by repeated 248 * parsing. 249 */ 250 static const uint8_t base64rfc4648_decode[256] = { 251 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 252 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 253 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 254 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64, 255 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 256 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 257 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 258 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 259 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 260 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 261 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 262 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 263 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 264 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 265 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 266 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 267 }; 268 269 static const uint8_t base64url_decode[256] = { 270 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 271 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 272 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 273 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64, 274 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 275 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63, 276 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 277 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 278 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 279 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 280 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 281 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 282 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 283 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 284 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 285 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 286 }; 287 288 static const uint8_t base64rfc4648_decode_skipspace[256] = { 289 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 64, 64, 65, 64, 64, 290 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 291 65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 292 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64, 293 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 294 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 295 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 296 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 297 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 298 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 299 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 300 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 301 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 302 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 303 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 304 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 305 }; 306 307 static const uint8_t base64url_decode_skipspace[256] = { 308 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 64, 64, 65, 64, 64, 309 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 310 65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 311 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 66, 64, 64, 312 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 313 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63, 314 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 315 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 316 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 317 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 318 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 319 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 320 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 321 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 322 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 323 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 324 }; 325 326 int ret = BASE64_EOK; 327 size_t i, k; 328 uint8_t hold[4]; 329 uint8_t *dst_base = dst; 330 size_t limit = (size_t)-1; 331 size_t len = (size_t)-1, mark; 332 const uint8_t *T = base64rfc4648_decode; 333 int skipspace = mode & base64_dec_modifier_skipspace; 334 335 if (src_len) { 336 len = *src_len; 337 } 338 mark = len; 339 mode = mode & ~base64_modifier_mask; 340 switch (mode) { 341 case base64_mode_rfc4648: 342 T = skipspace ? base64rfc4648_decode_skipspace : base64rfc4648_decode; 343 break; 344 case base64_mode_url: 345 T = skipspace ? base64url_decode_skipspace : base64url_decode; 346 break; 347 default: 348 ret = BASE64_EMODE; 349 goto done; 350 } 351 352 if (dst_len && *dst_len > 0) { 353 limit = *dst_len; 354 } 355 while(limit > 0) { 356 for (i = 0; i < 4; ++i) { 357 if (len == i) { 358 k = i; 359 len -= i; 360 goto tail; 361 } 362 if ((hold[i] = T[src[i]]) >= cinvalid) { 363 if (hold[i] == cignore) { 364 ++src; 365 --len; 366 --i; 367 continue; 368 } 369 k = i; 370 /* Strip padding and ignore hyphen in padding, if present. */ 371 if (hold[i] == cpadding) { 372 ++i; 373 while (i < len && i < 8) { 374 if (T[src[i]] != cpadding && T[src[i]] != cignore) { 375 break; 376 } 377 ++i; 378 } 379 } 380 len -= i; 381 goto tail; 382 } 383 } 384 if (limit < 3) { 385 goto more; 386 } 387 dst[0] = (uint8_t)((hold[0] << 2) | (hold[1] >> 4)); 388 dst[1] = (uint8_t)((hold[1] << 4) | (hold[2] >> 2)); 389 dst[2] = (uint8_t)((hold[2] << 6) | (hold[3])); 390 dst += 3; 391 src += 4; 392 limit -= 3; 393 len -= 4; 394 mark = len; 395 } 396 done: 397 if (dst_len) { 398 *dst_len = (size_t)(dst - dst_base); 399 } 400 if (src_len) { 401 *src_len -= mark; 402 } 403 return ret; 404 405 tail: 406 switch (k) { 407 case 0: 408 break; 409 case 2: 410 if ((hold[1] << 4) & 0xff) { 411 goto dirty; 412 } 413 if (limit < 1) { 414 goto more; 415 } 416 dst[0] = (uint8_t)((hold[0] << 2) | (hold[1] >> 4)); 417 dst += 1; 418 break; 419 case 3: 420 if ((hold[2] << 6) & 0xff) { 421 goto dirty; 422 } 423 if (limit < 2) { 424 goto more; 425 } 426 dst[0] = (uint8_t)((hold[0] << 2) | (hold[1] >> 4)); 427 dst[1] = (uint8_t)((hold[1] << 4) | (hold[2] >> 2)); 428 dst += 2; 429 break; 430 default: 431 ret = BASE64_ETAIL; 432 goto done; 433 } 434 mark = len; 435 goto done; 436 dirty: 437 ret = BASE64_EDIRTY; 438 goto done; 439 more: 440 ret = BASE64_EMORE; 441 goto done; 442 } 443 444 #ifdef __cplusplus 445 } 446 #endif 447 448 #endif /* PBASE64_H */