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