1/*++ 2/* NAME 3/* base32_code 3 4/* SUMMARY 5/* encode/decode data, base 32 style 6/* SYNOPSIS 7/* #include <base32_code.h> 8/* 9/* VSTRING *base32_encode(result, in, len) 10/* VSTRING *result; 11/* const char *in; 12/* ssize_t len; 13/* 14/* VSTRING *base32_decode(result, in, len) 15/* VSTRING *result; 16/* const char *in; 17/* ssize_t len; 18/* DESCRIPTION 19/* base32_encode() takes a block of len bytes and encodes it as one 20/* null-terminated string. The result value is the result argument. 21/* 22/* base32_decode() performs the opposite transformation. The result 23/* value is the result argument. The result is null terminated, whether 24/* or not that makes sense. 25/* DIAGNOSTICS 26/* base32_decode() returns a null pointer when the input contains 27/* characters not in the base 32 alphabet. 28/* SEE ALSO 29/* RFC 4648; padding is strictly enforced 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 <base32_code.h> 58 59/* Application-specific. */ 60 61static unsigned char to_b32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 62 63#define UNSIG_CHAR_PTR(x) ((unsigned char *)(x)) 64 65/* base32_encode - raw data to encoded */ 66 67VSTRING *base32_encode(VSTRING *result, const char *in, ssize_t len) 68{ 69 const unsigned char *cp; 70 ssize_t count; 71 static int pad_count[] = {0, 6, 4, 3, 1}; 72 73 /* 74 * Encode 5 -> 8. 75 */ 76 VSTRING_RESET(result); 77 for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 5, cp += 5) { 78 VSTRING_ADDCH(result, to_b32[cp[0] >> 3]); 79 if (count < 2) { 80 VSTRING_ADDCH(result, to_b32[(cp[0] & 0x7) << 2]); 81 break; 82 } 83 VSTRING_ADDCH(result, to_b32[(cp[0] & 0x7) << 2 | cp[1] >> 6]); 84 VSTRING_ADDCH(result, to_b32[(cp[1] & 0x3f) >> 1]); 85 if (count < 3) { 86 VSTRING_ADDCH(result, to_b32[(cp[1] & 0x1) << 4]); 87 break; 88 } 89 VSTRING_ADDCH(result, to_b32[(cp[1] & 0x1) << 4 | cp[2] >> 4]); 90 if (count < 4) { 91 VSTRING_ADDCH(result, to_b32[(cp[2] & 0xf) << 1]); 92 break; 93 } 94 VSTRING_ADDCH(result, to_b32[(cp[2] & 0xf) << 1 | cp[3] >> 7]); 95 VSTRING_ADDCH(result, to_b32[(cp[3] & 0x7f) >> 2]); 96 if (count < 5) { 97 VSTRING_ADDCH(result, to_b32[(cp[3] & 0x3) << 3]); 98 break; 99 } 100 VSTRING_ADDCH(result, to_b32[(cp[3] & 0x3) << 3 | cp[4] >> 5]); 101 VSTRING_ADDCH(result, to_b32[cp[4] & 0x1f]); 102 } 103 if (count > 0) 104 vstring_strncat(result, "======", pad_count[count]); 105 VSTRING_TERMINATE(result); 106 return (result); 107} 108 109/* base32_decode - encoded data to raw */ 110 111VSTRING *base32_decode(VSTRING *result, const char *in, ssize_t len) 112{ 113 static unsigned char *un_b32 = 0; 114 const unsigned char *cp; 115 ssize_t count; 116 unsigned int ch0; 117 unsigned int ch1; 118 unsigned int ch2; 119 unsigned int ch3; 120 unsigned int ch4; 121 unsigned int ch5; 122 unsigned int ch6; 123 unsigned int ch7; 124 125#define CHARS_PER_BYTE (UCHAR_MAX + 1) 126#define INVALID 0xff 127#if 1 128#define ENFORCE_LENGTH(x) (x) 129#define ENFORCE_PADDING(x) (x) 130#define ENFORCE_NULL_BITS(x) (x) 131#else 132#define ENFORCE_LENGTH(x) (1) 133#define ENFORCE_PADDING(x) (1) 134#define ENFORCE_NULL_BITS(x) (1) 135#endif 136 137 /* 138 * Sanity check. 139 */ 140 if (ENFORCE_LENGTH(len % 8)) 141 return (0); 142 143 /* 144 * Once: initialize the decoding lookup table on the fly. 145 */ 146 if (un_b32 == 0) { 147 un_b32 = (unsigned char *) mymalloc(CHARS_PER_BYTE); 148 memset(un_b32, INVALID, CHARS_PER_BYTE); 149 for (cp = to_b32; cp < to_b32 + sizeof(to_b32); cp++) 150 un_b32[*cp] = cp - to_b32; 151 } 152 153 /* 154 * Decode 8 -> 5. 155 */ 156 VSTRING_RESET(result); 157 for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 8) { 158 if ((ch0 = un_b32[*cp++]) == INVALID 159 || (ch1 = un_b32[*cp++]) == INVALID) 160 return (0); 161 VSTRING_ADDCH(result, ch0 << 3 | ch1 >> 2); 162 if ((ch2 = *cp++) == '=' 163 && ENFORCE_PADDING(strcmp((char *) cp, "=====") == 0) 164 && ENFORCE_NULL_BITS((ch1 & 0x3) == 0)) 165 break; 166 if ((ch2 = un_b32[ch2]) == INVALID) 167 return (0); 168 if ((ch3 = un_b32[*cp++]) == INVALID) 169 return (0); 170 VSTRING_ADDCH(result, ch1 << 6 | ch2 << 1 | ch3 >> 4); 171 if ((ch4 = *cp++) == '=' 172 && ENFORCE_PADDING(strcmp((char *) cp, "===") == 0) 173 && ENFORCE_NULL_BITS((ch3 & 0xf) == 0)) 174 break; 175 if ((ch4 = un_b32[ch4]) == INVALID) 176 return (0); 177 VSTRING_ADDCH(result, ch3 << 4 | ch4 >> 1); 178 if ((ch5 = *cp++) == '=' 179 && ENFORCE_PADDING(strcmp((char *) cp, "==") == 0) 180 && ENFORCE_NULL_BITS((ch4 & 0x1) == 0)) 181 break; 182 if ((ch5 = un_b32[ch5]) == INVALID) 183 return (0); 184 if ((ch6 = un_b32[*cp++]) == INVALID) 185 return (0); 186 VSTRING_ADDCH(result, ch4 << 7 | ch5 << 2 | ch6 >> 3); 187 if ((ch7 = *cp++) == '=' 188 && ENFORCE_NULL_BITS((ch6 & 0x7) == 0)) 189 break; 190 if ((ch7 = un_b32[ch7]) == INVALID) 191 return (0); 192 VSTRING_ADDCH(result, ch6 << 5 | ch7); 193 } 194 VSTRING_TERMINATE(result); 195 return (result); 196} 197 198#ifdef TEST 199 200 /* 201 * Proof-of-concept test program: convert to base 32 and back. 202 */ 203 204#define STR(x) vstring_str(x) 205#define LEN(x) VSTRING_LEN(x) 206 207int main(int unused_argc, char **unused_argv) 208{ 209 VSTRING *b1 = vstring_alloc(1); 210 VSTRING *b2 = vstring_alloc(1); 211 VSTRING *test = vstring_alloc(1); 212 int i, j; 213 214 /* 215 * Test all byte values (except null) on all byte positions. 216 */ 217 for (j = 0; j < 256; j++) 218 for (i = 1; i < 256; i++) 219 VSTRING_ADDCH(test, i); 220 VSTRING_TERMINATE(test); 221 222#define DECODE(b,x,l) { \ 223 if (base32_decode((b),(x),(l)) == 0) \ 224 msg_panic("bad base32: %s", (x)); \ 225 } 226#define VERIFY(b,t,l) { \ 227 if (memcmp((b), (t), (l)) != 0) \ 228 msg_panic("bad test: %s", (b)); \ 229 } 230 231 /* 232 * Test all padding variants. 233 */ 234 for (i = 1; i <= 8; i++) { 235 base32_encode(b1, STR(test), LEN(test)); 236 DECODE(b2, STR(b1), LEN(b1)); 237 VERIFY(STR(b2), STR(test), LEN(test)); 238 239 base32_encode(b1, STR(test), LEN(test)); 240 base32_encode(b2, STR(b1), LEN(b1)); 241 base32_encode(b1, STR(b2), LEN(b2)); 242 DECODE(b2, STR(b1), LEN(b1)); 243 DECODE(b1, STR(b2), LEN(b2)); 244 DECODE(b2, STR(b1), LEN(b1)); 245 VERIFY(STR(b2), STR(test), LEN(test)); 246 247 base32_encode(b1, STR(test), LEN(test)); 248 base32_encode(b2, STR(b1), LEN(b1)); 249 base32_encode(b1, STR(b2), LEN(b2)); 250 base32_encode(b2, STR(b1), LEN(b1)); 251 base32_encode(b1, STR(b2), LEN(b2)); 252 DECODE(b2, STR(b1), LEN(b1)); 253 DECODE(b1, STR(b2), LEN(b2)); 254 DECODE(b2, STR(b1), LEN(b1)); 255 DECODE(b1, STR(b2), LEN(b2)); 256 DECODE(b2, STR(b1), LEN(b1)); 257 VERIFY(STR(b2), STR(test), LEN(test)); 258 vstring_truncate(test, LEN(test) - 1); 259 } 260 vstring_free(test); 261 vstring_free(b1); 262 vstring_free(b2); 263 return (0); 264} 265 266#endif 267