fileman.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. #include "../include/fileman.h"
  2. #include "../include/base64.h"
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <iostream>
  6. using std::string;
  7. using std::vector;
  8. FileMan::FileMan() {
  9. isputting = false;
  10. islisting = false;
  11. keyenabled = false;
  12. cryptoreadye = false;
  13. cryptoreadyd = false;
  14. ERR_load_crypto_strings();
  15. }
  16. FileMan::~FileMan() {
  17. cancelGet();
  18. cancelPut();
  19. cancelList();
  20. if (keyenabled)
  21. closeKey();
  22. ERR_free_strings();
  23. }
  24. bool FileMan::isGetting() { return getfile.is_open(); }
  25. bool FileMan::isPutting() { return isputting; }
  26. bool FileMan::isListing() { return islisting; }
  27. bool FileMan::isListingSimple() { return islisting && !extendedListing; }
  28. bool FileMan::isListingExtended() { return islisting && extendedListing; }
  29. bool FileMan::isEncrypted() { return keyenabled; }
  30. bool FileMan::openPut(const string &path) {
  31. std::fstream keyfile;
  32. putpath = path;
  33. putname = pathToFilename(path);
  34. putfile.open(path, std::ios::ate | std::ios::binary);
  35. if (putfile.is_open()) {
  36. size_t size = putfile.tellg();
  37. //~ putsize = size;
  38. // increment by one for CCATs signature in its own chunk
  39. //~ putchunks = 1 + (size / max_read_len + ((size % max_read_len) ? 1 : 0));
  40. if (keyenabled) {
  41. // fill IV for file
  42. keyfile.open("/dev/urandom", std::ios::binary | std::ios::in);
  43. keyfile.read((char *)iv, sizeof(iv));
  44. keyfile.close();
  45. }
  46. putfile.seekg(0);
  47. // if crypto enabled, encrypt now
  48. if (keyenabled) {
  49. cipherpath = path + ".tmp";
  50. if (!!std::ifstream(cipherpath)) {
  51. // close put if tmp file already exists
  52. closePut();
  53. return false;
  54. }
  55. std::ofstream cipherFile(cipherpath, std::ios::binary);
  56. if (!cipherFile) {
  57. closePut();
  58. return false;
  59. }
  60. // resize buffer to also fit IV and tag
  61. size_t additionalsize = sizeof(iv) + sizeof(tag);
  62. // skip first 32 bytes. Those are needed for the header
  63. cipherFile.seekp(additionalsize);
  64. if (!initCryptoE()) {
  65. // failed to init crypto, do not continue
  66. return false;
  67. }
  68. unsigned char *plain = new unsigned char[max_read_len];
  69. unsigned char *cipher = new unsigned char[max_read_len];
  70. int cipherlen;
  71. // prepend signature
  72. memcpy(plain, signature, sizeof(signature));
  73. if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, plain, sizeof(signature))) {
  74. setOpensslError();
  75. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  76. }
  77. cipherFile.write((char *)cipher, cipherlen);
  78. while ((std::size_t)(putfile.tellg()) + max_read_len < size) {
  79. putfile.read((char *)plain, max_read_len);
  80. if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, plain, max_read_len)) {
  81. setOpensslError();
  82. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  83. }
  84. cipherFile.write((char *)cipher, cipherlen);
  85. }
  86. if (putfile.tellg() < size - 1) {
  87. int rest = size - putfile.tellg();
  88. putfile.read((char *)plain, rest);
  89. if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, plain, rest)) {
  90. setOpensslError();
  91. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  92. }
  93. cipherFile.write((char *)cipher, cipherlen);
  94. }
  95. if (!EVP_EncryptFinal_ex(cryptctxe, cipher, &cipherlen)) {
  96. setOpensslError();
  97. std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
  98. }
  99. // obtain tag
  100. if (!EVP_CIPHER_CTX_ctrl(cryptctxe, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
  101. setOpensslError();
  102. std::cerr << __PRETTY_FUNCTION__ << " failed to get tag " << getOpensslError() << std::endl;
  103. }
  104. // prepend IV and tag
  105. cipherFile.seekp(0);
  106. cipherFile.write((char *)iv, sizeof(iv));
  107. cipherFile.write((char *)tag, sizeof(tag));
  108. cipherFile.close();
  109. putfile.close();
  110. delete[] cipher;
  111. delete[] plain;
  112. // now open cipher file as put file
  113. putfile.open(cipherpath, std::ios::ate | std::ios::binary);
  114. if (putfile.is_open()) {
  115. size = putfile.tellg();
  116. putfile.seekg(0);
  117. } else {
  118. closePut();
  119. return false;
  120. }
  121. deinitCryptoE();
  122. } else {
  123. size += sizeof(signature);
  124. }
  125. // calculate chunks
  126. putsize = size;
  127. putchunksRemaining = putchunks = size / max_read_len + (size % max_read_len > 0 ? 1 : 0);
  128. isputting = true;
  129. return true;
  130. }
  131. return false;
  132. }
  133. bool FileMan::openGet(const string &path) {
  134. getpath = path;
  135. getname = pathToFilename(path);
  136. getchunks = 0;
  137. getchunksRemaining = 0;
  138. getfile.open(path, std::ios::app | std::ios::binary | std::ios::out);
  139. if (getfile.tellp() != std::ios::beg) {
  140. closeGet();
  141. return false;
  142. }
  143. return true;
  144. }
  145. bool FileMan::openList(bool extended) {
  146. if (isListing()) {
  147. return false;
  148. }
  149. listdata = vector<Json::Value>();
  150. extendedListing = extended;
  151. islisting = true;
  152. return true;
  153. }
  154. bool FileMan::openKey(const string &path) {
  155. std::ifstream keyfile;
  156. //~ std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
  157. if (isPutting() || isGetting())
  158. return false; // do not enable key mid-operation
  159. if (keyenabled)
  160. closeKey();
  161. keyfile.open(path, std::ios::ate | std::ios::binary);
  162. //~ std::cerr << __PRETTY_FUNCTION__ << " open keyfile" << std::endl;
  163. if (keyfile.is_open()) {
  164. //~ std::cerr << __PRETTY_FUNCTION__ << " keyfile open " << keyfile.tellg() << " " << sizeof(key) << std::endl;
  165. if (keyfile.tellg() == sizeof(key)) {
  166. keyfile.seekg(0);
  167. //~ std::cerr << "keyfile is at " << keyfile.tellg() << std::endl;
  168. keyfile.read((char *)key, sizeof(key));
  169. keyfile.close();
  170. keyenabled = true;
  171. return true;
  172. }
  173. }
  174. return false;
  175. }
  176. void FileMan::closePut() {
  177. putfile.close();
  178. putname = "";
  179. putpath = "";
  180. putchunks = 0;
  181. putchunksRemaining = 0;
  182. isputting = false;
  183. memset(iv, 0, sizeof(iv));
  184. memset(tag, 0, sizeof(tag));
  185. if (keyenabled && !!std::ifstream(cipherpath)) {
  186. // delete tmp cipher file if it exists
  187. std::remove(cipherpath.c_str());
  188. }
  189. }
  190. void FileMan::closeGet() {
  191. getfile.close();
  192. getname = "";
  193. getpath = "";
  194. getchunks = 0;
  195. getchunksRemaining = 0;
  196. memset(iv, 0, sizeof(iv));
  197. memset(tag, 0, sizeof(tag));
  198. }
  199. void FileMan::closeList() {
  200. listdata = vector<Json::Value>();
  201. islisting = false;
  202. listchunks = 0;
  203. listchunksRemaining = 0;
  204. }
  205. bool FileMan::closeKey() {
  206. if (isPutting() || isGetting())
  207. return false;
  208. if (keyenabled) {
  209. memset(key, 0, sizeof(key));
  210. keyenabled = false;
  211. }
  212. return true;
  213. }
  214. void FileMan::cancelPut() {
  215. if (isPutting()) {
  216. closePut();
  217. }
  218. }
  219. void FileMan::cancelGet() {
  220. if (isGetting()) {
  221. closeGet();
  222. std::remove(getname.c_str());
  223. }
  224. }
  225. void FileMan::cancelList() {
  226. if (isListing()) {
  227. closeList();
  228. }
  229. }
  230. void FileMan::writeGet(const vector<char> data) {
  231. if (getchunksRemaining == getchunks - 1 && !keyenabled) {
  232. // check if signature matches
  233. if (memcmp(data.data(), signature, 4)) {
  234. // mismatch, encrypted file without enabled key, write as is
  235. getfile.write(data.data(), data.size());
  236. } else {
  237. // skip signature for unencrypted files
  238. getfile.write(data.data() + 4, data.size() - 4);
  239. }
  240. } else {
  241. getfile.write(data.data(), data.size());
  242. }
  243. getchunksRemaining--;
  244. }
  245. void FileMan::writeBase64(string data) {
  246. vector<char> decode = base64::decodeVector(data);
  247. keyenabled ? writeEnc(decode) : writeGet(decode);
  248. }
  249. void FileMan::putListData(vector<Json::Value> files) {
  250. if (!extendedListing) {
  251. listdata.insert(listdata.end(), files.begin(), files.end());
  252. } else {
  253. for (Json::Value file : files) {
  254. Json::Value fileInfo;
  255. fileInfo["name"] = file["name"];
  256. fileInfo["size"] = file["size"];
  257. string headString = file["head"].asString();
  258. decryptability decr = isDecryptable(base64::decodeVector(headString));
  259. if (decr == FileMan::unknown) {
  260. fileInfo["encrypted"] = "unknown";
  261. } else if (decr == FileMan::undecryptable) {
  262. fileInfo["encrypted"] = "undecryptable";
  263. } else if (decr == FileMan::decryptable) {
  264. fileInfo["encrypted"] = "decryptable";
  265. } else {
  266. fileInfo["encrypted"] = "unencrypted";
  267. }
  268. listdata.push_back(fileInfo);
  269. }
  270. }
  271. listchunksRemaining--;
  272. }
  273. vector<Json::Value> FileMan::getListData() { return listdata; }
  274. int FileMan::getGetChunks() { return getchunks; }
  275. int FileMan::getGetRemainingChunks() { return getchunksRemaining; }
  276. void FileMan::setGetChunks(int chunks) {
  277. getchunks = chunks;
  278. getchunksRemaining = chunks - 1;
  279. }
  280. void FileMan::setListChunks(int chunks) {
  281. listchunks = chunks;
  282. listchunksRemaining = chunks - 1;
  283. }
  284. vector<char> FileMan::readPut() {
  285. int chunk = putchunks - putchunksRemaining;
  286. vector<char> data;
  287. int toread = max_read_len;
  288. if (putchunksRemaining == 1) {
  289. toread = putsize - (putchunks - 1) * max_read_len;
  290. }
  291. data.resize(toread);
  292. if (chunk == 0 && !keyenabled) {
  293. memcpy(&data.front(), signature, sizeof(signature));
  294. putfile.read(&data.front() + sizeof(signature), toread - sizeof(signature));
  295. } else {
  296. putfile.read(&data.front(), toread);
  297. }
  298. putchunksRemaining--;
  299. return data;
  300. }
  301. string FileMan::readBase64() { return base64::encodeVector(readPut()); }
  302. string FileMan::getPutName() { return putname; }
  303. string FileMan::getGetName() { return getname; }
  304. int FileMan::getPutChunks() { return putchunks; }
  305. int FileMan::getPutRemainingChunks() { return putchunksRemaining; }
  306. int FileMan::getPutSize() { return putsize; }
  307. int FileMan::getListRemainingChunks() { return listchunksRemaining; }
  308. int FileMan::getListChunks() { return listchunks; }
  309. string FileMan::pathToFilename(string path) {
  310. int lastFoundIndex = -1;
  311. for (int currentIndex = path.find("/"); currentIndex != string::npos; currentIndex = path.find("/", currentIndex + 1)) {
  312. // check if the "/" was escaped
  313. if (currentIndex > 0 && path[currentIndex - 1] == '\\')
  314. continue;
  315. /* // check unnecessary, because error occurs when trying to open file
  316. anyways
  317. // check if the "/" is at the end of path name
  318. if (currentIndex + 1 == path.length()) {
  319. // ERROR: INVALID FILENAME, BECAUSE ENDS WITH "/"
  320. }
  321. */
  322. // otherwise we found a valid "/"
  323. lastFoundIndex = currentIndex;
  324. }
  325. return path.substr(lastFoundIndex + 1);
  326. }
  327. string FileMan::getOpensslError() {
  328. pendingerr = false;
  329. return opensslerr;
  330. }
  331. void FileMan::setOpensslError() {
  332. opensslerr = ERR_error_string(ERR_get_error(), NULL);
  333. pendingerr = true;
  334. }
  335. bool FileMan::initCryptoE() {
  336. // try to initialize crypto context
  337. if (!cryptoreadye) {
  338. //~ std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
  339. if (!(cryptctxe = EVP_CIPHER_CTX_new())) {
  340. setOpensslError();
  341. return false;
  342. }
  343. if (!EVP_EncryptInit_ex(cryptctxe, EVP_aes_256_gcm(), NULL, key, iv)) {
  344. setOpensslError();
  345. return false;
  346. }
  347. cryptoreadye = true;
  348. }
  349. return true;
  350. }
  351. bool FileMan::initCryptoD() {
  352. // try to initialize crypto context
  353. if (!cryptoreadyd) {
  354. //~ std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
  355. if (!(cryptctxd = EVP_CIPHER_CTX_new())) {
  356. setOpensslError();
  357. return false;
  358. }
  359. if (!EVP_DecryptInit_ex(cryptctxd, EVP_aes_256_gcm(), NULL, key, iv)) {
  360. setOpensslError();
  361. return false;
  362. }
  363. cryptoreadyd = true;
  364. }
  365. return true;
  366. }
  367. void FileMan::deinitCryptoE() {
  368. if (cryptoreadye) {
  369. EVP_CIPHER_CTX_free(cryptctxe);
  370. cryptctxe = NULL;
  371. cryptoreadye = false;
  372. }
  373. }
  374. void FileMan::deinitCryptoD() {
  375. if (cryptoreadyd) {
  376. EVP_CIPHER_CTX_free(cryptctxd);
  377. cryptctxd = NULL;
  378. cryptoreadyd = false;
  379. }
  380. }
  381. void FileMan::writeEnc(const vector<char> data) {
  382. //~ std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
  383. writeGet(data);
  384. if (getchunksRemaining < 0) {
  385. // loaded everything, try to decrypt
  386. unsigned char *cipher, *plain;
  387. int plainlen = 0, finallen = 0;
  388. getfile.close();
  389. getfile.open(getpath, std::ios::binary | std::ios::in | std::ios::ate);
  390. size_t size = getfile.tellg();
  391. if (size < (sizeof(iv) + sizeof(tag))) {
  392. // avoid underflow with files that are too small
  393. return;
  394. }
  395. size -= (sizeof(iv) + sizeof(tag));
  396. cipher = new unsigned char[size];
  397. plain = new unsigned char[size];
  398. getfile.seekg(0);
  399. getfile.read((char *)iv, sizeof(iv));
  400. getfile.read((char *)tag, sizeof(tag));
  401. getfile.read((char *)cipher, size);
  402. getfile.close();
  403. if (initCryptoD()) {
  404. if (!EVP_DecryptUpdate(cryptctxd, plain, &plainlen, cipher, size)) {
  405. setOpensslError();
  406. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  407. }
  408. //~ std::cerr << __PRETTY_FUNCTION__ << " decrypted" << std::endl;
  409. if (!EVP_CIPHER_CTX_ctrl(cryptctxd, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
  410. setOpensslError();
  411. std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
  412. }
  413. //~ std::cerr << __PRETTY_FUNCTION__ << " set tag" << std::endl;
  414. if (0 < EVP_DecryptFinal_ex(cryptctxd, plain + plainlen, &finallen)) {
  415. plainlen += finallen;
  416. //~ std::cerr << __PRETTY_FUNCTION__ << " finalized with len " << plainlen << std::endl;
  417. getfile.close();
  418. // check signature
  419. if (memcmp(plain, signature, 4)) {
  420. std::cerr << __PRETTY_FUNCTION__ << " signature mismatch" << std::endl;
  421. } else {
  422. // signatur matches, skip it and dump the rest to disk
  423. getfile.open(getpath, std::ios::binary | std::ios::out | std::ios::trunc);
  424. getfile.write((char *)(plain + sizeof(signature)), plainlen - sizeof(signature));
  425. getfile.close();
  426. }
  427. } else {
  428. setOpensslError();
  429. std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
  430. }
  431. deinitCryptoD();
  432. }
  433. delete[] cipher;
  434. delete[] plain;
  435. }
  436. }
  437. FileMan::decryptability FileMan::isDecryptable(const vector<char> data) {
  438. unsigned char plain[sizeof(signature)] = {0}, *cipher;
  439. int plainlen;
  440. // check if signature matches in plaintext
  441. if (data.size() < sizeof(signature) || memcmp(data.data(), signature, sizeof(signature))) {
  442. // either size or signature dont match
  443. if (data.size() >= (sizeof(signature) + sizeof(iv) + sizeof(tag)) && keyenabled) {
  444. // enough data, key is enabled
  445. memcpy(iv, data.data(), sizeof(iv));
  446. memcpy(tag, data.data() + sizeof(iv), sizeof(tag));
  447. cipher = (unsigned char *)(data.data() + sizeof(iv) + sizeof(tag));
  448. if (initCryptoD()) {
  449. // crypto is ready
  450. if (!EVP_DecryptUpdate(cryptctxd, plain, &plainlen, cipher, sizeof(signature))) {
  451. setOpensslError();
  452. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  453. }
  454. deinitCryptoD();
  455. // check again if signature matches
  456. if (memcmp(plain, signature, sizeof(signature))) {
  457. // still no dice
  458. return undecryptable;
  459. }
  460. // yup, got good data
  461. return decryptable;
  462. }
  463. }
  464. // size mismatch or no key enabled, consider unknown
  465. return unknown;
  466. }
  467. // it does, assume plaintext
  468. return plaintext;
  469. }