]> git.ozlabs.org Git - ccan/blob - ccan/base64/base64.c
base64: fix for unsigned chars (e.g. ARM).
[ccan] / ccan / base64 / base64.c
1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include "base64.h"
3
4 #include <errno.h>
5 #include <string.h>
6 #include <assert.h>
7 #include <stdint.h>
8
9 /**
10  * sixbit_to_b64 - maps a 6-bit value to the base64 alphabet
11  * @param map A base 64 map (see base64_init_map)
12  * @param sixbit Six-bit value to map
13  * @return a base 64 character
14  */
15 static char sixbit_to_b64(const base64_maps_t *maps, const uint8_t sixbit)
16 {
17         assert(sixbit <= 63);
18
19         return maps->encode_map[(unsigned char)sixbit];
20 }
21
22 /**
23  * sixbit_from_b64 - maps a base64-alphabet character to its 6-bit value
24  * @param maps A base 64 maps structure (see base64_init_maps)
25  * @param sixbit Six-bit value to map
26  * @return a six-bit value
27  */
28 static int8_t sixbit_from_b64(const base64_maps_t *maps,
29                               const unsigned char b64letter)
30 {
31         int8_t ret;
32
33         ret = maps->decode_map[(unsigned char)b64letter];
34         if (ret == (char)0xff) {
35                 errno = EDOM;
36                 return -1;
37         }
38
39         return ret;
40 }
41
42 bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char)
43 {
44         return (maps->decode_map[(const unsigned char)b64char] != (char)0xff);
45 }
46
47 void base64_init_maps(base64_maps_t *dest, const char src[64])
48 {
49         unsigned char i;
50
51         memcpy(dest->encode_map,src,64);
52         memset(dest->decode_map,0xff,256);
53         for (i=0; i<64; i++) {
54                 dest->decode_map[(unsigned char)src[i]] = i;
55         }
56 }
57
58 size_t base64_encoded_length(size_t srclen)
59 {
60         return ((srclen + 2) / 3) * 4;
61 }
62
63 void base64_encode_triplet_using_maps(const base64_maps_t *maps,
64                                       char dest[4], const char src[3])
65 {
66         char a = src[0];
67         char b = src[1];
68         char c = src[2];
69
70         dest[0] = sixbit_to_b64(maps, (a & 0xfc) >> 2);
71         dest[1] = sixbit_to_b64(maps, ((a & 0x3) << 4) | ((b & 0xf0) >> 4));
72         dest[2] = sixbit_to_b64(maps, ((c & 0xc0) >> 6) | ((b & 0xf) << 2));
73         dest[3] = sixbit_to_b64(maps, c & 0x3f);
74 }
75
76 void base64_encode_tail_using_maps(const base64_maps_t *maps, char dest[4],
77                                    const char *src, const size_t srclen)
78 {
79         char longsrc[3] = { 0 };
80
81         assert(srclen <= 3);
82
83         memcpy(longsrc, src, srclen);
84         base64_encode_triplet_using_maps(maps, dest, longsrc);
85         memset(dest+1+srclen, '=', 3-srclen);
86 }
87
88 ssize_t base64_encode_using_maps(const base64_maps_t *maps,
89                                  char *dest, const size_t destlen,
90                                  const char *src, const size_t srclen)
91 {
92         size_t src_offset = 0;
93         size_t dest_offset = 0;
94
95         if (destlen < base64_encoded_length(srclen)) {
96                 errno = EOVERFLOW;
97                 return -1;
98         }
99
100         while (srclen - src_offset >= 3) {
101                 base64_encode_triplet_using_maps(maps, &dest[dest_offset], &src[src_offset]);
102                 src_offset += 3;
103                 dest_offset += 4;
104         }
105
106         if (src_offset < srclen) {
107                 base64_encode_tail_using_maps(maps, &dest[dest_offset], &src[src_offset], srclen-src_offset);
108                 dest_offset += 4;
109         }
110
111         memset(&dest[dest_offset], '\0', destlen-dest_offset);
112
113         return dest_offset;
114 }
115
116 size_t base64_decoded_length(size_t srclen)
117 {
118         return ((srclen+3)/4*3);
119 }
120
121 int base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3],
122                                      const char src[4])
123 {
124         signed char a;
125         signed char b;
126         signed char c;
127         signed char d;
128
129         a = sixbit_from_b64(maps, src[0]);
130         b = sixbit_from_b64(maps, src[1]);
131         c = sixbit_from_b64(maps, src[2]);
132         d = sixbit_from_b64(maps, src[3]);
133
134         if ((a == -1) || (b == -1) || (c == -1) || (d == -1)) {
135                 return -1;
136         }
137
138         dest[0] = (a << 2) | (b >> 4);
139         dest[1] = ((b & 0xf) << 4) | (c >> 2);
140         dest[2] = ((c & 0x3) << 6) | d;
141
142         return 0;
143 }
144
145
146 int base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3],
147                                   const char * src, const size_t srclen)
148 {
149         char longsrc[4];
150         int quartet_result;
151         size_t insize = srclen;
152
153         while (insize != 0 &&
154                src[insize-1] == '=') { /* throw away padding symbols */
155                 insize--;
156         }
157         if (insize == 0) {
158                 return 0;
159         }
160         if (insize == 1) {
161                 /* the input is malformed.... */
162                 errno = EINVAL;
163                 return -1;
164         }
165         memcpy(longsrc, src, insize);
166         memset(longsrc+insize, 'A', 4-insize);
167         quartet_result = base64_decode_quartet_using_maps(maps, dest, longsrc);
168         if (quartet_result == -1) {
169                 return -1;
170         }
171
172         return insize - 1;
173 }
174
175 ssize_t base64_decode_using_maps(const base64_maps_t *maps,
176                                  char *dest, const size_t destlen,
177                                  const char *src, const size_t srclen)
178 {
179         ssize_t dest_offset = 0;
180         ssize_t i;
181         size_t more;
182
183         if (destlen < base64_decoded_length(srclen)) {
184                 errno = EOVERFLOW;
185                 return -1;
186         }
187
188         for(i=0; srclen - i > 4; i+=4) {
189                 if (base64_decode_quartet_using_maps(maps, &dest[dest_offset], &src[i]) == -1) {
190                         return -1;
191                 }
192                 dest_offset += 3;
193         }
194
195         more = base64_decode_tail_using_maps(maps, &dest[dest_offset], &src[i], srclen - i);
196         if (more == -1) {
197                 return -1;
198         }
199         dest_offset += more;
200
201         memset(&dest[dest_offset], '\0', destlen-dest_offset);
202
203         return dest_offset;
204 }
205
206
207
208
209 /**
210  * base64_maps_rfc4648 - pregenerated maps struct for rfc4648
211  */
212 const base64_maps_t base64_maps_rfc4648 = {
213   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
214
215   "\xff\xff\xff\xff\xff" /* 0 */                                        \
216   "\xff\xff\xff\xff\xff" /* 5 */                                        \
217   "\xff\xff\xff\xff\xff" /* 10 */                                       \
218   "\xff\xff\xff\xff\xff" /* 15 */                                       \
219   "\xff\xff\xff\xff\xff" /* 20 */                                       \
220   "\xff\xff\xff\xff\xff" /* 25 */                                       \
221   "\xff\xff\xff\xff\xff" /* 30 */                                       \
222   "\xff\xff\xff\xff\xff" /* 35 */                                       \
223   "\xff\xff\xff\x3e\xff" /* 40 */                                       \
224   "\xff\xff\x3f\x34\x35" /* 45 */                                       \
225   "\x36\x37\x38\x39\x3a" /* 50 */                                       \
226   "\x3b\x3c\x3d\xff\xff" /* 55 */                                       \
227   "\xff\xff\xff\xff\xff" /* 60 */                                       \
228   "\x00\x01\x02\x03\x04" /* 65 A */                                     \
229   "\x05\x06\x07\x08\x09" /* 70 */                                       \
230   "\x0a\x0b\x0c\x0d\x0e" /* 75 */                                       \
231   "\x0f\x10\x11\x12\x13" /* 80 */                                       \
232   "\x14\x15\x16\x17\x18" /* 85 */                                       \
233   "\x19\xff\xff\xff\xff" /* 90 */                                       \
234   "\xff\xff\x1a\x1b\x1c" /* 95 */                                       \
235   "\x1d\x1e\x1f\x20\x21" /* 100 */                                      \
236   "\x22\x23\x24\x25\x26" /* 105 */                                      \
237   "\x27\x28\x29\x2a\x2b" /* 110 */                                      \
238   "\x2c\x2d\x2e\x2f\x30" /* 115 */                                      \
239   "\x31\x32\x33\xff\xff" /* 120 */                                      \
240   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */                  \
241   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
242   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
243   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */                  \
244   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
245   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
246   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */                  \
247   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
248   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
249   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */                  \
250   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
251   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"                            \
252   "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */
253 };