1 /*
2 * Copyright (c) 2018 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the names of Paul Mattes nor the names of his contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * base64.c
30 * Base64 encoding.
31 */
32
33 #include "globals.h"
34
35 #include "base64.h"
36
37 #define BITS_PER_BYTE 8
38 #define BITS_PER_BASE64 6
39 #define MASK64 0x3f
40 #define MASK256 0xff
41 #define PAD_BITS 2
42 #define MAX_PAD 2
43 #define BYTES_PER_BLOCK 3
44
45 /* The output alphabet. */
46 static char *alphabet64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
47
48 /*
49 * Encode a string in base64.
50 *
51 * Returns a malloc'd buffer.
52 */
53 char *
base64_encode(const char * s)54 base64_encode(const char *s)
55 {
56 /*
57 * We need one output character for every 6 bits of input, plus up to two
58 * padding characters, plus a terminaing NUL.
59 */
60 size_t nmalloc = (((strlen(s) * BITS_PER_BYTE) + (BITS_PER_BASE64 - 1)) / BITS_PER_BASE64) + MAX_PAD + 1;
61 char *ret = Malloc(nmalloc);
62 char *op = ret;
63 char c;
64 unsigned accum = 0; /* overflow bits */
65 int held_bits = 0; /* number of significant bits in 'accum' */
66 bool done = false;
67
68 while (!done) {
69 int i;
70 unsigned over;
71 unsigned pad_bits = 0;
72
73 /* Get the next 3 octets. */
74 for (i = 0; i < BYTES_PER_BLOCK; i++) {
75 if (!(c = *s++)) {
76 done = true;
77 break;
78 }
79 accum = (accum << BITS_PER_BYTE) | (unsigned char)c;
80 held_bits += BITS_PER_BYTE;
81 }
82
83 /* Pad to an even multiple of 6. */
84 over = held_bits % BITS_PER_BASE64;
85 if (over != 0) {
86 pad_bits = BITS_PER_BASE64 - over;
87 }
88 accum <<= pad_bits;
89 held_bits += pad_bits;
90
91 /* Emit the base64. */
92 while (held_bits > 0) {
93 *op++ = alphabet64[(accum >> BITS_PER_BASE64 * ((held_bits / BITS_PER_BASE64) - 1))
94 & MASK64];
95 held_bits -= BITS_PER_BASE64;
96 }
97
98 /* Emit padding indicators. */
99 while (pad_bits >= PAD_BITS) {
100 *op++ = '=';
101 pad_bits -= PAD_BITS;
102 }
103
104 /* Go around again. */
105 accum = 0;
106 held_bits = 0;
107 }
108
109 *op = '\0';
110 return ret;
111 }
112
113 /*
114 * Decode a base64 string.
115 *
116 * Returns a malloc'd buffer.
117 */
118 char *
base64_decode(const char * s)119 base64_decode(const char *s)
120 {
121 /* Do a little overkill on the decode buffer. */
122 char *ret = Malloc(strlen(s) + 1);
123 char *op = ret;
124 char c;
125 unsigned accum = 0;
126 unsigned accum_bits = 0;
127 int eq = 0;
128
129 while ((c = *s++)) {
130 if (c == '=') {
131 /* Each '=' is 2 bits of padding. */
132 eq++;
133 accum <<= PAD_BITS;
134 accum_bits += PAD_BITS;
135 } else {
136 char *ix = strchr(alphabet64, c);
137
138 if (eq > 0 || ix == NULL) {
139 /*
140 * Nothing can follow '=', and anything else needs to be in the
141 * alphabet.
142 */
143 Free(ret);
144 return NULL;
145 }
146 accum = (accum << BITS_PER_BASE64) | (ix - alphabet64);
147 accum_bits += BITS_PER_BASE64;
148 }
149
150 while (accum_bits >= BITS_PER_BYTE) {
151 *op++ = (accum >> (accum_bits % BITS_PER_BYTE)) & MASK256;
152 accum &= ~(MASK256 << (accum_bits % BITS_PER_BYTE));
153 accum_bits -= BITS_PER_BYTE;
154 }
155
156 if (eq > MAX_PAD) {
157 /* No more than 2 '='. */
158 Free(ret);
159 return NULL;
160 }
161 }
162
163 *op = '\0';
164 return ret;
165 }
166
167 #if defined UNIT_TEST /*[*/
168 #include <assert.h>
169 void *
Malloc(size_t n)170 Malloc(size_t n)
171 {
172 return malloc(n);
173 }
174
175 void
Free(void * buf)176 Free(void *buf)
177 {
178 free(buf);
179 }
180
181 int
main(int argc,char * argv[])182 main(int argc, char *argv[])
183 {
184 char *s[] = {
185 "foobar",
186 "x",
187 "xy",
188 "xyz",
189 "abcãd",
190 "username:password",
191 "Bzz Bzz Bzz",
192 NULL };
193 int i;
194
195 for (i = 0; s[i] != NULL; i++) {
196 char *b = base64_encode(s[i]);
197 char *e = base64_decode(b);
198
199 printf("'%s' -> '%s' -> '%s'\n", s[i], b, e);
200 assert (!strcmp(s[i], e));
201 }
202
203 assert(base64_decode("a=b") == NULL);
204 assert(base64_decode("a===") == NULL);
205 assert(base64_decode("[") == NULL);
206
207 return 0;
208 }
209 #endif /*]*/
210