fileman.cpp 13 KB

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