fileman.cpp 13 KB

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