xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/base64_code.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: base64_code.c,v 1.1.1.2 2014/07/06 19:27:57 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	base64_code 3
6 /* SUMMARY
7 /*	encode/decode data, base 64 style
8 /* SYNOPSIS
9 /*	#include <base64_code.h>
10 /*
11 /*	VSTRING	*base64_encode(result, in, len)
12 /*	VSTRING	*result;
13 /*	const char *in;
14 /*	ssize_t	len;
15 /*
16 /*	VSTRING	*base64_decode(result, in, len)
17 /*	VSTRING	*result;
18 /*	const char *in;
19 /*	ssize_t	len;
20 /*
21 /*	VSTRING	*base64_encode_opt(result, in, len, flags)
22 /*	VSTRING	*result;
23 /*	const char *in;
24 /*	ssize_t	len;
25 /*	int	flags;
26 /*
27 /*	VSTRING	*base64_decode_opt(result, in, len, flags)
28 /*	VSTRING	*result;
29 /*	const char *in;
30 /*	ssize_t	len;
31 /*	int	flags;
32 /* DESCRIPTION
33 /*	base64_encode() takes a block of len bytes and encodes it as one
34 /*	null-terminated string.  The result value is the result argument.
35 /*
36 /*	base64_decode() performs the opposite transformation. The result
37 /*	value is the result argument. The result is null terminated, whether
38 /*	or not that makes sense.
39 /*
40 /*	base64_encode_opt() and base64_decode_opt() provide extended
41 /*	interfaces.  In both cases the flags arguments is the bit-wise
42 /*	OR of zero or more the following:
43 /* .IP BASE64_FLAG_APPEND
44 /*	Append the result, instead of overwriting the result buffer.
45 /* .PP
46 /*	For convenience, BASE64_FLAG_NONE specifies none of the above.
47 /* DIAGNOSTICS
48 /*	base64_decode () returns a null pointer when the input contains
49 /*	characters not in the base 64 alphabet.
50 /* LICENSE
51 /* .ad
52 /* .fi
53 /*	The Secure Mailer license must be distributed with this software.
54 /* AUTHOR(S)
55 /*	Wietse Venema
56 /*	IBM T.J. Watson Research
57 /*	P.O. Box 704
58 /*	Yorktown Heights, NY 10598, USA
59 /*--*/
60 
61 /* System library. */
62 
63 #include "sys_defs.h"
64 #include <ctype.h>
65 #include <string.h>
66 #include <limits.h>
67 
68 #ifndef UCHAR_MAX
69 #define UCHAR_MAX 0xff
70 #endif
71 
72 /* Utility library. */
73 
74 #include <msg.h>
75 #include <mymalloc.h>
76 #include <vstring.h>
77 #include <base64_code.h>
78 
79 /* Application-specific. */
80 
81 static unsigned char to_b64[] =
82 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
83 
84 #define UNSIG_CHAR_PTR(x) ((unsigned char *)(x))
85 
86 /* base64_encode - raw data to encoded */
87 
88 #undef base64_encode
89 
90 extern VSTRING *base64_encode(VSTRING *, const char *, ssize_t);
91 
92 VSTRING *base64_encode(VSTRING *result, const char *in, ssize_t len)
93 {
94     return (base64_encode_opt(result, in, len, BASE64_FLAG_NONE));
95 }
96 
97 VSTRING *base64_encode_opt(VSTRING *result, const char *in, ssize_t len,
98 			           int flags)
99 {
100     const unsigned char *cp;
101     ssize_t count;
102 
103     /*
104      * Encode 3 -> 4.
105      */
106     if ((flags & BASE64_FLAG_APPEND) == 0)
107 	VSTRING_RESET(result);
108     for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) {
109 	VSTRING_ADDCH(result, to_b64[cp[0] >> 2]);
110 	if (count > 1) {
111 	    VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4 | cp[1] >> 4]);
112 	    if (count > 2) {
113 		VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2 | cp[2] >> 6]);
114 		VSTRING_ADDCH(result, to_b64[cp[2] & 0x3f]);
115 	    } else {
116 		VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2]);
117 		VSTRING_ADDCH(result, '=');
118 		break;
119 	    }
120 	} else {
121 	    VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4]);
122 	    VSTRING_ADDCH(result, '=');
123 	    VSTRING_ADDCH(result, '=');
124 	    break;
125 	}
126     }
127     VSTRING_TERMINATE(result);
128     return (result);
129 }
130 
131 /* base64_decode - encoded data to raw */
132 
133 #undef base64_decode
134 
135 extern VSTRING *base64_decode(VSTRING *, const char *, ssize_t);
136 
137 VSTRING *base64_decode(VSTRING *result, const char *in, ssize_t len)
138 {
139     return (base64_decode_opt(result, in, len, BASE64_FLAG_NONE));
140 }
141 
142 VSTRING *base64_decode_opt(VSTRING *result, const char *in, ssize_t len,
143 			           int flags)
144 {
145     static unsigned char *un_b64 = 0;
146     const unsigned char *cp;
147     ssize_t count;
148     unsigned int ch0;
149     unsigned int ch1;
150     unsigned int ch2;
151     unsigned int ch3;
152 
153 #define CHARS_PER_BYTE	(UCHAR_MAX + 1)
154 #define INVALID		0xff
155 
156     /*
157      * Sanity check.
158      */
159     if (len % 4)
160 	return (0);
161 
162     /*
163      * Once: initialize the decoding lookup table on the fly.
164      */
165     if (un_b64 == 0) {
166 	un_b64 = (unsigned char *) mymalloc(CHARS_PER_BYTE);
167 	memset(un_b64, INVALID, CHARS_PER_BYTE);
168 	for (cp = to_b64; cp < to_b64 + sizeof(to_b64); cp++)
169 	    un_b64[*cp] = cp - to_b64;
170     }
171 
172     /*
173      * Decode 4 -> 3.
174      */
175     if ((flags & BASE64_FLAG_APPEND) == 0)
176 	VSTRING_RESET(result);
177     for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
178 	if ((ch0 = un_b64[*cp++]) == INVALID
179 	    || (ch1 = un_b64[*cp++]) == INVALID)
180 	    return (0);
181 	VSTRING_ADDCH(result, ch0 << 2 | ch1 >> 4);
182 	if ((ch2 = *cp++) == '=')
183 	    break;
184 	if ((ch2 = un_b64[ch2]) == INVALID)
185 	    return (0);
186 	VSTRING_ADDCH(result, ch1 << 4 | ch2 >> 2);
187 	if ((ch3 = *cp++) == '=')
188 	    break;
189 	if ((ch3 = un_b64[ch3]) == INVALID)
190 	    return (0);
191 	VSTRING_ADDCH(result, ch2 << 6 | ch3);
192     }
193     VSTRING_TERMINATE(result);
194     return (result);
195 }
196 
197 #ifdef TEST
198 
199  /*
200   * Proof-of-concept test program: convert to base 64 and back.
201   */
202 
203 #define STR(x)	vstring_str(x)
204 #define LEN(x)	VSTRING_LEN(x)
205 
206 #define TESXT 	"this is a test!"
207 
208 int     main(int unused_argc, char **unused_argv)
209 {
210     VSTRING *b1 = vstring_alloc(1);
211     VSTRING *b2 = vstring_alloc(1);
212     char   *test = TESXT;
213     char   *test2 = TESXT TESXT;
214 
215 #define DECODE(b,x,l) { \
216 	if (base64_decode((b),(x),(l)) == 0) \
217 	    msg_panic("bad base64: %s", (x)); \
218     }
219 #define VERIFY(b,t) { \
220 	if (strcmp((b), (t)) != 0) \
221 	    msg_panic("bad test: %s", (b)); \
222     }
223 
224     base64_encode(b1, test, strlen(test));
225     DECODE(b2, STR(b1), LEN(b1));
226     VERIFY(STR(b2), test);
227 
228     base64_encode(b1, test, strlen(test));
229     base64_encode(b2, STR(b1), LEN(b1));
230     base64_encode(b1, STR(b2), LEN(b2));
231     DECODE(b2, STR(b1), LEN(b1));
232     DECODE(b1, STR(b2), LEN(b2));
233     DECODE(b2, STR(b1), LEN(b1));
234     VERIFY(STR(b2), test);
235 
236     base64_encode(b1, test, strlen(test));
237     base64_encode(b2, STR(b1), LEN(b1));
238     base64_encode(b1, STR(b2), LEN(b2));
239     base64_encode(b2, STR(b1), LEN(b1));
240     base64_encode(b1, STR(b2), LEN(b2));
241     DECODE(b2, STR(b1), LEN(b1));
242     DECODE(b1, STR(b2), LEN(b2));
243     DECODE(b2, STR(b1), LEN(b1));
244     DECODE(b1, STR(b2), LEN(b2));
245     DECODE(b2, STR(b1), LEN(b1));
246     VERIFY(STR(b2), test);
247 
248     base64_encode(b1, test, strlen(test));
249     base64_encode_opt(b1, test, strlen(test), BASE64_FLAG_APPEND);
250     DECODE(b2, STR(b1), LEN(b1));
251     VERIFY(STR(b2), test2);
252 
253     vstring_free(b1);
254     vstring_free(b2);
255     return (0);
256 }
257 
258 #endif
259