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