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