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