#include #include #include #include "Config.h" #define CONFIG_SECTION_NAME "obs-websocket" #define CONFIG_PARAM_CHALLENGE "auth_hash" #define CONFIG_PARAM_SALT "auth_salt" #define CONFIG_PARAM_AUTHREQUIRED "auth_required" Config *Config::_instance = new Config(); Config::Config() { // Default settings AuthRequired = false; Challenge = ""; Salt = ""; SettingsLoaded = false; mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&rng); mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0); //mbedtls_ctr_drbg_set_prediction_resistance(&rng, MBEDTLS_CTR_DRBG_PR_ON); } Config::~Config() { mbedtls_ctr_drbg_free(&rng); mbedtls_entropy_free(&entropy); } const char* Config::GenerateSalt(mbedtls_ctr_drbg_context *rng) { // Generate 32 random chars unsigned char *random_chars = (unsigned char *)bzalloc(32); mbedtls_ctr_drbg_random(rng, random_chars, 32); // Convert the 32 random chars to a base64 string unsigned char *salt = (unsigned char*)bzalloc(64); size_t salt_bytes; mbedtls_base64_encode(salt, 64, &salt_bytes, random_chars, 32); salt[salt_bytes] = 0; // Null-terminate the string bfree(random_chars); return (char *)salt; } const char* Config::GenerateChallenge(const char *password, const char *salt) { size_t passwordLength = strlen(password); size_t saltLength = strlen(salt); // Concatenate the password and the salt unsigned char *passAndSalt = (unsigned char*)bzalloc(passwordLength + saltLength); memcpy(passAndSalt, password, passwordLength); memcpy(passAndSalt + passwordLength, salt, saltLength); passAndSalt[passwordLength + saltLength] = 0; // Null-terminate the string // Generate a SHA256 hash of the password unsigned char *challengeHash = (unsigned char *)bzalloc(32); mbedtls_sha256(passAndSalt, passwordLength + saltLength, challengeHash, 0); // Encode SHA256 hash to Base64 unsigned char *challenge = (unsigned char*)bzalloc(64); size_t challenge_bytes = 0; mbedtls_base64_encode(challenge, 64, &challenge_bytes, challengeHash, 32); challenge[64] = 0; // Null-terminate the string bfree(passAndSalt); bfree(challengeHash); return (char*)challenge; } void Config::SetPassword(const char *password) { const char *new_salt = GenerateSalt(&rng); const char *new_challenge = GenerateChallenge(password, new_salt); this->Salt = new_salt; this->Challenge = new_challenge; } bool Config::CheckAuth(const char *response) { size_t challengeLength = strlen(this->Challenge); size_t responseLength = strlen(response); // Concatenate challenge and auth response char *challengeAndResponse = (char*)bzalloc(challengeLength + responseLength); memcpy(challengeAndResponse, this->Challenge, challengeLength); memcpy(challengeAndResponse + challengeLength, response, responseLength); challengeAndResponse[challengeLength + responseLength] = 0; // Null-terminate the string // Generate a SHA256 hash of challengeAndResponse unsigned char *hash = (unsigned char*)bzalloc(32); mbedtls_sha256((unsigned char*)challengeAndResponse, challengeLength + responseLength, hash, 0); // Encode the SHA256 hash to Base64 unsigned char *expected_response = (unsigned char*)bzalloc(64); size_t base64_size = 0; mbedtls_base64_encode(expected_response, 64, &base64_size, hash, 32); expected_response[64] = 0; // Null-terminate the string return (strcmp((char*)expected_response, response) == 0); } void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) { Config *conf = static_cast(private_data); if (saving) { obs_data_t *settings = obs_data_create(); obs_data_set_bool(settings, CONFIG_PARAM_AUTHREQUIRED, conf->AuthRequired); obs_data_set_string(settings, CONFIG_PARAM_CHALLENGE, conf->Challenge); obs_data_set_string(settings, CONFIG_PARAM_SALT, conf->Salt); obs_data_set_obj(save_data, CONFIG_SECTION_NAME, settings); obs_data_release(settings); } else { obs_data_t *settings = obs_data_get_obj(save_data, CONFIG_SECTION_NAME); if (settings) { conf->AuthRequired = obs_data_get_bool(settings, CONFIG_PARAM_AUTHREQUIRED); conf->Challenge = obs_data_get_string(settings, CONFIG_PARAM_CHALLENGE); conf->Salt = obs_data_get_string(settings, CONFIG_PARAM_SALT); conf->SettingsLoaded = true; } obs_data_release(settings); } } Config* Config::Current() { return _instance; }