fileman.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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. isputting = false;
  158. memset(iv, 0, sizeof(iv));
  159. memset(tag, 0, sizeof(tag));
  160. }
  161. void FileMan::closeGet() {
  162. getfile.close();
  163. memset(iv, 0, sizeof(iv));
  164. memset(tag, 0, sizeof(tag));
  165. }
  166. void FileMan::closeList() { islisting = false; }
  167. bool FileMan::closeKey() {
  168. if (isPutting() || isGetting())
  169. return false;
  170. if (keyenabled) {
  171. memset(key, 0, sizeof(key));
  172. keyenabled = false;
  173. }
  174. return true;
  175. }
  176. void FileMan::cancelPut() {
  177. if (isPutting()) {
  178. closePut();
  179. }
  180. }
  181. void FileMan::cancelGet() {
  182. if (isGetting()) {
  183. closeGet();
  184. std::remove(getname.c_str());
  185. }
  186. }
  187. void FileMan::cancelList() {
  188. if (isListing()) {
  189. closeList();
  190. }
  191. }
  192. void FileMan::writeGet(const vector<char> data) {
  193. if (getchunksRemaining == getchunks - 1 && !keyenabled) {
  194. // check if signature matches
  195. if (memcmp(data.data(), signature, 4)) {
  196. // mismatch, encrypted file without enabled key, write as is
  197. getfile.write(data.data(), data.size());
  198. } else {
  199. // skip signature for unencrypted files
  200. getfile.write(data.data() + 4, data.size() - 4);
  201. }
  202. } else {
  203. getfile.write(data.data(), data.size());
  204. }
  205. getchunksRemaining--;
  206. }
  207. void FileMan::writeBase64(string data) {
  208. vector<char> decode = base64::decodeVector(data);
  209. keyenabled ? writeEnc(decode) : writeGet(decode);
  210. }
  211. void FileMan::putListData(vector<Json::Value> files) {
  212. if (!extendedListing) {
  213. listdata.insert(listdata.end(), files.begin(), files.end());
  214. } else {
  215. for (Json::Value file : files) {
  216. Json::Value fileInfo;
  217. fileInfo["name"] = file["name"];
  218. fileInfo["size"] = file["size"];
  219. string headString = file["head"].asString();
  220. decryptability decr = isDecryptable(base64::decodeVector(headString));
  221. if (decr == FileMan::unknown) {
  222. fileInfo["encrypted"] = "unknown";
  223. } else if (decr == FileMan::undecryptable) {
  224. fileInfo["encrypted"] = "undecryptable";
  225. } else if (decr == FileMan::decryptable) {
  226. fileInfo["encrypted"] = "decryptable";
  227. } else {
  228. fileInfo["encrypted"] = "unencrypted";
  229. }
  230. listdata.push_back(fileInfo);
  231. }
  232. }
  233. listchunksRemaining--;
  234. }
  235. vector<Json::Value> FileMan::getListData() { return listdata; }
  236. int FileMan::getGetChunks() { return getchunks; }
  237. int FileMan::getGetRemainingChunks() { return getchunksRemaining; }
  238. void FileMan::setGetChunks(int chunks) {
  239. getchunks = chunks;
  240. getchunksRemaining = chunks - 1;
  241. }
  242. void FileMan::setListChunks(int chunks) {
  243. listchunks = chunks;
  244. listchunksRemaining = chunks - 1;
  245. }
  246. vector<char> FileMan::readPut() {
  247. vector<char> data = putdata[putchunks - putchunksRemaining];
  248. putchunksRemaining--;
  249. return data;
  250. }
  251. string FileMan::readBase64() { return base64::encodeVector(readPut()); }
  252. string FileMan::getPutName() { return putname; }
  253. string FileMan::getGetName() { return getname; }
  254. int FileMan::getPutChunks() { return putchunks; }
  255. int FileMan::getPutRemainingChunks() { return putchunksRemaining; }
  256. int FileMan::getPutSize() { return putsize; }
  257. int FileMan::getListRemainingChunks() { return listchunksRemaining; }
  258. int FileMan::getListChunks() { return listchunks; }
  259. string FileMan::pathToFilename(string path) {
  260. int lastFoundIndex = -1;
  261. for (int currentIndex = path.find("/"); currentIndex != string::npos; currentIndex = path.find("/", currentIndex + 1)) {
  262. // check if the "/" was escaped
  263. if (currentIndex > 0 && path[currentIndex - 1] == '\\')
  264. continue;
  265. /* // check unnecessary, because error occurs when trying to open file
  266. anyways
  267. // check if the "/" is at the end of path name
  268. if (currentIndex + 1 == path.length()) {
  269. // ERROR: INVALID FILENAME, BECAUSE ENDS WITH "/"
  270. }
  271. */
  272. // otherwise we found a valid "/"
  273. lastFoundIndex = currentIndex;
  274. }
  275. return path.substr(lastFoundIndex + 1);
  276. }
  277. string FileMan::getOpensslError() {
  278. pendingerr = false;
  279. return opensslerr;
  280. }
  281. void FileMan::setOpensslError() {
  282. opensslerr = ERR_error_string(ERR_get_error(), NULL);
  283. pendingerr = true;
  284. }
  285. bool FileMan::initCryptoE() {
  286. // try to initialize crypto context
  287. if (!cryptoreadye) {
  288. std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
  289. if (!(cryptctxe = EVP_CIPHER_CTX_new())) {
  290. setOpensslError();
  291. return false;
  292. }
  293. if (!EVP_EncryptInit_ex(cryptctxe, EVP_aes_256_gcm(), NULL, key, iv)) {
  294. setOpensslError();
  295. return false;
  296. }
  297. cryptoreadye = true;
  298. }
  299. return true;
  300. }
  301. bool FileMan::initCryptoD() {
  302. // try to initialize crypto context
  303. if (!cryptoreadyd) {
  304. std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
  305. if (!(cryptctxd = EVP_CIPHER_CTX_new())) {
  306. setOpensslError();
  307. return false;
  308. }
  309. if (!EVP_DecryptInit_ex(cryptctxd, EVP_aes_256_gcm(), NULL, key, iv)) {
  310. setOpensslError();
  311. return false;
  312. }
  313. cryptoreadyd = true;
  314. }
  315. return true;
  316. }
  317. void FileMan::deinitCryptoE() {
  318. if (cryptoreadye) {
  319. EVP_CIPHER_CTX_free(cryptctxe);
  320. cryptctxe = NULL;
  321. cryptoreadye = false;
  322. }
  323. }
  324. void FileMan::deinitCryptoD() {
  325. if (cryptoreadyd) {
  326. EVP_CIPHER_CTX_free(cryptctxd);
  327. cryptctxd = NULL;
  328. cryptoreadyd = false;
  329. }
  330. }
  331. void FileMan::writeEnc(const vector<char> data) {
  332. //~ std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
  333. writeGet(data);
  334. if (getchunksRemaining < 0) {
  335. // loaded everything, try to decrypt
  336. unsigned char *cipher, *plain;
  337. int plainlen = 0, finallen = 0;
  338. getfile.close();
  339. getfile.open(getpath, std::ios::binary | std::ios::in | std::ios::ate);
  340. size_t size = getfile.tellg();
  341. if (size < (sizeof(iv) + sizeof(tag))) {
  342. // avoid underflow with files that are too small
  343. return;
  344. }
  345. size -= (sizeof(iv) + sizeof(tag));
  346. cipher = new unsigned char[size];
  347. plain = new unsigned char[size];
  348. getfile.seekg(0);
  349. getfile.read((char *)iv, sizeof(iv));
  350. getfile.read((char *)tag, sizeof(tag));
  351. getfile.read((char *)cipher, size);
  352. getfile.close();
  353. if (initCryptoD()) {
  354. if (!EVP_DecryptUpdate(cryptctxd, plain, &plainlen, cipher, size)) {
  355. setOpensslError();
  356. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  357. }
  358. std::cerr << __PRETTY_FUNCTION__ << " decrypted" << std::endl;
  359. if (!EVP_CIPHER_CTX_ctrl(cryptctxd, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
  360. setOpensslError();
  361. std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
  362. }
  363. std::cerr << __PRETTY_FUNCTION__ << " set tag" << std::endl;
  364. if (0 < EVP_DecryptFinal_ex(cryptctxd, plain + plainlen, &finallen)) {
  365. plainlen += finallen;
  366. std::cerr << __PRETTY_FUNCTION__ << " finalized with len " << plainlen << std::endl;
  367. getfile.close();
  368. // check signature
  369. if (memcmp(plain, signature, 4)) {
  370. std::cerr << __PRETTY_FUNCTION__ << " signature mismatch" << std::endl;
  371. } else {
  372. // signatur matches, skip it and dump the rest to disk
  373. getfile.open(getpath, std::ios::binary | std::ios::out | std::ios::trunc);
  374. getfile.write((char *)(plain + sizeof(signature)), plainlen - sizeof(signature));
  375. getfile.close();
  376. }
  377. } else {
  378. setOpensslError();
  379. std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
  380. }
  381. deinitCryptoD();
  382. }
  383. delete[] cipher;
  384. delete[] plain;
  385. }
  386. }
  387. FileMan::decryptability FileMan::isDecryptable(const vector<char> data) {
  388. unsigned char plain[sizeof(signature)] = {0}, *cipher;
  389. int plainlen;
  390. // check if signature matches in plaintext
  391. if (data.size() < sizeof(signature) || memcmp(data.data(), signature, sizeof(signature))) {
  392. // either size or signature dont match
  393. if (data.size() >= (sizeof(signature) + sizeof(iv) + sizeof(tag)) && keyenabled) {
  394. // enough data, key is enabled
  395. memcpy(iv, data.data(), sizeof(iv));
  396. memcpy(tag, data.data() + sizeof(iv), sizeof(tag));
  397. cipher = (unsigned char *)(data.data() + sizeof(iv) + sizeof(tag));
  398. if (initCryptoD()) {
  399. // crypto is ready
  400. if (!EVP_DecryptUpdate(cryptctxd, plain, &plainlen, cipher, sizeof(signature))) {
  401. setOpensslError();
  402. std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
  403. }
  404. deinitCryptoD();
  405. // check again if signature matches
  406. if (memcmp(plain, signature, sizeof(signature))) {
  407. // still no dice
  408. return undecryptable;
  409. }
  410. // yup, got good data
  411. return decryptable;
  412. }
  413. }
  414. // size mismatch or no key enabled, consider unknown
  415. return unknown;
  416. }
  417. // it does, assume plaintext
  418. return plaintext;
  419. }