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