#include "Cryptor.h" bool Cryptor::haveKey = false; unsigned char Cryptor::key[AES_BLOCK_LENGTH]; static void toHex(char *output, const void *input, size_t size) { const unsigned char *_input = (const unsigned char *)input; for (size_t i = 0; i < size; i++) { sprintf(output+(i*2), "%02hhX", _input[i]); } } static void fromHex(void *output, const char *input) { unsigned char *_output = (unsigned char *)output; size_t size = strlen(input); if (size & 1) { throw 1; } else { size /= 2; } for (size_t i = 0; i < size; i++) { if (sscanf(input+(i*2), "%02hhx", &_output[i]) != 1) { throw 1; } } } void Cryptor::encryptAndSave(std::string directory, std::string payload) { // Key if (!haveKey) { assembleKey(true); } // IV unsigned char iv[AES_BLOCK_LENGTH]; generateRandom(iv, sizeof(iv)); // Encrypt size_t ciphertextLen = 0; unsigned char *ciphertext = new unsigned char[payload.size() + 64]; const mbedtls_cipher_info_t *cipherInfo = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CBC); mbedtls_cipher_context_t cipher; mbedtls_cipher_init(&cipher); mbedtls_cipher_setup(&cipher, cipherInfo); mbedtls_cipher_setkey(&cipher, key, sizeof(key)*8, MBEDTLS_ENCRYPT); mbedtls_cipher_set_padding_mode(&cipher, MBEDTLS_PADDING_PKCS7); mbedtls_cipher_crypt(&cipher, iv, sizeof(iv), (const unsigned char *)payload.c_str(), payload.size(), ciphertext, &ciphertextLen); mbedtls_cipher_free(&cipher); // Save Data char encIV[(AES_BLOCK_LENGTH * 2) + 1]; char *encCipher = new char[(ciphertextLen * 2) + 1]; toHex(encIV, iv, sizeof(iv)); toHex(encCipher, ciphertext, ciphertextLen); std::string _encIV(encIV); std::string _encCipher(encCipher); delete[] encCipher; delete[] ciphertext; if (directory[directory.size() - 1] != '/') { directory += "/"; } directory += KEYCHAIN_FILE; std::ofstream f(directory.c_str()); f << _encIV << std::endl; f << _encCipher << std::endl; f.close(); } std::string Cryptor::loadAndDecrypt(std::string directory) { // Load Data std::string encIV, encCipher; if (directory[directory.size() - 1] != '/') { directory += "/"; } directory += KEYCHAIN_FILE; std::ifstream f(directory.c_str()); if (!f.good()) { f.close(); throw 1; } f >> encIV; f >> encCipher; f.close(); // Decode data unsigned char *ciphertext = new unsigned char[encCipher.size() / 2]; unsigned char iv[AES_BLOCK_LENGTH]; fromHex(ciphertext, encCipher.c_str()); fromHex(iv, encIV.c_str()); // Key if (!haveKey) { assembleKey(); } // Decrypt size_t plaintextLen = 0; char *plaintext = new char[(encCipher.size() / 2) + 64]; const mbedtls_cipher_info_t *cipherInfo = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CBC); mbedtls_cipher_context_t cipher; mbedtls_cipher_init(&cipher); mbedtls_cipher_setup(&cipher, cipherInfo); mbedtls_cipher_setkey(&cipher, key, sizeof(key)*8, MBEDTLS_DECRYPT); mbedtls_cipher_set_padding_mode(&cipher, MBEDTLS_PADDING_PKCS7); mbedtls_cipher_crypt(&cipher, iv, sizeof(iv), ciphertext, (encCipher.size() / 2), (unsigned char *)plaintext, &plaintextLen); mbedtls_cipher_free(&cipher); std::string payload(plaintext, plaintextLen); delete[] plaintext; delete[] ciphertext; return payload; } std::string Cryptor::createRandomPassword(PasswordSpec spec) { std::string password; std::vector validChars; // Always allow lower-case alphabetic characters for (int i = 0; i < 26; i++) validChars.push_back('a' + i); // Allow special chars? if (!spec.ns) { validChars.push_back('`'); validChars.push_back('~'); validChars.push_back('!'); validChars.push_back('@'); validChars.push_back('#'); validChars.push_back('$'); validChars.push_back('%'); validChars.push_back('^'); validChars.push_back('&'); validChars.push_back('*'); validChars.push_back('('); validChars.push_back(')'); validChars.push_back('-'); validChars.push_back('_'); validChars.push_back('='); validChars.push_back('+'); validChars.push_back('['); validChars.push_back('{'); validChars.push_back(']'); validChars.push_back('}'); validChars.push_back('\\'); validChars.push_back('|'); validChars.push_back(';'); validChars.push_back(':'); validChars.push_back(','); validChars.push_back('<'); validChars.push_back('.'); validChars.push_back('>'); validChars.push_back('/'); validChars.push_back('?'); } // Allow capital letters? if (!spec.nc) { for (int i = 0; i < 26; i++) validChars.push_back('A' + i); } // Allow numbers? if (!spec.nn) { for (int i = 0; i < 10; i++) validChars.push_back('0' + i); } // Build string for (int i = 0; i < spec.ml; i++) { unsigned char c; generateRandom(&c, sizeof(c)); password += validChars[c % validChars.size()]; } return password; } void Cryptor::rekey() { haveKey = false; } void Cryptor::sha256(std::string str) { unsigned char hashbuf[32]; mbedtls_sha256((const unsigned char *)str.c_str(), str.size(), hashbuf, 0); memcpy(key, hashbuf, sizeof(key)); haveKey = true; } void Cryptor::generateRandom(void *output, size_t size) { mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context random; mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&random); mbedtls_ctr_drbg_seed(&random, mbedtls_entropy_func, &entropy, NULL, 0); mbedtls_ctr_drbg_random(&random, (unsigned char *)output, size); mbedtls_ctr_drbg_free(&random); mbedtls_entropy_free(&entropy); } std::string Cryptor::readPassword(bool confirm) { std::string password; #ifdef WIN32 HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; GetConsoleMode(hstdin, &mode); SetConsoleMode(hstdin, mode & ~ENABLE_ECHO_INPUT); password = promptPassword(confirm); SetConsoleMode(hstdin, mode); #else termios oldt; tcgetattr(STDIN_FILENO, &oldt); termios newt = oldt; newt.c_lflag &= ~ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &newt); password = promptPassword(confirm); tcsetattr(STDIN_FILENO, TCSANOW, &oldt); #endif // WIN32 return password; } std::string Cryptor::readPassword() { return readPassword(false); } void Cryptor::assembleKey(bool confirm) { sha256(readPassword(confirm)); } void Cryptor::assembleKey() { assembleKey(false); } std::string Cryptor::promptPassword(bool confirm) { std::string password; while (true) { std::cout << PASSWORD_PROMPT; std::cin >> password; std::cout << std::endl; if (confirm) { std::string conf; std::cout << PASSWORD_CONF; std::cin >> conf; std::cout << std::endl; if (password == conf) break; else std::cout << PASSWORD_ERROR << std::endl; } else break; } return password; }