1 /*****************************************************************************
2  * Author:   Valient Gough <vgough@pobox.com>
3  *
4  *****************************************************************************
5  * Copyright (c) 2004-2011, 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 "BlockNameIO.h"
22 
23 #include <cstring>
24 #include <memory>
25 #include <utility>
26 
27 #include "Cipher.h"
28 #include "CipherKey.h"
29 #include "Error.h"
30 #include "Interface.h"
31 #include "NameIO.h"
32 #include "base64.h"
33 #include "easylogging++.h"
34 #include "intl/gettext.h"
35 
36 namespace encfs {
37 
38 static std::shared_ptr<NameIO> NewBlockNameIO(
39     const Interface &iface, const std::shared_ptr<Cipher> &cipher,
40     const CipherKey &key) {
41   int blockSize = 8;
42   if (cipher) {
43     blockSize = cipher->cipherBlockSize();
44   }
45 
46   return std::shared_ptr<NameIO>(
47       new BlockNameIO(iface, cipher, key, blockSize, false));
48 }
49 
50 static std::shared_ptr<NameIO> NewBlockNameIO32(
51     const Interface &iface, const std::shared_ptr<Cipher> &cipher,
52     const CipherKey &key) {
53   int blockSize = 8;
54   if (cipher) {
55     blockSize = cipher->cipherBlockSize();
56   }
57 
58   return std::shared_ptr<NameIO>(
59       new BlockNameIO(iface, cipher, key, blockSize, true));
60 }
61 
62 static bool BlockIO_registered = NameIO::Register(
63     "Block",
64     // description of block name encoding algorithm..
65     // xgroup(setup)
66     gettext_noop("Block encoding, hides file name size somewhat"),
67     BlockNameIO::CurrentInterface(false), NewBlockNameIO);
68 
69 static bool BlockIO32_registered = NameIO::Register(
70     "Block32",
71     // description of block name encoding algorithm..
72     // xgroup(setup)
73     gettext_noop(
74         "Block encoding with base32 output for case-insensitive systems"),
75     BlockNameIO::CurrentInterface(true), NewBlockNameIO32);
76 
77 /*
78     - Version 1.0 computed MAC over the filename, but not the padding bytes.
79       This version was from pre-release 1.1, never publically released, so no
80       backward compatibility necessary.
81 
82     - Version 2.0 includes padding bytes in MAC computation.  This way the MAC
83       computation uses the same number of bytes regardless of the number of
84       padding bytes.
85 
86     - Version 3.0 uses full 64 bit initialization vector during IV chaining.
87       Prior versions used only the output from the MAC_16 call, giving a 1 in
88       2^16 chance of the same name being produced.  Using the full 64 bit IV
89       changes that to a 1 in 2^64 chance..
90 
91     - Version 4.0 adds support for base32, creating names more suitable for
92       case-insensitive filesystems (eg Mac).
93 */
94 Interface BlockNameIO::CurrentInterface(bool caseInsensitive) {
95   // implement major version 4 plus support for two prior versions
96   if (caseInsensitive) {
97     return Interface("nameio/block32", 4, 0, 2);
98   }
99   return Interface("nameio/block", 4, 0, 2);
100 }
101 
102 BlockNameIO::BlockNameIO(const Interface &iface, std::shared_ptr<Cipher> cipher,
103                          CipherKey key, int blockSize,
104                          bool caseInsensitiveEncoding)
105     : _interface(iface.current()),
106       _bs(blockSize),
107       _cipher(std::move(cipher)),
108       _key(std::move(key)),
109       _caseInsensitive(caseInsensitiveEncoding) {
110   // just to be safe..
111   rAssert(blockSize < 128);
112 }
113 
114 BlockNameIO::~BlockNameIO() = default;
115 
116 Interface BlockNameIO::interface() const {
117   return CurrentInterface(_caseInsensitive);
118 }
119 
120 int BlockNameIO::maxEncodedNameLen(int plaintextNameLen) const {
121   // number of blocks, rounded up.. Only an estimate at this point, err on
122   // the size of too much space rather then too little.
123   int numBlocks = (plaintextNameLen + _bs) / _bs;
124   int encodedNameLen = numBlocks * _bs + 2;  // 2 checksum bytes
125   if (_caseInsensitive) {
126     return B256ToB32Bytes(encodedNameLen);
127   }
128   return B256ToB64Bytes(encodedNameLen);
129 }
130 
131 int BlockNameIO::maxDecodedNameLen(int encodedNameLen) const {
132   int decLen256 = _caseInsensitive ? B32ToB256Bytes(encodedNameLen)
133                                    : B64ToB256Bytes(encodedNameLen);
134   return decLen256 - 2;  // 2 checksum bytes removed..
135 }
136 
137 int BlockNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv,
138                             char *encodedName, int bufferLength) const {
139 
140   // Pad encryption buffer to block boundary..
141   int padding = _bs - length % _bs;
142   if (padding == 0) {
143     padding = _bs;  // padding a full extra block!
144   }
145 
146   rAssert(bufferLength >= length + 2 + padding);
147   memset(encodedName + length + 2, (unsigned char)padding, padding);
148 
149   // copy the data into the encoding buffer..
150   memcpy(encodedName + 2, plaintextName, length);
151 
152   // store the IV before it is modified by the MAC call.
153   uint64_t tmpIV = 0;
154   if ((iv != nullptr) && _interface >= 3) {
155     tmpIV = *iv;
156   }
157 
158   // include padding in MAC computation
159   unsigned int mac = _cipher->MAC_16((unsigned char *)encodedName + 2,
160                                      length + padding, _key, iv);
161 
162   // add checksum bytes
163   encodedName[0] = (mac >> 8) & 0xff;
164   encodedName[1] = (mac)&0xff;
165 
166   bool ok;
167   ok = _cipher->blockEncode((unsigned char *)encodedName + 2, length + padding,
168                             (uint64_t)mac ^ tmpIV, _key);
169   if (!ok) {
170     throw Error("block encode failed in filename encode");
171   }
172 
173   // convert to base 64 ascii
174   int encodedStreamLen = length + 2 + padding;
175   int encLen;
176 
177   if (_caseInsensitive) {
178     encLen = B256ToB32Bytes(encodedStreamLen);
179 
180     changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 5,
181                       true);
182     B32ToAscii((unsigned char *)encodedName, encLen);
183   } else {
184     encLen = B256ToB64Bytes(encodedStreamLen);
185 
186     changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6,
187                       true);
188     B64ToAscii((unsigned char *)encodedName, encLen);
189   }
190 
191   return encLen;
192 }
193 
194 int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv,
195                             char *plaintextName, int bufferLength) const {
196   int decLen256 =
197       _caseInsensitive ? B32ToB256Bytes(length) : B64ToB256Bytes(length);
198   int decodedStreamLen = decLen256 - 2;
199 
200   // don't bother trying to decode files which are too small
201   if (decodedStreamLen < _bs) {
202     VLOG(1) << "Rejecting filename " << encodedName;
203     throw Error("Filename too small to decode");
204   }
205 
206   BUFFER_INIT(tmpBuf, 32, (unsigned int)length);
207 
208   // decode into tmpBuf,
209   if (_caseInsensitive) {
210     AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
211     changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false);
212   } else {
213     AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length);
214     changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false);
215   }
216 
217   // pull out the header information
218   unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 |
219                      ((unsigned int)((unsigned char)tmpBuf[1]));
220 
221   uint64_t tmpIV = 0;
222   if ((iv != nullptr) && _interface >= 3) {
223     tmpIV = *iv;
224   }
225 
226   bool ok;
227   ok = _cipher->blockDecode((unsigned char *)tmpBuf + 2, decodedStreamLen,
228                             (uint64_t)mac ^ tmpIV, _key);
229   if (!ok) {
230     throw Error("block decode failed in filename decode");
231   }
232 
233   // find out true string length
234   int padding = (unsigned char)tmpBuf[2 + decodedStreamLen - 1];
235   int finalSize = decodedStreamLen - padding;
236 
237   // might happen if there is an error decoding..
238   if (padding > _bs || finalSize < 0) {
239     VLOG(1) << "padding, _bx, finalSize = " << padding << ", " << _bs << ", "
240             << finalSize;
241     throw Error("invalid padding size");
242   }
243 
244   // copy out the result..
245   rAssert(finalSize < bufferLength);
246   memcpy(plaintextName, tmpBuf + 2, finalSize);
247   plaintextName[finalSize] = '\0';
248 
249   // check the mac
250   unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf + 2,
251                                       decodedStreamLen, _key, iv);
252 
253   BUFFER_RESET(tmpBuf);
254 
255   if (mac2 != mac) {
256     VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2
257             << " on decode of " << finalSize << " bytes";
258     throw Error("checksum mismatch in filename decode");
259   }
260 
261   return finalSize;
262 }
263 
264 bool BlockNameIO::Enabled() { return true; }
265 
266 }  // namespace encfs
267