/src/lighttpd1.4/src/base64.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "first.h" |
2 | | |
3 | | #include "base64.h" |
4 | | |
5 | | /* reverse mapping: |
6 | | * >= 0: base64 value |
7 | | * -1: invalid character |
8 | | * -2: skip character (whitespace/control) |
9 | | * -3: padding |
10 | | */ |
11 | | |
12 | | /* BASE64_STANDARD: "A-Z a-z 0-9 + /" maps to 0-63, pad with "=" */ |
13 | | static const char base64_standard_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; |
14 | | static const signed char base64_standard_reverse_table[] = { |
15 | | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
16 | | -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x00 - 0x0F */ |
17 | | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x10 - 0x1F */ |
18 | | -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 - 0x2F */ |
19 | | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1, /* 0x30 - 0x3F */ |
20 | | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */ |
21 | | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 - 0x5F */ |
22 | | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */ |
23 | | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */ |
24 | | }; |
25 | | |
26 | | /* BASE64_URL: "A-Z a-z 0-9 - _" maps to 0-63, pad with "=" */ |
27 | | static const char base64_url_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="; |
28 | | static const signed char base64_url_reverse_table[] = { |
29 | | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
30 | | -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x00 - 0x0F */ |
31 | | -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x10 - 0x1F */ |
32 | | -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, /* 0x20 - 0x2F */ |
33 | | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1, /* 0x30 - 0x3F */ |
34 | | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */ |
35 | | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, /* 0x50 - 0x5F */ |
36 | | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */ |
37 | | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */ |
38 | | }; |
39 | | |
40 | 0 | size_t li_base64_dec(unsigned char * const result, const size_t out_length, const char * const in, const size_t in_length, const base64_charset charset) { |
41 | 0 | const unsigned char *un = (const unsigned char *)in; |
42 | 0 | const unsigned char * const end = un + in_length; |
43 | 0 | const signed char * const base64_reverse_table = (charset) |
44 | 0 | ? base64_url_reverse_table /* BASE64_URL */ |
45 | 0 | : base64_standard_reverse_table; /* BASE64_STANDARD */ |
46 | |
|
47 | 0 | int_fast32_t ch = 0; |
48 | 0 | int_fast32_t out4 = 0; |
49 | 0 | size_t i = 0; |
50 | 0 | size_t out_pos = 0; |
51 | 0 | for (; un < end; ++un) { |
52 | 0 | ch = (*un < 128) ? base64_reverse_table[*un] : -1; |
53 | 0 | if (__builtin_expect( (ch < 0), 0)) { |
54 | | /* skip formatted base64; skip whitespace ('\r' '\n' '\t' ' ')*/ |
55 | 0 | if (-2 == ch) /*(loose check; skip ' ', all ctrls not \127 or \0)*/ |
56 | 0 | continue; /* skip character */ |
57 | 0 | break; |
58 | 0 | } |
59 | | |
60 | 0 | out4 = (out4 << 6) | ch; |
61 | 0 | if ((++i & 3) == 0) { |
62 | 0 | result[out_pos] = (out4 >> 16) & 0xFF; |
63 | 0 | result[out_pos+1] = (out4 >> 8) & 0xFF; |
64 | 0 | result[out_pos+2] = (out4 ) & 0xFF; |
65 | 0 | out_pos += 3; |
66 | 0 | out4 = 0; |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | | /* permit base64 string ending with pad chars (ch == -3); not checking |
71 | | * for one or two pad chars + optional whitespace reaches in_length) */ |
72 | | /* permit base64 string truncated before in_length (*un == '\0') */ |
73 | 0 | switch (un == end || ch == -3 || *un != '\0' ? (i & 3) : 1) { |
74 | 0 | case 3: |
75 | 0 | result[out_pos++] = (out4 >> 10); |
76 | 0 | out4 <<= 2; |
77 | 0 | __attribute_fallthrough__ |
78 | 0 | case 2: |
79 | 0 | result[out_pos++] = (out4 >> 4) & 0xFF; |
80 | 0 | __attribute_fallthrough__ |
81 | 0 | case 0: |
82 | 0 | force_assert(out_pos <= out_length); |
83 | 0 | return out_pos; |
84 | 0 | case 1: /* pad char or str end can only come after 2+ base64 chars */ |
85 | 0 | default: |
86 | 0 | return 0; /* invalid character, abort */ |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | 0 | size_t li_base64_enc(char * const restrict out, const size_t out_length, const unsigned char * const restrict in, const size_t in_length, const base64_charset charset, const int pad) { |
91 | 0 | size_t i; |
92 | 0 | size_t out_pos = 0; |
93 | 0 | uint_fast32_t v; |
94 | 0 | const char* const base64_table = (charset) |
95 | 0 | ? base64_url_table /* BASE64_URL */ |
96 | 0 | : base64_standard_table; /* BASE64_STANDARD */ |
97 | 0 | const char padchar = base64_table[64]; /* padding char */ |
98 | | |
99 | | /* check overflows */ |
100 | | /* 1073741823 is max num full_tuples to avoid overflow in uint32_t; |
101 | | * and (1 GB - 1) is ridiculously large for base64-encoded data */ |
102 | 0 | force_assert(in_length <= 3221225469); /* (3221225469+2) / 3 * 4 < UINT32_MAX */ |
103 | 0 | force_assert((in_length+2)/3*4 <= out_length); |
104 | | |
105 | 0 | for (i = 2; i < in_length; i += 3) { |
106 | 0 | v = (in[i-2] << 16) | (in[i-1] << 8) | in[i]; |
107 | 0 | out[out_pos+0] = base64_table[(v >> 18) & 0x3f]; |
108 | 0 | out[out_pos+1] = base64_table[(v >> 12) & 0x3f]; |
109 | 0 | out[out_pos+2] = base64_table[(v >> 6) & 0x3f]; |
110 | 0 | out[out_pos+3] = base64_table[(v ) & 0x3f]; |
111 | 0 | out_pos += 4; |
112 | 0 | } |
113 | |
|
114 | 0 | switch (in_length - (i-2)) { |
115 | 0 | case 0: |
116 | 0 | default: |
117 | 0 | break; |
118 | 0 | case 1: |
119 | 0 | { |
120 | | /* pretend in[i-1] = in[i] = 0, don't write last two (out_pos+3, out_pos+2) characters */ |
121 | 0 | v = (in[i-2] << 4); |
122 | 0 | out[out_pos+0] = base64_table[(v >> 6) & 0x3f]; |
123 | 0 | out[out_pos+1] = base64_table[(v ) & 0x3f]; |
124 | 0 | if (pad) { |
125 | 0 | out[out_pos+2] = out[out_pos+3] = padchar; |
126 | 0 | out_pos += 4; |
127 | 0 | } |
128 | 0 | else |
129 | 0 | out_pos += 2; |
130 | 0 | } |
131 | 0 | break; |
132 | 0 | case 2: |
133 | 0 | { |
134 | | /* pretend in[i] = 0, don't write last (out_pos+3) character */ |
135 | 0 | v = (in[i-2] << 10) | (in[i-1] << 2); |
136 | 0 | out[out_pos+0] = base64_table[(v >> 12) & 0x3f]; |
137 | 0 | out[out_pos+1] = base64_table[(v >> 6) & 0x3f]; |
138 | 0 | out[out_pos+2] = base64_table[(v ) & 0x3f]; |
139 | 0 | if (pad) { |
140 | 0 | out[out_pos+3] = padchar; |
141 | 0 | out_pos += 4; |
142 | 0 | } |
143 | 0 | else |
144 | 0 | out_pos += 3; |
145 | 0 | } |
146 | 0 | break; |
147 | 0 | } |
148 | | |
149 | 0 | return out_pos; |
150 | 0 | } |
151 | | |
152 | | |
153 | 0 | char* buffer_append_base64_enc(buffer *out, const unsigned char* in, size_t in_length, base64_charset charset, int pad) { |
154 | 0 | const size_t reserve = (in_length+2)/3*4; |
155 | 0 | char * const result = buffer_string_prepare_append(out, reserve); |
156 | 0 | const size_t out_pos = |
157 | 0 | li_base64_enc(result, reserve, in, in_length, charset, pad); |
158 | |
|
159 | 0 | buffer_commit(out, out_pos); |
160 | |
|
161 | 0 | return result; |
162 | 0 | } |
163 | | |
164 | | |
165 | 0 | unsigned char* buffer_append_base64_decode(buffer *out, const char* in, size_t in_length, base64_charset charset) { |
166 | 0 | const size_t reserve = 3*(in_length/4) + 3; |
167 | 0 | unsigned char * const result = (unsigned char *) |
168 | 0 | buffer_string_prepare_append(out, reserve); |
169 | 0 | const size_t out_pos = |
170 | 0 | li_base64_dec(result, reserve, in, in_length, charset); |
171 | |
|
172 | 0 | buffer_commit(out, out_pos); |
173 | |
|
174 | 0 | return (out_pos || !in_length) ? result : NULL; |
175 | 0 | } |