1 #include "crypto-base64.h"
2 #include <ctype.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <time.h>
7
8 /*****************************************************************************
9 *****************************************************************************/
10 size_t
base64_encode(void * vdst,size_t sizeof_dst,const void * vsrc,size_t sizeof_src)11 base64_encode(void *vdst, size_t sizeof_dst,
12 const void *vsrc, size_t sizeof_src)
13 {
14 static const char *b64 =
15 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
16 "abcdefghijklmnopqrstuvwxyz"
17 "0123456789"
18 "+/";
19 size_t i = 0;
20 size_t d = 0;
21 unsigned char *dst = (unsigned char *)vdst;
22 const unsigned char *src = (const unsigned char *)vsrc;
23
24 /* encode every 3 bytes of source into 4 bytes of destination text */
25 while (i + 3 <= sizeof_src) {
26 unsigned n;
27
28 /* make sure there is enough space */
29 if (d + 4 > sizeof_dst)
30 return d;
31
32 /* convert the chars */
33 n = src[i]<<16 | src[i+1]<<8 | src[i+2];
34 dst[d+0] = b64[ (n>>18) & 0x3F ];
35 dst[d+1] = b64[ (n>>12) & 0x3F ];
36 dst[d+2] = b64[ (n>> 6) & 0x3F ];
37 dst[d+3] = b64[ (n>> 0) & 0x3F ];
38
39 i += 3;
40 d += 4;
41 }
42
43 /* If the source text isn't an even multiple of 3 characters, then we'll
44 * have to append a '=' or '==' to the output to compensate */
45 if (i + 2 <= sizeof_src && d + 4 <= sizeof_dst) {
46 unsigned n = src[i]<<16 | src[i+1]<<8;
47 dst[d+0] = b64[ (n>>18) & 0x3F ];
48 dst[d+1] = b64[ (n>>12) & 0x3F ];
49 dst[d+2] = b64[ (n>> 6) & 0x3F ];
50 dst[d+3] = '=';
51 d += 4;
52 } else if (i + 1 <= sizeof_src && d + 4 <= sizeof_dst) {
53 unsigned n = src[i]<<16;
54 dst[d+0] = b64[ (n>>18) & 0x3F ];
55 dst[d+1] = b64[ (n>>12) & 0x3F ];
56 dst[d+2] = '=';
57 dst[d+3] = '=';
58 d += 4;
59 }
60
61 return d;
62 }
63
64
65 /*****************************************************************************
66 *****************************************************************************/
67 size_t
base64_decode(void * vdst,size_t sizeof_dst,const void * vsrc,size_t sizeof_src)68 base64_decode(void *vdst, size_t sizeof_dst,
69 const void *vsrc, size_t sizeof_src)
70 {
71 static const unsigned char rstr[] = {
72 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
73 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
74 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
75 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
76 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
77 0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63,
78 52, 53, 54, 55, 56, 57, 58, 59,
79 60, 61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
80 0xFF, 0, 1, 2, 3, 4, 5, 6,
81 7, 8, 9, 10, 11, 12, 13, 14,
82 15, 16, 17, 18, 19, 20, 21, 22,
83 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
84 0xFF, 26, 27, 28, 29, 30, 31, 32,
85 33, 34, 35, 36, 37, 38, 39, 40,
86 41, 42, 43, 44, 45, 46, 47, 48,
87 49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
88 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
89 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
90 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
91 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
92 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
93 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
94 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
95 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
96 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
97 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
98 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
99 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
100 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
101 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
102 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
103 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
104 };
105 size_t i = 0;
106 size_t d = 0;
107 unsigned char *dst = (unsigned char *)vdst;
108 const unsigned char *src = (const unsigned char *)vsrc;
109
110
111 while (i < sizeof_src) {
112 unsigned b;
113 unsigned c=0;
114
115 /* byte#1 */
116 while (i<sizeof_src && (c = rstr[src[i]]) > 64)
117 i++;
118 if (src[i] == '=' || i++ >= sizeof_src)
119 break;
120 b = (c << 2) & 0xfc;
121
122 while (i<sizeof_src && (c = rstr[src[i]]) > 64)
123 i++;
124 if (src[i] == '=' || i++ >= sizeof_src)
125 break;
126 b |= (c>>4) & 0x03;
127 if (d<sizeof_dst)
128 dst[d++] = (unsigned char)b;
129 if (i>=sizeof_src)
130 break;
131
132 /* byte#2 */
133 b = (c<<4) & 0xF0;
134 while (i<sizeof_src && src[i] != '=' && (c = rstr[src[i]]) > 64)
135 ;
136 if (src[i] == '=' || i++ >= sizeof_src)
137 break;
138 b |= (c>>2) & 0x0F;
139 if (d<sizeof_dst)
140 dst[d++] = (unsigned char)b;
141 if (i>=sizeof_src)
142 break;
143
144 /* byte#3*/
145 b = (c<<6) & 0xC0;
146 while (i<sizeof_src && src[i] != '=' && (c = rstr[src[i]]) > 64)
147 ;
148 if (src[i] == '=' || i++ >= sizeof_src)
149 break;
150 b |= c;
151 if (d<sizeof_dst)
152 dst[d++] = (unsigned char)b;
153 if (i>=sizeof_src)
154 break;
155 }
156
157 if (d<sizeof_dst)
158 dst[d] = '\0';
159 return d;
160 }
161
162 /*****************************************************************************
163 * Provide my own rand() simply to avoid static-analysis warning me that
164 * 'rand()' is unrandom, when in fact we want the non-random properties of
165 * rand() for regression testing.
166 *****************************************************************************/
167 static unsigned
r_rand(unsigned * seed)168 r_rand(unsigned *seed)
169 {
170 static const unsigned a = 214013;
171 static const unsigned c = 2531011;
172
173 *seed = (*seed) * a + c;
174 return (*seed)>>16 & 0x7fff;
175 }
176
177 /*****************************************************************************
178 *****************************************************************************/
179 int
base64_selftest(void)180 base64_selftest(void)
181 {
182 char buf[100];
183 char buf2[100];
184 char buf3[100];
185 size_t buf_len;
186 size_t buf2_len;
187 unsigned i;
188 unsigned seed = (unsigned)time(0);
189
190 buf_len = base64_encode(buf, sizeof(buf), "hello", 5);
191 buf2_len = base64_decode(buf2, sizeof(buf2), buf, buf_len);
192 if (buf2_len != 5 && memcmp(buf2, "hello", 5) != 0) {
193 fprintf(stderr, "base64: selftest failed\n");
194 return 1;
195 }
196
197 /*
198 * Generate a bunch of random strings, encode them, then decode them,
199 * making sure the final result matches the original string
200 */
201 for (i=0; i<100; i++) {
202 unsigned j;
203 size_t buf3_len;
204
205 /* create a string of random bytes */
206 buf_len = r_rand(&seed) % 50;
207 for (j=0; j<buf_len; j++) {
208 buf[j] = (char)r_rand(&seed);
209 }
210
211 /* encode it */
212 buf2_len = base64_encode(buf2, sizeof(buf2), buf, buf_len);
213
214 /* decode it back again */
215 buf3_len = base64_decode(buf3, sizeof(buf3), buf2, buf2_len);
216
217 /* now make sure result equals original */
218 if (buf3_len != buf_len && memcmp(buf3, buf, buf_len) != 0) {
219 fprintf(stderr, "base64: selftest failed\n");
220 return 1;
221 }
222 }
223
224 return 0;
225 }
226