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