23 #include <boost/algorithm/string.hpp>
29 #include <openssl/evp.h>
30 #include <openssl/err.h>
32 #include <CommonCrypto/CommonCryptor.h>
36 #include <boost/range/iterator_range.hpp>
41 #define DBG_CFG LOG_STREAM(debug , log_config)
42 #define ERR_CFG LOG_STREAM(err , log_config)
48 : vector<unsigned char>(std::forward<T>(args)...)
89 if(GetUserNameW(buffer, &
size)) {
92 res = unicode_cast<std::string>(boost::iterator_range<wchar_t*>(buffer, buffer +
size - 1));
95 if(
char*
const login = getenv(
"USER")) {
107 std::fill(cred.username.begin(), cred.username.end(),
'\0');
108 std::fill(cred.server.begin(), cred.server.end(),
'\0');
122 }
else if(name.size() > 2 && name.front() ==
'@' && name.back() ==
'@') {
123 name = name.substr(1, name.size() - 2);
125 ERR_CFG <<
"malformed user credentials (did you manually edit the preferences file?)";
135 auto login_clean =
login;
159 DBG_CFG <<
"Retrieving password for server: '" << server <<
"', login: '" <<
login <<
"'";
160 auto login_clean =
login;
166 return std::string(temp.begin(), temp.end());
172 return cred.server == server && cred.username == login_clean;
178 return std::string(temp.begin(), temp.end());
183 DBG_CFG <<
"Setting password for server: '" << server <<
"', login: '" <<
login <<
"'";
184 auto login_clean =
login;
194 return cred.server == server && cred.username == login_clean;
215 secure_buffer data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
218 ERR_CFG <<
"Invalid data in credentials file";
222 std::size_t
at = elem.find_last_of(
'@');
223 std::size_t eq = elem.find_first_of(
'=',
at + 1);
224 if(
at != std::string::npos && eq != std::string::npos) {
240 credentials_data.insert(credentials_data.end(), cred.username.begin(), cred.username.end());
241 credentials_data.push_back(
'@');
242 credentials_data.insert(credentials_data.end(), cred.server.begin(), cred.server.end());
243 credentials_data.push_back(
'=');
245 credentials_data.insert(credentials_data.end(), key_escaped.begin(), key_escaped.end());
250 credentials_file->write(
reinterpret_cast<const char*
>(encrypted.data()), encrypted.size());
268 secure_buffer result(std::max<std::size_t>(server.size() +
login.size() + sysname.size(), 32));
270 std::generate(result.begin(), result.end(), [&
i]() {return
'x' ^ i++;});
271 std::copy(
login.begin(),
login.end(), result.begin());
272 std::copy(sysname.begin(), sysname.end(), result.begin() +
login.size());
273 std::copy(server.begin(), server.end(), result.begin() +
login.size() + sysname.size());
295 const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
296 unsigned char encrypted_buffer[1024];
298 if(plaintext.size() > 1008)
300 ERR_CFG <<
"Cannot encrypt data larger than 1008 bytes.";
303 DBG_CFG <<
"Encrypting data with length: " << plaintext.size();
305 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
308 ERR_CFG <<
"AES EVP_CIPHER_CTX_new failed with error:";
309 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
314 if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv) != 1)
316 ERR_CFG <<
"AES EVP_EncryptInit_ex failed with error:";
317 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
318 EVP_CIPHER_CTX_free(ctx);
322 if(EVP_EncryptUpdate(ctx, encrypted_buffer, &update_length, plaintext.data(), plaintext.size()) != 1)
324 ERR_CFG <<
"AES EVP_EncryptUpdate failed with error:";
325 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
326 EVP_CIPHER_CTX_free(ctx);
329 DBG_CFG <<
"Update length: " << update_length;
331 if(EVP_EncryptFinal_ex(ctx, encrypted_buffer + update_length, &extra_length) != 1)
333 ERR_CFG <<
"AES EVP_EncryptFinal failed with error:";
334 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
335 EVP_CIPHER_CTX_free(ctx);
338 DBG_CFG <<
"Extra length: " << extra_length;
340 EVP_CIPHER_CTX_free(ctx);
342 total_length = update_length+extra_length;
344 for(
int i = 0;
i < total_length;
i++)
346 result.push_back(encrypted_buffer[
i]);
349 DBG_CFG <<
"Successfully encrypted plaintext value of '" <<
utils::join(plaintext,
"") <<
"' having length " << plaintext.size();
350 DBG_CFG <<
"For a total encrypted length of: " << total_length;
354 size_t outWritten = 0;
357 CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
359 kCCOptionPKCS7Padding,
369 assert(ccStatus == kCCSuccess);
370 assert(outWritten == plaintext.size());
386 const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
387 unsigned char plaintext_buffer[1024];
389 if(encrypted.size() > 1024)
391 ERR_CFG <<
"Cannot decrypt data larger than 1024 bytes.";
394 DBG_CFG <<
"Decrypting data with length: " << encrypted.size();
396 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
399 ERR_CFG <<
"AES EVP_CIPHER_CTX_new failed with error:";
400 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
405 if(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv) != 1)
407 ERR_CFG <<
"AES EVP_DecryptInit_ex failed with error:";
408 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
409 EVP_CIPHER_CTX_free(ctx);
413 if(EVP_DecryptUpdate(ctx, plaintext_buffer, &update_length, encrypted.data(), encrypted.size()) != 1)
415 ERR_CFG <<
"AES EVP_DecryptUpdate failed with error:";
416 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
417 EVP_CIPHER_CTX_free(ctx);
420 DBG_CFG <<
"Update length: " << update_length;
422 if(EVP_DecryptFinal_ex(ctx, plaintext_buffer + update_length, &extra_length) != 1)
424 ERR_CFG <<
"AES EVP_DecryptFinal failed with error:";
425 ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
426 EVP_CIPHER_CTX_free(ctx);
429 DBG_CFG <<
"Extra length: " << extra_length;
431 EVP_CIPHER_CTX_free(ctx);
433 total_length = update_length+extra_length;
435 for(
int i = 0;
i < total_length;
i++)
437 result.push_back(plaintext_buffer[
i]);
441 DBG_CFG <<
"For a total decrypted length of: " << total_length;
445 size_t outWritten = 0;
448 CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
450 kCCOptionPKCS7Padding,
460 assert(ccStatus == kCCSuccess);
461 assert(outWritten == encrypted.size());
464 while(!result.empty() && result.back() == 0) {
475 unescaped.reserve(text.size());
476 bool escaping =
false;
480 unescaped.push_back(
'\xc');
481 }
else if(
c ==
'.') {
482 unescaped.push_back(
'@');
484 unescaped.push_back(
c);
487 }
else if(
c ==
'\x1') {
490 unescaped.push_back(
c);
500 escaped.reserve(text.size());
503 escaped.push_back(
'\x1');
504 escaped.push_back(
'\x1');
505 }
else if(
c ==
'\xc') {
506 escaped.push_back(
'\x1');
507 escaped.push_back(
'\xa');
508 }
else if(
c ==
'@') {
509 escaped.push_back(
'\x1');
510 escaped.push_back(
'.');
512 escaped.push_back(
c);
secure_buffer(T &&... args)
static const unsigned char CREDENTIAL_SEPARATOR
static std::string get_system_username()
static const std::string EMPTY_LOGIN
static std::vector< login_info > credentials
static secure_buffer unescape(const secure_buffer &text)
static secure_buffer escape(const secure_buffer &text)
static secure_buffer aes_encrypt(const secure_buffer &text, const secure_buffer &key)
Encrypts the value of plaintext using key and a hard coded IV using AES.
static secure_buffer build_key(const std::string &server, const std::string &login)
Fills a secure_buffer with 32 bytes of deterministically generated bytes, then overwrites it with the...
static secure_buffer aes_decrypt(const secure_buffer &text, const secure_buffer &key)
Same as aes_encrypt(), except of course it takes encrypted data as an argument and returns decrypted ...
static lg::log_domain log_config("config")
static void clear_credentials()
Declarations for File-IO.
Standard logging facilities (interface).
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
bool delete_file(const std::string &filename)
static bool file_exists(const bfs::path &fpath)
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
std::string get_credentials_file()
std::unique_ptr< std::ostream > scoped_ostream
Modify, read and display user preferences.
void set_remember_password(bool remember)
std::string password(const std::string &server, const std::string &login)
void set(const std::string &key, bool value)
std::string get(const std::string &key)
void set_login(const std::string &login)
void set_password(const std::string &server, const std::string &login, const std::string &key)
static std::string at(const std::string &file, int line)
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
void trim(std::string_view &s)
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
An exception object used when an IO error occurs.
login_info(const std::string &username, const std::string &server)
login_info(const std::string &username, const std::string &server, const secure_buffer &key)