C3/Src/Core/BackendCommons.cpp

138 lines
7.7 KiB
C++

#include "Stdafx.h"
#include "Common/FSecure/C3/Internals/BackendCommons.h"
#include "Common/FSecure/Crypto/Base64.h"
#include "GateRelay.h"
#include "NodeRelay.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Anonymous namespace with helpers.
namespace
{
/// Obtains Gateway's encryption keys. If the key file exists, it's contents will be Base64 decoded and validated. Otherwise they will be generated.
/// @param keysFilePath path to the keys file.
/// @return std::tuple with a flag (true if keys were read, false if generated) and all the keys.
std::tuple<bool, FSecure::Crypto::SignatureKeys, FSecure::Crypto::SymmetricKey> ReadFromFileOrGenerateGatewayKeys(std::filesystem::path const& keysFilePath)
{
// Check if file exists.
if (std::filesystem::exists(keysFilePath))
try
{
// Parse keys.
auto keys = json::parse(std::ifstream{ keysFilePath, std::ios::in });
// Helper lambda to read patch entries. TODO: rewrite as a template lambda when C++20 is out.
auto ParseEntry = [&keys](auto* typePtr, auto const& entryName) -> std::remove_pointer_t<decltype(typePtr)>
{
try
{
return cppcodec::base64_rfc4648::decode<FSecure::ByteVector>(keys[entryName].template get<std::string>());
}
catch (std::exception& exception)
{
throw std::runtime_error{ OBF("Couldn't parse ") + std::string(entryName) + OBF(" element from patch. ") + exception.what() };
}
catch (...)
{
throw std::runtime_error{ OBF("Caught an unknown exception while parsing ") + std::string(entryName) + OBF(" element from patch.") };
}
};
// Parse and return keys.
return std::make_tuple(true, FSecure::Crypto::SignatureKeys{ ParseEntry((FSecure::Crypto::PrivateSignature*)nullptr, OBF("Private signature")),
ParseEntry((FSecure::Crypto::PublicSignature*)nullptr, OBF("Public signature")) }, ParseEntry((FSecure::Crypto::SymmetricKey*)nullptr, OBF("Broadcast key")));
}
catch (const std::exception & exception)
{
throw std::runtime_error{ OBF_STR("Incorrect key file. ") + exception.what() };
}
// File doesn't exist. Generate all the keys.
auto singatures = FSecure::Crypto::GenerateSignatureKeys();
auto broadcastKey = FSecure::Crypto::GenerateSymmetricKey();
// Create json tree.
auto keys = json{};
keys[OBF("Private signature")] = cppcodec::base64_rfc4648::encode(singatures.first.ToByteVector().data(), FSecure::Crypto::PrivateSignature::Size);
keys[OBF("Public signature")] = cppcodec::base64_rfc4648::encode(singatures.second.ToByteVector().data(), FSecure::Crypto::PublicSignature::Size);
keys[OBF("Broadcast key")] = cppcodec::base64_rfc4648::encode(broadcastKey.ToByteVector().data(), FSecure::Crypto::SymmetricKey::Size);
// Store them in provided file and return.
std::ofstream{ keysFilePath, std::ios::out | std::ios::trunc } << keys.dump(4); // dump(4) adds 4 space indentation to file.
return std::make_tuple(false, singatures, broadcastKey);
}
/// Obtains Gateway's configuration.
/// @param configurationFilePath path to the configuration file.
/// @return std::tuple with read configuration.
auto ReadGatewayConfigurationFile(std::filesystem::path const& configurationFilePath)
{
// Parse json configuration file.
if (!std::filesystem::exists(configurationFilePath))
throw std::invalid_argument{ OBF("Gateway configuration file not found.") };
auto configuration = json::parse(std::ifstream{ configurationFilePath, std::ios::in });
// Unfortunetly json::value throws for most value_types. This helper will return default in such case.
auto jsonValueClosure = [&configuration](auto const& key, auto const& defaultValue)
{
return configuration.is_object() ? configuration.value(key, defaultValue) : defaultValue; };
return std::make_tuple
(
jsonValueClosure(OBF("API Bridge IP"), OBF("127.0.0.1")),
jsonValueClosure(OBF("API Bridge port"), std::uint16_t{ 2323 }),
FSecure::C3::BuildId{ jsonValueClosure(OBF("BuildId"), FSecure::C3::BuildId::GenerateRandom().ToString()) },
FSecure::C3::AgentId{ jsonValueClosure(OBF("AgentId"), FSecure::C3::AgentId::GenerateRandom().ToString()) },
jsonValueClosure("Name", "")
);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<FSecure::C3::Relay> FSecure::C3::Utils::CreateGatewayFromConfigurationFiles(LoggerCallback callbackOnLog, InterfaceFactory& interfaceFactory, std::filesystem::path const& keysFileName, std::filesystem::path const& configurationFileName)
{
// Identify and store the directory that the executable is running in (-not- the current directory) using WinAPI.
wchar_t executableFilePath[MAX_PATH];
if (!GetModuleFileNameW(nullptr, executableFilePath, MAX_PATH))
throw std::runtime_error{ OBF("Could not find process image path.") };
// Read both input files.
callbackOnLog({ OBF("Reading input files..."), LogMessage::Severity::Information }, "");
auto [apiBridgeIp, apiBrigdePort, buildId, agentId, name] = ReadGatewayConfigurationFile(std::filesystem::path{ executableFilePath }.remove_filename() / configurationFileName);
auto [wereKeysReadOrGenerated, signatures, broadcastKey] = ReadFromFileOrGenerateGatewayKeys(std::filesystem::path{ executableFilePath }.remove_filename() / keysFileName);
auto snapshotPath = std::filesystem::path{ executableFilePath }.remove_filename() / OBF("GatewaySnapshot.json");
// Create and run the Gateway.
if (!wereKeysReadOrGenerated)
callbackOnLog({ OBF("Generated new keys/signatures and stored them on disk."), LogMessage::Severity::Information }, "");
callbackOnLog({ OBF("Starting Gateway..."), LogMessage::Severity::Information }, "");
return FSecure::C3::Core::GateRelay::CreateAndRun(callbackOnLog, interfaceFactory, apiBridgeIp, apiBrigdePort, signatures, broadcastKey, buildId, snapshotPath, agentId, name);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<FSecure::C3::Relay> FSecure::C3::Utils::CreateNodeRelayFromImagePatch(LoggerCallback callbackOnLog, InterfaceFactory& interfaceFactory, ByteView buildId, ByteView gatewaySignature, ByteView broadcastKey, std::vector<ByteVector> const& gatewayInitialPackets)
{
return Core::NodeRelay::CreateAndRun(callbackOnLog, interfaceFactory, gatewaySignature, broadcastKey, gatewayInitialPackets, buildId.Read<BuildId>());
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string FSecure::C3::Utils::ConvertLogMessageToConsoleText(std::string_view relayName, LogMessage const& message, std::string_view sender)
{
// Format message as: "[relay]|@> [InterfaceID] message", where @ is different for each severity.
std::string retVal = sender.empty() ? (std::string(relayName) + '|') : ""s;
switch (message.m_Severity)
{
case FSecure::C3::LogMessage::Severity::Error: retVal += OBF("Error> "); break;
case FSecure::C3::LogMessage::Severity::Information: retVal += OBF("Info> "); break;
case FSecure::C3::LogMessage::Severity::Warning: retVal += OBF("Warning> "); break;
case FSecure::C3::LogMessage::Severity::DebugInformation: retVal += OBF("Debug> "); break;
default: retVal += OBF("???> ");
}
retVal += !sender.empty() ? '[' + std::string(sender) + "] " : "";
return retVal += message.m_Body;
}