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