51 broke -E logic completely, rewise it
[tfcrypt.git] / base64.c
1 /*
2  * base64.c: libb64 compressed code. Public domain.
3  * See http://libb64.sourceforge.net/ for original code and infos.
4  *
5  * Modified and fixed by Lynx <lynx@lynxlynx.ru> 03Jun2016:
6  * - Single TU, minimal external dependencies
7  * - Stream operation, no newline insertions
8  * - Fixed code style to pure K&R
9  * - Fixed integer overflows and fixed size types
10  * - Fixed out of bounds access in base64_decode_block 
11  * - Force specify output size for output buffer when decoding
12  * - Fixed signed/unsigned issue on ARM
13  * - Added generic memory converter wrappers which do not expose internals
14  * - All functions calculate number of processed characters and return them to caller
15  */
16
17 #include <string.h>
18 #include <stdlib.h>
19
20 enum base64_decodestep {
21         estep_a, estep_b, estep_c, estep_d
22 };
23
24 struct base64_decodestate {
25         enum base64_decodestep step;
26         char plainchar;
27         size_t count;
28 };
29
30 enum base64_encodestep {
31         dstep_a, dstep_b, dstep_c
32 };
33
34 struct base64_encodestate {
35         enum base64_encodestep step;
36         char result;
37         size_t count;
38 };
39
40 int base64_decode_value(signed char value_in)
41 {
42         static const signed char decoding[] = {
43                 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1,
44                 -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
45                 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
46                 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
47         };
48         static const char decoding_size = sizeof(decoding);
49         if (value_in < 43) return -1;
50         value_in -= 43;
51         if (value_in >= decoding_size) return -1;
52         return decoding[(int)value_in];
53 }
54
55 void base64_init_decodestate(struct base64_decodestate *state_in)
56 {
57         state_in->step = estep_a;
58         state_in->plainchar = 0;
59         state_in->count = 0;
60 }
61
62 #define CHECK_BOUNDS do { if (plainchar-plaintext_out >= plaintext_outl) goto _ret; } while (0)
63
64 size_t base64_decode_block(const char *code_in, size_t length_in, char *plaintext_out, size_t plaintext_outl, struct base64_decodestate *state_in)
65 {
66         const char *codechar = code_in;
67         char *plainchar = plaintext_out;
68         int fragment;
69         
70         *plainchar = state_in->plainchar;
71         
72         switch (state_in->step) {
73                 while (1) {
74                         case estep_a:
75                                         do {
76                                                 if (codechar == code_in+length_in) {
77                                                         state_in->step = estep_a;
78                                                         state_in->plainchar = *plainchar;
79                                                         state_in->count += (plainchar - plaintext_out);
80                                                         return plainchar - plaintext_out;
81                                                 }
82                                                 fragment = base64_decode_value(*codechar++);
83                                         } while (fragment < 0);
84                                         *plainchar = (fragment & 0x3f) << 2;
85                         case estep_b:
86                                         do {
87                                                 if (codechar == code_in+length_in) {
88                                                         state_in->step = estep_b;
89                                                         state_in->plainchar = *plainchar;
90                                                         state_in->count += (plainchar - plaintext_out);
91                                                         return plainchar - plaintext_out;
92                                                 }
93                                                 fragment = base64_decode_value(*codechar++);
94                                         } while (fragment < 0);
95                                         *plainchar++ |= (fragment & 0x30) >> 4;
96                                         CHECK_BOUNDS;
97                                         *plainchar = (fragment & 0x0f) << 4;
98                         case estep_c:
99                                         do {
100                                                 if (codechar == code_in+length_in) {
101                                                         state_in->step = estep_c;
102                                                         state_in->plainchar = *plainchar;
103                                                         state_in->count += (plainchar - plaintext_out);
104                                                         return plainchar - plaintext_out;
105                                                 }
106                                                 fragment = base64_decode_value(*codechar++);
107                                         } while (fragment < 0);
108                                         *plainchar++ |= (fragment & 0x3c) >> 2;
109                                         CHECK_BOUNDS;
110                                         *plainchar = (fragment & 0x03) << 6;
111                         case estep_d:
112                                         do {
113                                                 if (codechar == code_in+length_in) {
114                                                         state_in->step = estep_d;
115                                                         state_in->plainchar = *plainchar;
116                                                         state_in->count += (plainchar - plaintext_out);
117                                                         return plainchar - plaintext_out;
118                                                 }
119                                                 fragment = base64_decode_value(*codechar++);
120                                         } while (fragment < 0);
121                                         *plainchar++ |= (fragment & 0x3f);
122                 }
123         }
124
125 _ret:   state_in->count += (plainchar - plaintext_out);
126         return plainchar - plaintext_out;
127 }
128
129 void base64_init_encodestate(struct base64_encodestate *state_in)
130 {
131         state_in->step = dstep_a;
132         state_in->result = 0;
133         state_in->count = 0;
134 }
135
136 char base64_encode_value(char value_in)
137 {
138         static const char *encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
139         if (value_in > 63) return '=';
140         return encoding[(int)value_in];
141 }
142
143 size_t base64_encode_block(const char *plaintext_in, size_t length_in, char *code_out, struct base64_encodestate *state_in)
144 {
145         const char *plainchar = plaintext_in;
146         const char *const plaintextend = plaintext_in + length_in;
147         char *codechar = code_out;
148         char result;
149         char fragment;
150         
151         result = state_in->result;
152         
153         switch (state_in->step) {
154                 while (1) {
155                         case dstep_a:
156                                         if (plainchar == plaintextend) {
157                                                 state_in->result = result;
158                                                 state_in->step = dstep_a;
159                                                 state_in->count += (codechar - code_out);
160                                                 return codechar - code_out;
161                                         }
162                                         fragment = *plainchar++;
163                                         result = (fragment & 0xfc) >> 2;
164                                         *codechar++ = base64_encode_value(result);
165                                         result = (fragment & 0x03) << 4;
166                         case dstep_b:
167                                         if (plainchar == plaintextend) {
168                                                 state_in->result = result;
169                                                 state_in->step = dstep_b;
170                                                 state_in->count += (codechar - code_out);
171                                                 return codechar - code_out;
172                                         }
173                                         fragment = *plainchar++;
174                                         result |= (fragment & 0xf0) >> 4;
175                                         *codechar++ = base64_encode_value(result);
176                                         result = (fragment & 0x0f) << 2;
177                         case dstep_c:
178                                         if (plainchar == plaintextend) {
179                                                 state_in->result = result;
180                                                 state_in->step = dstep_c;
181                                                 state_in->count += (codechar - code_out);
182                                                 return codechar - code_out;
183                                         }
184                                         fragment = *plainchar++;
185                                         result |= (fragment & 0xc0) >> 6;
186                                         *codechar++ = base64_encode_value(result);
187                                         result  = (fragment & 0x3f) >> 0;
188                                         *codechar++ = base64_encode_value(result);
189                 }
190         }
191         /* control should not reach here */
192         state_in->count += (codechar - code_out);
193         return codechar - code_out;
194 }
195
196 size_t base64_encode_blockend(char *code_out, struct base64_encodestate *state_in)
197 {
198         char *codechar = code_out + state_in->count;
199         
200         switch (state_in->step) {
201                 case dstep_b:
202                         *codechar++ = base64_encode_value(state_in->result);
203                         *codechar++ = '=';
204                         *codechar++ = '=';
205                         state_in->count += 3;
206                         break;
207                 case dstep_c:
208                         *codechar++ = base64_encode_value(state_in->result);
209                         *codechar++ = '=';
210                         state_in->count += 2;
211                         break;
212                 case dstep_a:
213                         break;
214         }
215
216         return codechar - code_out;
217 }
218
219 /* Process single block of memory */
220 size_t base64_decode(char *output, size_t outputl, const char *input, size_t inputl)
221 {
222         struct base64_decodestate dstate;
223         size_t r;
224
225         base64_init_decodestate(&dstate);
226         base64_decode_block(input, inputl, output, outputl, &dstate);
227
228         r = dstate.count;
229         memset(&dstate, 0, sizeof(struct base64_decodestate));
230
231         return r;
232 }
233
234 size_t base64_encode(char *output, const char *input, size_t inputl)
235 {
236         struct base64_encodestate estate;
237         size_t r;
238
239         base64_init_encodestate(&estate);
240         base64_encode_block(input, inputl, output, &estate);
241         base64_encode_blockend(output, &estate);
242
243         r = estate.count;
244         memset(&estate, 0, sizeof(struct base64_encodestate));
245
246         return r;
247 }