xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/base64_code.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: base64_code.c,v 1.1.1.1 2009/06/23 10:08:59 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 /* DESCRIPTION
21 /*	base64_encode() takes a block of len bytes and encodes it as one
22 /*	null-terminated string.  The result value is the result argument.
23 /*
24 /*	base64_decode() performs the opposite transformation. The result
25 /*	value is the result argument. The result is null terminated, whether
26 /*	or not that makes sense.
27 /* DIAGNOSTICS
28 /*	base64_decode () returns a null pointer when the input contains
29 /*	characters not in the base 64 alphabet.
30 /* LICENSE
31 /* .ad
32 /* .fi
33 /*	The Secure Mailer license must be distributed with this software.
34 /* AUTHOR(S)
35 /*	Wietse Venema
36 /*	IBM T.J. Watson Research
37 /*	P.O. Box 704
38 /*	Yorktown Heights, NY 10598, USA
39 /*--*/
40 
41 /* System library. */
42 
43 #include "sys_defs.h"
44 #include <ctype.h>
45 #include <string.h>
46 #include <limits.h>
47 
48 #ifndef UCHAR_MAX
49 #define UCHAR_MAX 0xff
50 #endif
51 
52 /* Utility library. */
53 
54 #include <msg.h>
55 #include <mymalloc.h>
56 #include <vstring.h>
57 #include <base64_code.h>
58 
59 /* Application-specific. */
60 
61 static unsigned char to_b64[] =
62 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
63 
64 #define UNSIG_CHAR_PTR(x) ((unsigned char *)(x))
65 
66 /* base64_encode - raw data to encoded */
67 
68 VSTRING *base64_encode(VSTRING *result, const char *in, ssize_t len)
69 {
70     const unsigned char *cp;
71     ssize_t count;
72 
73     /*
74      * Encode 3 -> 4.
75      */
76     VSTRING_RESET(result);
77     for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) {
78 	VSTRING_ADDCH(result, to_b64[cp[0] >> 2]);
79 	if (count > 1) {
80 	    VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4 | cp[1] >> 4]);
81 	    if (count > 2) {
82 		VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2 | cp[2] >> 6]);
83 		VSTRING_ADDCH(result, to_b64[cp[2] & 0x3f]);
84 	    } else {
85 		VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2]);
86 		VSTRING_ADDCH(result, '=');
87 		break;
88 	    }
89 	} else {
90 	    VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4]);
91 	    VSTRING_ADDCH(result, '=');
92 	    VSTRING_ADDCH(result, '=');
93 	    break;
94 	}
95     }
96     VSTRING_TERMINATE(result);
97     return (result);
98 }
99 
100 /* base64_decode - encoded data to raw */
101 
102 VSTRING *base64_decode(VSTRING *result, const char *in, ssize_t len)
103 {
104     static unsigned char *un_b64 = 0;
105     const unsigned char *cp;
106     ssize_t count;
107     unsigned int ch0;
108     unsigned int ch1;
109     unsigned int ch2;
110     unsigned int ch3;
111 
112 #define CHARS_PER_BYTE	(UCHAR_MAX + 1)
113 #define INVALID		0xff
114 
115     /*
116      * Sanity check.
117      */
118     if (len % 4)
119 	return (0);
120 
121     /*
122      * Once: initialize the decoding lookup table on the fly.
123      */
124     if (un_b64 == 0) {
125 	un_b64 = (unsigned char *) mymalloc(CHARS_PER_BYTE);
126 	memset(un_b64, INVALID, CHARS_PER_BYTE);
127 	for (cp = to_b64; cp < to_b64 + sizeof(to_b64); cp++)
128 	    un_b64[*cp] = cp - to_b64;
129     }
130 
131     /*
132      * Decode 4 -> 3.
133      */
134     VSTRING_RESET(result);
135     for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
136 	if ((ch0 = un_b64[*cp++]) == INVALID
137 	    || (ch1 = un_b64[*cp++]) == INVALID)
138 	    return (0);
139 	VSTRING_ADDCH(result, ch0 << 2 | ch1 >> 4);
140 	if ((ch2 = *cp++) == '=')
141 	    break;
142 	if ((ch2 = un_b64[ch2]) == INVALID)
143 	    return (0);
144 	VSTRING_ADDCH(result, ch1 << 4 | ch2 >> 2);
145 	if ((ch3 = *cp++) == '=')
146 	    break;
147 	if ((ch3 = un_b64[ch3]) == INVALID)
148 	    return (0);
149 	VSTRING_ADDCH(result, ch2 << 6 | ch3);
150     }
151     VSTRING_TERMINATE(result);
152     return (result);
153 }
154 
155 #ifdef TEST
156 
157  /*
158   * Proof-of-concept test program: convert to base 64 and back.
159   */
160 
161 #define STR(x)	vstring_str(x)
162 #define LEN(x)	VSTRING_LEN(x)
163 
164 int     main(int unused_argc, char **unused_argv)
165 {
166     VSTRING *b1 = vstring_alloc(1);
167     VSTRING *b2 = vstring_alloc(1);
168     char   *test = "this is a test";
169 
170 #define DECODE(b,x,l) { \
171 	if (base64_decode((b),(x),(l)) == 0) \
172 	    msg_panic("bad base64: %s", (x)); \
173     }
174 #define VERIFY(b,t) { \
175 	if (strcmp((b), (t)) != 0) \
176 	    msg_panic("bad test: %s", (b)); \
177     }
178 
179     base64_encode(b1, test, strlen(test));
180     DECODE(b2, STR(b1), LEN(b1));
181     VERIFY(STR(b2), test);
182 
183     base64_encode(b1, test, strlen(test));
184     base64_encode(b2, STR(b1), LEN(b1));
185     base64_encode(b1, STR(b2), LEN(b2));
186     DECODE(b2, STR(b1), LEN(b1));
187     DECODE(b1, STR(b2), LEN(b2));
188     DECODE(b2, STR(b1), LEN(b1));
189     VERIFY(STR(b2), test);
190 
191     base64_encode(b1, test, strlen(test));
192     base64_encode(b2, STR(b1), LEN(b1));
193     base64_encode(b1, STR(b2), LEN(b2));
194     base64_encode(b2, STR(b1), LEN(b1));
195     base64_encode(b1, STR(b2), LEN(b2));
196     DECODE(b2, STR(b1), LEN(b1));
197     DECODE(b1, STR(b2), LEN(b2));
198     DECODE(b2, STR(b1), LEN(b1));
199     DECODE(b1, STR(b2), LEN(b2));
200     DECODE(b2, STR(b1), LEN(b1));
201     VERIFY(STR(b2), test);
202 
203     vstring_free(b1);
204     vstring_free(b2);
205     return (0);
206 }
207 
208 #endif
209