xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/base64_code.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: base64_code.c,v 1.2 2017/02/14 01:16:49 christos 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 
base64_encode(VSTRING * result,const char * in,ssize_t len)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 
base64_encode_opt(VSTRING * result,const char * in,ssize_t len,int flags)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 
base64_decode(VSTRING * result,const char * in,ssize_t len)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 
base64_decode_opt(VSTRING * result,const char * in,ssize_t len,int flags)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) - 1; 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 
main(int unused_argc,char ** unused_argv)206 int     main(int unused_argc, char **unused_argv)
207 {
208     VSTRING *b1 = vstring_alloc(1);
209     VSTRING *b2 = vstring_alloc(1);
210     char    test[256];
211     int     n;
212 
213     for (n = 0; n < sizeof(test); n++)
214 	test[n] = n;
215     base64_encode(b1, test, sizeof(test));
216     if (base64_decode(b2, STR(b1), LEN(b1)) == 0)
217 	msg_panic("bad base64: %s", STR(b1));
218     if (LEN(b2) != sizeof(test))
219 	msg_panic("bad decode length: %ld != %ld",
220 		  (long) LEN(b2), (long) sizeof(test));
221     for (n = 0; n < sizeof(test); n++)
222 	if (STR(b2)[n] != test[n])
223 	    msg_panic("bad decode value %d != %d",
224 		      (unsigned char) STR(b2)[n], (unsigned char) test[n]);
225     vstring_free(b1);
226     vstring_free(b2);
227     return (0);
228 }
229 
230 #endif
231