base64.c (6247B)
1 #include <assert.h> 2 #include <stddef.h> 3 #include <stdint.h> 4 #include <string.h> 5 6 #include "auto/config.h" // IWYU pragma: keep 7 #include "nvim/ascii_defs.h" 8 #include "nvim/base64.h" 9 #include "nvim/memory.h" 10 11 #ifdef HAVE_BE64TOH 12 # include ENDIAN_INCLUDE_FILE 13 #endif 14 15 #include "base64.c.generated.h" 16 17 static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 18 19 // Indices are 1-based because we use 0 to indicate a letter that is not part of the alphabet 20 static const uint8_t char_to_index[256] = { 21 ['A'] = 1, ['B'] = 2, ['C'] = 3, ['D'] = 4, ['E'] = 5, ['F'] = 6, ['G'] = 7, ['H'] = 8, 22 ['I'] = 9, ['J'] = 10, ['K'] = 11, ['L'] = 12, ['M'] = 13, ['N'] = 14, ['O'] = 15, ['P'] = 16, 23 ['Q'] = 17, ['R'] = 18, ['S'] = 19, ['T'] = 20, ['U'] = 21, ['V'] = 22, ['W'] = 23, ['X'] = 24, 24 ['Y'] = 25, ['Z'] = 26, ['a'] = 27, ['b'] = 28, ['c'] = 29, ['d'] = 30, ['e'] = 31, ['f'] = 32, 25 ['g'] = 33, ['h'] = 34, ['i'] = 35, ['j'] = 36, ['k'] = 37, ['l'] = 38, ['m'] = 39, ['n'] = 40, 26 ['o'] = 41, ['p'] = 42, ['q'] = 43, ['r'] = 44, ['s'] = 45, ['t'] = 46, ['u'] = 47, ['v'] = 48, 27 ['w'] = 49, ['x'] = 50, ['y'] = 51, ['z'] = 52, ['0'] = 53, ['1'] = 54, ['2'] = 55, ['3'] = 56, 28 ['4'] = 57, ['5'] = 58, ['6'] = 59, ['7'] = 60, ['8'] = 61, ['9'] = 62, ['+'] = 63, ['/'] = 64, 29 }; 30 31 #ifndef HAVE_BE64TOH 32 static inline uint64_t vim_htobe64(uint64_t host_64bits) 33 { 34 # ifdef ORDER_BIG_ENDIAN 35 return host_64bits; 36 # else 37 uint8_t *buf = (uint8_t *)&host_64bits; 38 uint64_t ret = 0; 39 for (size_t i = 8; i; i--) { 40 ret |= ((uint64_t)buf[i - 1]) << ((8 - i) * 8); 41 } 42 return ret; 43 # endif 44 } 45 46 static inline uint32_t vim_htobe32(uint32_t host_32bits) 47 { 48 # ifdef ORDER_BIG_ENDIAN 49 return host_32bits; 50 # else 51 uint8_t *buf = (uint8_t *)&host_32bits; 52 uint32_t ret = 0; 53 for (size_t i = 4; i; i--) { 54 ret |= ((uint32_t)buf[i - 1]) << ((4 - i) * 8); 55 } 56 return ret; 57 # endif 58 } 59 # define htobe64 vim_htobe64 60 # define htobe32 vim_htobe32 61 #endif 62 63 /// Encode a string using Base64. 64 /// 65 /// @param src String to encode 66 /// @param src_len Length of the string 67 /// @return Base64 encoded string 68 char *base64_encode(const char *src, size_t src_len) 69 FUNC_ATTR_NONNULL_ALL 70 { 71 assert(src != NULL); 72 73 const size_t out_len = ((src_len + 2) / 3) * 4; 74 char *dest = xmalloc(out_len + 1); 75 76 size_t src_i = 0; 77 size_t out_i = 0; 78 79 const uint8_t *s = (const uint8_t *)src; 80 81 // Read 8 bytes at a time as much as we can 82 for (; src_i + 7 < src_len; src_i += 6) { 83 uint64_t bits_h; 84 memcpy(&bits_h, &s[src_i], sizeof(uint64_t)); 85 const uint64_t bits_be = htobe64(bits_h); 86 dest[out_i + 0] = alphabet[(bits_be >> 58) & 0x3F]; 87 dest[out_i + 1] = alphabet[(bits_be >> 52) & 0x3F]; 88 dest[out_i + 2] = alphabet[(bits_be >> 46) & 0x3F]; 89 dest[out_i + 3] = alphabet[(bits_be >> 40) & 0x3F]; 90 dest[out_i + 4] = alphabet[(bits_be >> 34) & 0x3F]; 91 dest[out_i + 5] = alphabet[(bits_be >> 28) & 0x3F]; 92 dest[out_i + 6] = alphabet[(bits_be >> 22) & 0x3F]; 93 dest[out_i + 7] = alphabet[(bits_be >> 16) & 0x3F]; 94 out_i += sizeof(uint64_t); 95 } 96 97 for (; src_i + 3 < src_len; src_i += 3) { 98 uint32_t bits_h; 99 memcpy(&bits_h, &s[src_i], sizeof(uint32_t)); 100 const uint32_t bits_be = htobe32(bits_h); 101 dest[out_i + 0] = alphabet[(bits_be >> 26) & 0x3F]; 102 dest[out_i + 1] = alphabet[(bits_be >> 20) & 0x3F]; 103 dest[out_i + 2] = alphabet[(bits_be >> 14) & 0x3F]; 104 dest[out_i + 3] = alphabet[(bits_be >> 8) & 0x3F]; 105 out_i += sizeof(uint32_t); 106 } 107 108 if (src_i + 2 < src_len) { 109 dest[out_i + 0] = alphabet[s[src_i] >> 2]; 110 dest[out_i + 1] = alphabet[((s[src_i] & 0x3) << 4) | (s[src_i + 1] >> 4)]; 111 dest[out_i + 2] = alphabet[(s[src_i + 1] & 0xF) << 2 | (s[src_i + 2] >> 6)]; 112 dest[out_i + 3] = alphabet[(s[src_i + 2] & 0x3F)]; 113 out_i += 4; 114 } else if (src_i + 1 < src_len) { 115 dest[out_i + 0] = alphabet[s[src_i] >> 2]; 116 dest[out_i + 1] = alphabet[((s[src_i] & 0x3) << 4) | (s[src_i + 1] >> 4)]; 117 dest[out_i + 2] = alphabet[(s[src_i + 1] & 0xF) << 2]; 118 out_i += 3; 119 } else if (src_i < src_len) { 120 dest[out_i + 0] = alphabet[s[src_i] >> 2]; 121 dest[out_i + 1] = alphabet[(s[src_i] & 0x3) << 4]; 122 out_i += 2; 123 } 124 125 for (; out_i < out_len; out_i++) { 126 dest[out_i] = '='; 127 } 128 129 dest[out_len] = NUL; 130 131 return dest; 132 } 133 134 /// Decode a Base64 encoded string. 135 /// 136 /// The returned string is NOT null-terminated, because the decoded string may 137 /// contain embedded NULLs. Use the output parameter out_lenp to determine the 138 /// length of the returned string. 139 /// 140 /// @param src Base64 encoded string 141 /// @param src_len Length of {src} 142 /// @param [out] out_lenp Returns the length of the decoded string 143 /// @return Decoded string 144 char *base64_decode(const char *src, size_t src_len, size_t *out_lenp) 145 FUNC_ATTR_NONNULL_ALL 146 { 147 assert(src != NULL); 148 assert(out_lenp != NULL); 149 150 char *dest = NULL; 151 152 if (src_len % 4 != 0) { 153 goto invalid; 154 } 155 156 size_t out_len = (src_len / 4) * 3; 157 if (src_len >= 1 && src[src_len - 1] == '=') { 158 out_len--; 159 } 160 if (src_len >= 2 && src[src_len - 2] == '=') { 161 out_len--; 162 } 163 164 const uint8_t *s = (const uint8_t *)src; 165 166 dest = xmalloc(out_len); 167 168 int acc = 0; 169 int acc_len = 0; 170 size_t out_i = 0; 171 size_t src_i = 0; 172 int leftover_i = -1; 173 174 for (; src_i < src_len; src_i++) { 175 const uint8_t c = s[src_i]; 176 const uint8_t d = char_to_index[c]; 177 if (d == 0) { 178 if (c == '=') { 179 leftover_i = (int)src_i; 180 break; 181 } 182 goto invalid; 183 } 184 185 acc = ((acc << 6) & 0xFFF) + (d - 1); 186 acc_len += 6; 187 if (acc_len >= 8) { 188 acc_len -= 8; 189 dest[out_i] = (char)(acc >> acc_len); 190 out_i += 1; 191 } 192 } 193 194 if (acc_len > 4 || ((acc & ((1 << acc_len) - 1)) != 0)) { 195 goto invalid; 196 } 197 198 if (leftover_i > -1) { 199 int padding_len = acc_len / 2; 200 int padding_chars = 0; 201 for (; (size_t)leftover_i < src_len; leftover_i++) { 202 const uint8_t c = s[leftover_i]; 203 if (c != '=') { 204 goto invalid; 205 } 206 padding_chars += 1; 207 } 208 209 if (padding_chars != padding_len) { 210 goto invalid; 211 } 212 } 213 214 *out_lenp = out_len; 215 216 return dest; 217 218 invalid: 219 if (dest) { 220 xfree((void *)dest); 221 } 222 223 *out_lenp = 0; 224 225 return NULL; 226 }