1 /*****************************************************************************
2 * Author: Valient Gough <vgough@pobox.com>
3 *
4 *****************************************************************************
5 * Copyright (c) 2002-2004, Valient Gough
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "base64.h"
22
23 #include <cctype> // for toupper
24
25 #include "Error.h"
26
27 namespace encfs {
28
29 // change between two powers of two, stored as the low bits of the bytes in the
30 // arrays.
31 // It is the caller's responsibility to make sure the output array is large
32 // enough.
changeBase2(unsigned char * src,int srcLen,int src2Pow,unsigned char * dst,int dstLen,int dst2Pow)33 void changeBase2(unsigned char *src, int srcLen, int src2Pow,
34 unsigned char *dst, int dstLen, int dst2Pow) {
35 unsigned long work = 0;
36 int workBits = 0; // number of bits left in the work buffer
37 unsigned char *end = src + srcLen;
38 unsigned char *origDst = dst;
39 const int mask = (1 << dst2Pow) - 1;
40
41 // copy the new bits onto the high bits of the stream.
42 // The bits that fall off the low end are the output bits.
43 while (src != end) {
44 work |= ((unsigned long)(*src++)) << workBits;
45 workBits += src2Pow;
46
47 while (workBits >= dst2Pow) {
48 *dst++ = work & mask;
49 work >>= dst2Pow;
50 workBits -= dst2Pow;
51 }
52 }
53
54 // now, we could have a partial value left in the work buffer..
55 if ((workBits != 0) && ((dst - origDst) < dstLen)) {
56 *dst++ = work & mask;
57 }
58 }
59
60 /*
61 Same as changeBase2, except the output is written over the input data. The
62 output is assumed to be large enough to accept the data.
63
64 Uses the stack to store output values. Recurse every time a new value is
65 to be written, then write the value at the tail end of the recursion.
66 */
changeBase2Inline(unsigned char * src,int srcLen,int src2Pow,int dst2Pow,bool outputPartialLastByte,unsigned long work,int workBits,unsigned char * outLoc)67 static void changeBase2Inline(unsigned char *src, int srcLen, int src2Pow,
68 int dst2Pow, bool outputPartialLastByte,
69 unsigned long work, int workBits,
70 unsigned char *outLoc) {
71 const int mask = (1 << dst2Pow) - 1;
72 if (outLoc == nullptr) {
73 outLoc = src;
74 }
75
76 // copy the new bits onto the high bits of the stream.
77 // The bits that fall off the low end are the output bits.
78 while ((srcLen != 0) && workBits < dst2Pow) {
79 work |= ((unsigned long)(*src++)) << workBits;
80 workBits += src2Pow;
81 --srcLen;
82 }
83
84 // we have at least one value that can be output
85 unsigned char outVal = work & mask;
86 work >>= dst2Pow;
87 workBits -= dst2Pow;
88
89 if (srcLen != 0) {
90 // more input left, so recurse
91 changeBase2Inline(src, srcLen, src2Pow, dst2Pow, outputPartialLastByte,
92 work, workBits, outLoc + 1);
93 *outLoc = outVal;
94 } else {
95 // no input left, we can write remaining values directly
96 *outLoc++ = outVal;
97
98 // we could have a partial value left in the work buffer..
99 if (outputPartialLastByte) {
100 while (workBits > 0) {
101 *outLoc++ = work & mask;
102 work >>= dst2Pow;
103 workBits -= dst2Pow;
104 }
105 }
106 }
107 }
108
changeBase2Inline(unsigned char * src,int srcLen,int src2Pow,int dst2Pow,bool outputPartialLastByte)109 void changeBase2Inline(unsigned char *src, int srcLen, int src2Pow, int dst2Pow,
110 bool outputPartialLastByte) {
111 changeBase2Inline(src, srcLen, src2Pow, dst2Pow, outputPartialLastByte, 0, 0,
112 nullptr);
113 }
114
115 // character set for ascii b64:
116 // ",-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
117 // a standard base64 (eg a64l doesn't use ',-' but uses './'. We don't
118 // do that because '/' is a reserved character, and it is useful not to have
119 // '.' included in the encrypted names, so that it can be reserved for files
120 // with special meaning.
121 static const char B642AsciiTable[] = ",-0123456789";
B64ToAscii(unsigned char * in,int length)122 void B64ToAscii(unsigned char *in, int length) {
123 for (int offset = 0; offset < length; ++offset) {
124 int ch = in[offset];
125 if (ch > 11) {
126 if (ch > 37) {
127 ch += 'a' - 38;
128 } else {
129 ch += 'A' - 12;
130 }
131 } else {
132 ch = B642AsciiTable[ch];
133 }
134
135 in[offset] = ch;
136 }
137 }
138
139 static const unsigned char Ascii2B64Table[] =
140 " 01 23456789:; ";
141 // 0123456789 123456789 123456789 123456789 123456789 123456789 1234
142 // 0 1 2 3 4 5 6
AsciiToB64(unsigned char * in,int length)143 void AsciiToB64(unsigned char *in, int length) {
144 return AsciiToB64(in, in, length);
145 }
146
AsciiToB64(unsigned char * out,const unsigned char * in,int length)147 void AsciiToB64(unsigned char *out, const unsigned char *in, int length) {
148 while ((length--) != 0) {
149 unsigned char ch = *in++;
150 if (ch >= 'A') {
151 if (ch >= 'a') {
152 ch += 38 - 'a';
153 } else {
154 ch += 12 - 'A';
155 }
156 } else {
157 ch = Ascii2B64Table[ch] - '0';
158 }
159 *out++ = ch;
160 }
161 }
162
B32ToAscii(unsigned char * buf,int len)163 void B32ToAscii(unsigned char *buf, int len) {
164 for (int offset = 0; offset < len; ++offset) {
165 int ch = buf[offset];
166 if (ch >= 0 && ch < 26) {
167 ch += 'A';
168 } else {
169 ch += '2' - 26;
170 }
171
172 buf[offset] = ch;
173 }
174 }
175
AsciiToB32(unsigned char * in,int length)176 void AsciiToB32(unsigned char *in, int length) {
177 return AsciiToB32(in, in, length);
178 }
179
AsciiToB32(unsigned char * out,const unsigned char * in,int length)180 void AsciiToB32(unsigned char *out, const unsigned char *in, int length) {
181 while ((length--) != 0) {
182 unsigned char ch = *in++;
183 int lch = toupper(ch);
184 if (lch >= 'A') {
185 lch -= 'A';
186 } else {
187 lch += 26 - '2';
188 }
189 *out++ = (unsigned char)lch;
190 }
191 }
192
193 #define WHITESPACE 64
194 #define EQUALS 65
195 #define INVALID 66
196
197 static const unsigned char d[] = {
198 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66,
199 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
200 66, 66, 66, 66, 66, 66, 66, 66, 66, 62, 66, 66, 66, 63, 52, 53, 54,
201 55, 56, 57, 58, 59, 60, 61, 66, 66, // 50-59
202 66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
203 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 66, 66, 66,
204 66, 66, 66, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100-109
205 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
206
B64StandardDecode(unsigned char * out,const unsigned char * in,int inLen)207 bool B64StandardDecode(unsigned char *out, const unsigned char *in, int inLen) {
208 const unsigned char *end = in + inLen;
209 size_t buf = 1;
210
211 while (in < end) {
212 unsigned char v = *in++;
213 if (v > 'z') {
214 RLOG(ERROR) << "Invalid character: " << (unsigned int)v;
215 return false;
216 }
217 unsigned char c = d[v];
218
219 switch (c) {
220 case WHITESPACE:
221 continue; /* skip whitespace */
222 case INVALID:
223 RLOG(ERROR) << "Invalid character: " << (unsigned int)v;
224 return false; /* invalid input, return error */
225 case EQUALS: /* pad character, end of data */
226 in = end;
227 continue;
228 default:
229 buf = buf << 6 | c;
230
231 /* If the buffer is full, split it into bytes */
232 if ((buf & 0x1000000) != 0u) {
233 *out++ = buf >> 16;
234 *out++ = buf >> 8;
235 *out++ = buf;
236 buf = 1;
237 }
238 }
239 }
240
241 if ((buf & 0x40000) != 0u) {
242 *out++ = buf >> 10;
243 *out++ = buf >> 2;
244 } else if ((buf & 0x1000) != 0u) {
245 *out++ = buf >> 4;
246 }
247
248 return true;
249 }
250
251 // Lookup table for encoding
252 // If you want to use an alternate alphabet, change the characters here
253 const static char encodeLookup[] =
254 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
B64StandardEncode(const std::vector<unsigned char> & inputBuffer)255 std::string B64StandardEncode(const std::vector<unsigned char> &inputBuffer) {
256 std::string encodedString;
257 encodedString.reserve(B256ToB64Bytes(inputBuffer.size()));
258 long temp;
259 auto cursor = inputBuffer.begin();
260 for (size_t idx = 0; idx < inputBuffer.size() / 3; idx++) {
261 temp = (*cursor++) << 16; // Convert to big endian
262 temp += (*cursor++) << 8;
263 temp += (*cursor++);
264 encodedString.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]);
265 encodedString.append(1, encodeLookup[(temp & 0x0003F000) >> 12]);
266 encodedString.append(1, encodeLookup[(temp & 0x00000FC0) >> 6]);
267 encodedString.append(1, encodeLookup[(temp & 0x0000003F)]);
268 }
269
270 switch (inputBuffer.size() % 3) {
271 case 1:
272 temp = (*cursor++) << 16; // Convert to big endian
273 encodedString.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]);
274 encodedString.append(1, encodeLookup[(temp & 0x0003F000) >> 12]);
275 encodedString.append(2, '=');
276 break;
277 case 2:
278 temp = (*cursor++) << 16; // Convert to big endian
279 temp += (*cursor++) << 8;
280 encodedString.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]);
281 encodedString.append(1, encodeLookup[(temp & 0x0003F000) >> 12]);
282 encodedString.append(1, encodeLookup[(temp & 0x00000FC0) >> 6]);
283 encodedString.append(1, '=');
284 break;
285 }
286 return encodedString;
287 }
288
289 } // namespace encfs
290