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