Guest User

Untitled

a guest
Dec 17th, 2017
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.84 KB | None | 0 0
  1. /*
  2. * A simple database of user accounts.
  3. *
  4. * Author: Adam Gasior
  5. */
  6.  
  7. #include <cassert>
  8. #include <cstdio>
  9. #include <cstdlib>
  10. #include <fstream>
  11. #include <string>
  12. #include <unordered_map>
  13. #include <vector>
  14.  
  15.  
  16. // Utility functions.
  17.  
  18. /**
  19. * \brief Generates a hash for a given string.
  20. *
  21. * Takes each character of a given string and performs series of bitwise operations
  22. * on it and large prime numbers resulting in a huge number. Its purpose is to
  23. * represent any string of characters as a unique number, however collisions are
  24. * inevitable.
  25. *
  26. * \param str A string to hash.
  27. *
  28. * \return The resulting hash.
  29. */
  30. long long hash(std::string str)
  31. {
  32. // Taking some not too big prime.
  33. long long hash = 37;
  34.  
  35. // XOR-ing each character's multiply by a large prime with our previous prime's
  36. // multiply by another large prime.
  37. for (char& c : str)
  38. {
  39. hash = (hash * 54059) ^ (c * 76963);
  40. }
  41. return hash;
  42. }
  43.  
  44. /**
  45. * \brief Replaces characters of a string from one to other set of characters.
  46. *
  47. * Takes a string and based on a character's index in one character array replaces it with
  48. * a character of the same index from the other array. Used for keyword encryption.
  49. *
  50. * \param str The string to encrypt.
  51. * \param chars Set of characters from which index of <code>str</code>'s character is taken.
  52. * \param sybst Set of characters with which the characters of <code>str</code> are replaced.
  53. *
  54. * \return Encrypted version of <code>str</code>.
  55. */
  56. std::string replace(std::string str, std::string chars, std::string subst)
  57. {
  58. // These two strings must be of equal length.
  59. assert(chars.length() == subst.length());
  60.  
  61. // Iterating through str and chars to find the index of a str's character in chars.
  62. for (char& c : str) for (int i = 0; i < chars.length(); ++i)
  63. if (c == chars[i])
  64. {
  65. c = subst[i]; // If the character is found in chars then a str's character is
  66. break; // replaced with its corresponding version in subst.
  67. }
  68.  
  69. return str;
  70. }
  71.  
  72. /**
  73. * \brief Generates a random string of a given length, used as salt.
  74. *
  75. * \param length Desired length of the random string.
  76. *
  77. * \return A random string.
  78. */
  79. std::string randstr(size_t length)
  80. {
  81. // Initialising an empty string.
  82. std::string res = "";
  83.  
  84. // Generating specified number of characters.
  85. for (size_t i = 0; i < length; ++i)
  86. {
  87. res += [](char c) -> char {
  88. return (c != ',' && c != '\n' ? c : c + 1); // Making sure that the resulting string
  89. } // doesn't contain a comma or a new line
  90. ((char) rand() % 255 + 1); // character.
  91. }
  92. return res;
  93. }
  94.  
  95. /**
  96. * \brief Splits a string into an array of subsequent its parts.
  97. *
  98. * Takes a string and every time it encounters a delimiting character it adds a substring
  99. * between the previous and the current delimiter to an array. After the string ends, it
  100. * returns the array of segments without the delimiters.
  101. *
  102. * \param str The string to split.
  103. * \param delim Delimiter.
  104. *
  105. * \return The array of string segments.
  106. */
  107. std::vector<std::string> split(std::string str, char delim)
  108. {
  109. // Creating the array of segments.
  110. std::vector<std::string> segms;
  111.  
  112. // Initialising the auxiliary string storing current str's segment.
  113. std::string segm = "";
  114.  
  115. // Iterating through str.
  116. for (char c : str)
  117. {
  118. // If a delimiter is encountered, adding the segment to the array and
  119. // clearing it.
  120. if (c == delim)
  121. {
  122. segms.push_back(segm);
  123. segm = "";
  124. }
  125. else segm += c; // If not a delimiter, then it can be appended to the segment.
  126. }
  127.  
  128. // After iterating the last resulting segment can be added to the array,
  129. // given it is not empty.
  130. if (segm != "") segms.push_back(segm);
  131.  
  132. return segms;
  133. }
  134.  
  135.  
  136. // Structures.
  137.  
  138. /**
  139. * Data of a user account.
  140. */
  141. struct UserAccount
  142. {
  143. public:
  144. std::string username_enc; ///< Encrypted username.
  145. std::string salt_enc; ///< Encrypted salt.
  146. long long hashed_pword; ///< Hash of salted password.
  147.  
  148. std::string name_enc; ///< Encrypted first name.
  149. std::string surname_enc; ///< Encrypted surname.
  150. std::string age_enc; ///< Encrypted age.
  151. std::string favFood_enc; ///< Encrypted favourite food.
  152.  
  153. public:
  154. /**
  155. * \brief Creates a user account.
  156. *
  157. * Encrypts user account data, salts and hashes the password and construct a user
  158. * account object.
  159. *
  160. * \param username Username.
  161. * \param password Password that will be salted and hashed.
  162. * \param name First name.
  163. * \param surname Surname.
  164. * \param age Age.
  165. * \param favFood Favourite food.
  166. */
  167. UserAccount(std::string username = "",
  168. std::string password = "",
  169. std::string name = "",
  170. std::string surname = "",
  171. std::string age = "",
  172. std::string favFood = "")
  173. {
  174. this->username_enc = encrypt(username); // Encrypting the username.
  175. this->salt_enc = randstr(rand() % 50 + 50); // Generating the salt.
  176. this->hashed_pword = hash(this->salt_enc + password); // Salting and hashing the password.
  177. this->name_enc = encrypt(name); // Encrypting the first name.
  178. this->surname_enc = encrypt(surname); // Encrypting the surname.
  179. this->age_enc = encrypt(age); // Encrypting the age.
  180. this->favFood_enc = encrypt(favFood); // Encrypting favourite food.
  181.  
  182. this->salt_enc = encrypt(this->salt_enc); // Encrypting the salt.
  183. }
  184.  
  185. /**
  186. * \brief Represents the account as a string containing the encrypted data. Used to save the
  187. * \brief data into a file.
  188. *
  189. * \returns The string representation of the account.
  190. */
  191. std::string to_string() const
  192. {
  193. return username_enc + "," + salt_enc + "," + std::to_string(hashed_pword) + "," + name_enc
  194. + "," + surname_enc + "," + age_enc + "," + favFood_enc;
  195. }
  196.  
  197. /**
  198. * \brief Parses a string to initialise data of the account.
  199. *
  200. * \param str The string to parse.
  201. */
  202. void parse_string(std::string str)
  203. {
  204. // Splitting the array into legible data.
  205. auto entries = split(str, ',');
  206.  
  207. // There must be exactly 7 entries as there are 7 fields to initialise.
  208. assert(entries.size() == 7);
  209.  
  210. username_enc = entries[0]; // Assigning the encrypted username.
  211. salt_enc = entries[1]; // Assigning the encrypted salt.
  212. hashed_pword = atoll(entries[2].c_str()); // Assigning the hash of salted pasword.
  213. name_enc = entries[3]; // Assigning the encrypted first name.
  214. surname_enc = entries[4]; // Assigning the encrypted surname.
  215. age_enc = entries[5]; // Assigning the age.
  216. favFood_enc = entries[6]; // Assigning the encrypted favourite food.
  217. }
  218.  
  219. private:
  220. /**
  221. * \brief Encrypts a string using keyword encryption. It's hidden for safety reasons.
  222. *
  223. * \return The encrypted string.
  224. */
  225. std::string encrypt(std::string str)
  226. {
  227. // Setting up the available characters.
  228. std::string chars = "0123456789abcdefghijklmnopqrtsuvwxyz";
  229.  
  230. // Setting up the keyword mixed into chars.
  231. std::string subst = "rocksmith2014356789abdefgjlnpquvwxtz";
  232.  
  233. // Encrypting.
  234. return replace(str, chars, subst);
  235. }
  236. };
  237.  
  238. /**
  239. * Database storing data of all users.
  240. */
  241. struct UserDatabase
  242. {
  243. private:
  244. std::unordered_map<std::string, UserAccount> users; ///< Hash map of all users.
  245.  
  246. public:
  247. /**
  248. * \brief Creates a user account and adds it to the database.
  249. *
  250. * \param username Username.
  251. * \param password Password that will be salted and hashed.
  252. * \param name First name.
  253. * \param surname Surname.
  254. * \param age Age.
  255. * \param favFood Favourite food.
  256. */
  257. void add_user(std::string username,
  258. std::string password,
  259. std::string name,
  260. std::string surname,
  261. std::string age,
  262. std::string favFood)
  263. {
  264. users[encrypt(username)] = UserAccount(username, password, name, surname, age, favFood);
  265. }
  266.  
  267. /**
  268. * \brief Logs into a user account with matching username and password.
  269. *
  270. * If the usernam and password are found in the database then it prints the user data to the console.
  271. *
  272. * \param username Username.
  273. * \param password Password.
  274. */
  275. void login(std::string username, std::string password)
  276. {
  277. // Checking if the username is found.
  278. if (users.find(encrypt(username)) == users.end())
  279. {
  280. puts("Invalid login or password."); // Printing an ambiguous message to make it
  281. return; // harder to hack into an account.
  282. }
  283.  
  284. auto user = users[encrypt(username)];
  285. long long hashed_pword = hash(decrypt(user.salt_enc) + password);
  286.  
  287. // Checking if password is correct.
  288. if (hashed_pword != user.hashed_pword)
  289. {
  290. puts("Invalid login or password."); // Printing an ambiguous message to make it
  291. return; // harder to hack into an account.
  292. }
  293.  
  294. // Now print the greeting message.
  295. puts("-----------------------");
  296. puts(("Hello, " + decrypt(user.name_enc) + " " + decrypt(user.surname_enc) + "!").c_str());
  297. puts(("You are " + decrypt(user.age_enc) + " years old.").c_str());
  298. puts(("Your favourite food is " + decrypt(user.favFood_enc) + ".").c_str());
  299. }
  300.  
  301. /**
  302. * \brief Loads the database from a text file.
  303. *
  304. * \param file The input file stream from which the database will be read.
  305. */
  306. void load(std::ifstream& file)
  307. {
  308. std::string line;
  309. while (std::getline(file, line))
  310. {
  311. UserAccount acc = UserAccount();
  312. acc.parse_string(line);
  313. users[acc.username_enc] = acc;
  314. }
  315. }
  316.  
  317. /**
  318. * \brief Saves the database to a text file.
  319. *
  320. * \param file The output file stream to which the database will be saved.
  321. */
  322. void save(std::ofstream& file)
  323. {
  324. for (auto& user : users)
  325. {
  326. file << user.second.to_string() << '\n';
  327. }
  328. }
  329.  
  330. private:
  331. /**
  332. * \brief Encrypts a string using keyword encryption. It's hidden for safety reasons.
  333. *
  334. * \return The encrypted string.
  335. */
  336. std::string encrypt(std::string str)
  337. {
  338. // Setting up the available characters.
  339. std::string chars = "0123456789abcdefghijklmnopqrtsuvwxyz";
  340.  
  341. // Setting up the keyword mixed into chars.
  342. std::string subst = "rocksmith2014356789abdefgjlnpquvwxtz";
  343.  
  344. // Encrypting.
  345. return replace(str, chars, subst);
  346. }
  347.  
  348. /**
  349. * \brief Decrypts a string using keyword decryption. It's hidden for safety reasons.
  350. *
  351. * \return The decrypted string.
  352. */
  353. std::string decrypt(std::string str)
  354. {
  355. // Setting up the keyword mixed into chars.
  356. std::string chars = "rocksmith2014356789abdefgjlnpquvwxtz";
  357.  
  358. // Setting up the available characters.
  359. std::string subst = "0123456789abcdefghijklmnopqrtsuvwxyz";
  360.  
  361. // Decrypting.
  362. return replace(str, chars, subst);
  363. }
  364. };
  365.  
  366.  
  367. // User interface functions.
  368.  
  369. /**
  370. * \brief Displays a list of options a user can choose from.
  371. */
  372. void display_menu();
  373.  
  374. /**
  375. * \brief Asks the user for the option number.
  376. */
  377. unsigned int read_option();
  378.  
  379. /**
  380. * \brief Reads an unsigned integer and handles all input errors.
  381. */
  382. unsigned int safe_read_int();
  383.  
  384. /**
  385. * \brief Performs task assigned to an option chosen by the user.
  386. *
  387. * \param option Number of the option assigned to a task.
  388. * \param database Reference to a database to perform a task on.
  389. *
  390. * \return <code>true</code> if the program has to be running after the task is completed,
  391. * <code>false</code> otherwise.
  392. */
  393. bool perform_task(unsigned int option, UserDatabase& database);
  394.  
  395. /**
  396. * \brief Asks the user for login detais and gives an access to their account.
  397. *
  398. * \param database Reference to a database to perform a task on.
  399. */
  400. void login(UserDatabase& database);
  401.  
  402. /**
  403. * \brief Asks the user for login details and account data to create them an account.
  404. *
  405. * \param database Reference to a database to perform a task on.
  406. */
  407. void add_user(UserDatabase& database);
  408.  
  409.  
  410. /**
  411. * \brief The program's main function.
  412. *
  413. * Initialises the user database and runs the program's main loop.
  414. */
  415. int main()
  416. {
  417. // Setting the random number generator's seed to current time.
  418. srand(time(0));
  419.  
  420. // Creating the database.
  421. auto ub = UserDatabase();
  422.  
  423. // Loading data.
  424. std::ifstream file("data.txt");
  425. if (file.good()) ub.load(file);
  426.  
  427. // Main loop.
  428. while (true)
  429. {
  430. // Displaying menu.
  431. display_menu();
  432.  
  433. // Reading option number.
  434. unsigned int opt = read_option();
  435.  
  436. // Performing a task and exiting if told to do so.
  437. if (!perform_task(opt, ub)) break;
  438. }
  439.  
  440. return 0;
  441. }
  442.  
  443. void display_menu()
  444. {
  445. puts("-----------------------");
  446. puts("What do you want to do?");
  447. puts("0. Exit.");
  448. puts("1. Login.");
  449. puts("2. Register.");
  450. }
  451.  
  452. unsigned int read_option()
  453. {
  454. return safe_read_int();
  455. }
  456.  
  457. unsigned int safe_read_int()
  458. {
  459. // Setting a negative number to keep the loop going until correct data is entered.
  460. int num = -1;
  461. while (num < 0)
  462. {
  463. // Printing prompt.
  464. printf("~> ");
  465.  
  466. // Reading the value.
  467. scanf(" %d", &num);
  468.  
  469. // If the input is incorrect, the buffer needs to be cleared.
  470. char c;
  471. while ((c = getchar()) != '\n' && c != EOF) {}
  472. }
  473.  
  474. return (unsigned int) num;
  475. }
  476.  
  477. bool perform_task(unsigned int option, UserDatabase& database)
  478. {
  479. switch (option)
  480. {
  481. case 0: // Saving the database to a text file and exiting.
  482. {
  483. std::ofstream file("data.txt");
  484. file.clear();
  485. database.save(file);
  486. file.close();
  487. return false;
  488. }
  489.  
  490. case 1: // Logging in.
  491. login(database);
  492. break;
  493.  
  494. case 2: // Registering a new user.
  495. add_user(database);
  496. break;
  497.  
  498. default:
  499. puts("Unknown option, please enter it again.");
  500. break;
  501. }
  502.  
  503. return true;
  504. }
  505.  
  506. void login(UserDatabase& database)
  507. {
  508. // Setting up username and password buffers.
  509. char username[51];
  510. char password[51];
  511.  
  512. // Reading username.
  513. puts("Please enter your username.");
  514. printf("~> ");
  515. scanf(" %s", username);
  516.  
  517. // Reading password.
  518. puts("Please enter your password.");
  519. printf("~> ");
  520. scanf(" %s", password);
  521.  
  522. // Logging in.
  523. database.login(username, password);
  524. }
  525.  
  526. void add_user(UserDatabase& database)
  527. {
  528. // Setting up buffers for login and account data.
  529. char username[51];
  530. char password[51];
  531. char name[51];
  532. char surname[51];
  533. std::string age;
  534. char favFood[51];
  535.  
  536. // Reading username.
  537. puts("Please enter your username.");
  538. printf("~> ");
  539. scanf(" %s", username);
  540.  
  541. // Reading password.
  542. puts("Please enter your password.");
  543. printf("~> ");
  544. scanf(" %s", password);
  545.  
  546. // Reading first name.
  547. puts("Please enter your first name.");
  548. printf("~> ");
  549. scanf(" %s", name);
  550.  
  551. // Reading last name.
  552. puts("Please enter your last name.");
  553. printf("~> ");
  554. scanf(" %s", surname);
  555.  
  556. // Reading age.
  557. puts("Please enter your age.");
  558. age = std::to_string(safe_read_int());
  559.  
  560. // Reading favourite food.
  561. puts("Please enter your favourite food.");
  562. printf("~> ");
  563. scanf(" %s", favFood);
  564.  
  565. // Registering a new user.
  566. database.add_user(username, password, name, surname, age, favFood);
  567. }
Add Comment
Please, Sign In to add comment