Implementation of the AuthService entity in the Secure Login example

main.cpp

#include "general.h"

#include "server.h"

#include <iostream>

#include <sys/mount.h>

#include <sys/stat.h>

#include <sys/types.h>

int main(int argc, char *argv[])

{

std::cout << app::AppTag << "Service started" << std::endl;

Server server;

auto retCode = server.Run(std::make_shared<DiffieHellman>());

std::cout << app::AppTag << "Service stoped. Exit code = " << retCode

<< std::endl;

return retCode;

}

server.cpp

#include "server.h"

#include "general.h"

#include "login_form_handler.h"

#include "login_result_handler.h"

#include "auth_service/AuthService.edl.h"

#include <connections.h>

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

#include <fcntl.h>

#include <iostream>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <unistd.h>

Server::Server()

: m_reqArena(

NK_ARENA_INITIALIZER(m_reqBuffer, m_reqBuffer + sizeof(m_reqBuffer)))

, m_resArena(

NK_ARENA_INITIALIZER(m_resBuffer, m_resBuffer + sizeof(m_resBuffer)))

{}

int Server::Run(ISecretDataPtr secret)

{

ServiceId iid;

NkKosTransport transport;

// Gets the server IPC handle for the connection

auto handleClients =

ServiceLocatorRegister(connections::ConnectionName, NULL, 0, &iid);

if (handleClients == INVALID_HANDLE)

{

std::cerr

<< app::AppTag

<< "Error: can`t establish static IPC connection! Connection name: "

<< connections::ConnectionName << std::endl;

return EXIT_FAILURE;

}

// Initializes transport to clients

NkKosTransport_Init(&transport, handleClients, NK_NULL, 0);

// Initializes the Logger entity dispatcher

auth_service_AuthService_entity entity;

auth_service_AuthService_entity_init(

&entity,

LoginFormHandler::CreateImpl(secret),

LoginResultHandler::CreateImpl(secret));

// Main cycle: requests execution.

while (true)

{

nk_req_reset(&m_req);

nk_arena_reset(&m_reqArena);

nk_req_reset(&m_res);

nk_arena_reset(&m_resArena);

if (auto resCode =

nk_transport_recv(&transport.base, &m_req.base_, &m_reqArena);

resCode == NK_EOK)

auth_service_AuthService_entity_dispatch(

&entity, &m_req.base_, &m_reqArena, &m_res.base_, &m_resArena);

else

std::cerr << app::AppTag

<< "Error: nk_transport_recv is not OK. Error code = "

<< resCode << std::endl;

if (auto resCode =

nk_transport_reply(&transport.base, &m_res.base_, &m_resArena);

resCode != NK_EOK)

std::cerr << app::AppTag

<< "Error: nk_transport_reply is not OK. Error code = "

<< resCode << std::endl;

}

return EXIT_SUCCESS;

}

authservice.cpp

#include "authservice.h"

#include <map>

bool AuthService::Authentication(

const std::string &userName, const std::string &password) const

{

const std::map<std::string, std::string> Users = {

{"user1", "password1"}, {"User2", "security"}, {"user3", "R0g9in65"}};

auto it = Users.find(userName);

if (it == Users.end())

{

return false;

}

return it->second == password;

}

diffiehellman.cpp

#include "diffiehellman.h"

#include "formatter.h"

#include <openssl/bn.h>

#include <openssl/rand.h>

#include <chrono>

#include <exception>

#include <iomanip>

#include <iostream>

#include <math.h>

#include <memory.h>

#include <time.h>

#include <vector>

namespace consts {

constexpr uint32_t BnBits = sizeof(uint64_t) * 8;

}

using time_point = std::chrono::system_clock::time_point;

std::string

serializeTimePoint(const time_point &tmNow, const std::string &format)

{

std::time_t tt = std::chrono::system_clock::to_time_t(tmNow);

auto tm = *std::gmtime(&tt);

std::stringstream ss;

ss << std::put_time(&tm, format.c_str());

return ss.str();

}

void InitRandSeed()

{

if (RAND_status())

return;

auto input = std::chrono::system_clock::now();

auto timeStr = serializeTimePoint(input, "UTC: %Y-%m-%d %H:%M:%S");

auto seedStr = timeStr.c_str();

char rndSeed[] =

"string to make the random number generator think it has entropy";

strcpy(rndSeed, seedStr);

RAND_seed(&rndSeed, sizeof(rndSeed));

}

uint64_t Bn2Uint64t(const BIGNUM *bnValue)

{

uint64_t value;

std::istringstream iss(BN_bn2dec(bnValue));

iss >> value;

return value;

}

DiffieHellman::~DiffieHellman()

{

Dispose();

}

void DiffieHellman::Init()

{

std::vector<std::tuple<std::string, std::string>> dhPair = {

{"23", "5"}, {"13", "6"}, {"6734586", "2"}};

srand(static_cast<unsigned int>(time(nullptr)));

uint64_t idx = rand() % 3;

InitRandSeed();

m_bignumDh = std::make_unique<BignumDhData>();

if (!BN_generate_prime_ex(

m_bignumDh->secretKey, consts::BnBits, 0, NULL, NULL, NULL))

{

throw std::runtime_error(

Formatter() << "Unable to get random value for " << consts::BnBits

<< " bits");

}

BN_dec2bn(&m_bignumDh->p, std::get<0>(dhPair[idx]).c_str());

BN_dec2bn(&m_bignumDh->g, std::get<1>(dhPair[idx]).c_str());

if (BN_mod_exp(

m_bignumDh->openKey,

m_bignumDh->g,

m_bignumDh->secretKey,

m_bignumDh->p,

m_bignumDh->ctx) <= 0)

{

throw std::runtime_error("Error of BN_mod_exp.");

}

m_dhData.openKey = Bn2Uint64t(m_bignumDh->openKey);

m_dhData.secretKey = Bn2Uint64t(m_bignumDh->secretKey);

m_dhData.p = Bn2Uint64t(m_bignumDh->p);

m_dhData.g = Bn2Uint64t(m_bignumDh->g);

}

void DiffieHellman::Dispose()

{

m_dhData.g = 0;

m_dhData.p = 0;

m_dhData.secretKey = 0;

m_dhData.openKey = 0;

m_dhData.secret = 0;

m_bignumDh.reset();

}

bool DiffieHellman::IsInit()

{

return m_dhData.p != 0 && m_dhData.g != 0;

}

uint64_t DiffieHellman::GetSecret(uint64_t oppositeOpenKey) const

{

std::stringstream ss;

ss << oppositeOpenKey;

BN_dec2bn(&m_bignumDh->oppositeKey, ss.str().c_str());

if (BN_mod_exp(

m_bignumDh->secret,

m_bignumDh->oppositeKey,

m_bignumDh->secretKey,

m_bignumDh->p,

m_bignumDh->ctx) <= 0)

{

throw std::runtime_error("Error of BN_mod_exp.");

}

return Bn2Uint64t(m_bignumDh->secret);

}

htmlhelper.cpp

#include "htmlhelper.h"

#include <string>

namespace html_helper {

bool ReplaceValueByMask(

std::string &htmlLine, const std::string &mask, const std::string &value)

{

if (mask.empty())

{

return false;

}

auto pos = htmlLine.find(mask);

if (pos > htmlLine.length())

{

return false;

}

auto replacedLine = htmlLine.substr(0, pos);

replacedLine.append(value);

replacedLine.append(

htmlLine.substr(pos + mask.length(), htmlLine.length()));

htmlLine = replacedLine;

return true;

}

bool ReplaceValueByMask(

std::string &htmlLine, const std::string &mask, long value)

{

return ReplaceValueByMask(htmlLine, mask, std::to_string(value));

}

void ReadHtmlLine(std::ifstream &fm, std::string &htmlLine)

{

htmlLine.clear();

std::getline(fm, htmlLine);

if (!fm.eof() && htmlLine.empty())

{

htmlLine = "\n";

}

}

} // namespace html_helper

login_form_handler.cpp

#include "login_form_handler.h"

#include "general.h"

#include "htmlhelper.h"

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

#include <rtl/stdio.h>

#include <rtl/string.h>

#include <iostream>

namespace consts {

const std::string AuthHtmlFile("mnt/auth.html");

} // namespace consts

struct LoginFormHandlerImpl : auth_service_ILoginForm_ops

{

LoginFormHandler *handler = nullptr;

};

nk_err_t OpenImpl(

struct auth_service_ILoginForm *self,

const auth_service_ILoginForm_Open_req *req,

const nk_arena *reqArena,

auth_service_ILoginForm_Open_res *res,

nk_arena *resArena)

{

auto ops = self != nullptr ?

static_cast<const LoginFormHandlerImpl *>(self->ops) :

nullptr;

if (ops == nullptr)

{

return NK_ENOENT;

}

res->result = ops->handler->OpenLoginStream();

return NK_EOK;

}

nk_err_t DisposeImpl(

struct auth_service_ILoginForm *self,

const auth_service_ILoginForm_Dispose_req *req,

const nk_arena *reqArr,

auth_service_ILoginForm_Dispose_res *res,

nk_arena *resArena)

{

auto reader = self != nullptr ?

static_cast<const LoginFormHandlerImpl *>(self->ops) :

nullptr;

if (reader == nullptr)

{

return NK_ENOENT;

}

reader->handler->Close();

return NK_EOK;

}

nk_err_t ReadImpl(

struct auth_service_ILoginForm *self,

const auth_service_ILoginForm_Read_req *req,

const nk_arena *reqArr,

auth_service_ILoginForm_Read_res *res,

nk_arena *resArena)

{

auto impl = self != nullptr ?

static_cast<const LoginFormHandlerImpl *>(self->ops) :

nullptr;

if (impl == nullptr)

{

return NK_ENOENT;

}

std::string htmlLine;

if (!impl->handler->ReadLine(htmlLine))

{

std::cerr << app::AppTag << __func__

<< " Error: Unable to read html line from file!" << std::endl;

}

nk_char_t *str =

nk_arena_alloc(nk_char_t, resArena, &res->line, htmlLine.length() + 1);

rtl_strncpy(str, htmlLine.c_str(), htmlLine.length());

return NK_EOK;

}

auth_service_ILoginForm *LoginFormHandler::CreateImpl(ISecretDataPtr secretData)

{

static LoginFormHandler reader(secretData);

static LoginFormHandlerImpl ops {};

static auth_service_ILoginForm impl = {.ops = &ops};

ops.Open = OpenImpl;

ops.Read = ReadImpl;

ops.Dispose = DisposeImpl;

ops.handler = &reader;

return &impl;

}

LoginFormHandler::LoginFormHandler(ISecretDataPtr secret) : m_secret(secret)

{}

bool LoginFormHandler::OpenLoginStream()

{

std::cout << app::AppTag << "Open login page request started" << std::endl;

m_secret->Init();

auto dh = m_secret->GetDhData();

m_htmlMaskData.clear();

m_htmlMaskData.emplace("{$serverG}", dh.g);

m_htmlMaskData.emplace("{$serverP}", dh.p);

m_htmlMaskData.emplace("{$serverKey}", dh.openKey);

m_htmlFile.close();

m_htmlFile.open(consts::AuthHtmlFile, std::ifstream::in);

if (!m_htmlFile.is_open())

{

std::cerr << app::AppTag << "Error: File '" << consts::AuthHtmlFile

<< "' not found" << std::endl;

return 0;

}

return m_htmlFile.is_open();

}

bool LoginFormHandler::ReadLine(std::string &htmlLine)

{

try

{

html_helper::ReadHtmlLine(m_htmlFile, htmlLine);

PutSecret(htmlLine);

}

catch (const std::exception &e)

{

std::cerr << app::AppTag << "Error: " << e.what() << std::endl;

return false;

}

return true;

}

void LoginFormHandler::Close()

{

m_htmlMaskData.clear();

m_htmlFile.close();

std::cout << app::AppTag << "Open login page request finished" << std::endl;

}

void LoginFormHandler::PutSecret(std::string &htmlLine)

{

for (auto it : m_htmlMaskData)

{

if (auto pval = std::get_if<std::string>(&it.second))

{

if (html_helper::ReplaceValueByMask(htmlLine, it.first, *pval))

{

break;

}

}

else if (auto pval = std::get_if<long>(&it.second))

{

if (html_helper::ReplaceValueByMask(htmlLine, it.first, *pval))

{

break;

}

}

}

}

login_result_handler.cpp

#include "login_result_handler.h"

#include "authservice.h"

#include "general.h"

#include "htmlhelper.h"

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

#include <rtl/string.h>

#include <iostream>

#include <sstream>

namespace consts {

const std::string AuthResultOkHtmlFile("mnt/result_ok.html");

const std::string AuthResultErrHtmlFile("mnt/result_err.html");

} // namespace consts

namespace utils {

std::string GetArenaString(const nk_arena *arena, const nk_ptr_t *src)

{

nk_uint32_t msgLength = 0;

nk_char_t *nkMsg = nk_arena_get(nk_char_t, arena, src, &msgLength);

return nkMsg != nullptr ? nkMsg : "";

}

} // namespace utils

struct LoginResultHandlerImpl : auth_service_ILoginResultForm_ops

{

LoginResultHandler *handler;

};

nk_err_t OpenImpl(

struct auth_service_ILoginResultForm *self,

const auth_service_ILoginResultForm_Open_req *req,

const nk_arena *reqArena,

auth_service_ILoginResultForm_Open_res *res,

nk_arena *resArena)

{

auto impl = self != nullptr ?

static_cast<const LoginResultHandlerImpl *>(self->ops) :

nullptr;

if (impl == nullptr)

{

return NK_ENOENT;

}

auto userName = utils::GetArenaString(reqArena, &req->login.userName);

auto hexPassword = utils::GetArenaString(reqArena, &req->login.password);

auto oppositeOpenKey = utils::GetArenaString(reqArena, &req->login.cryptoB);

res->result =

impl->handler->OpenStream(userName, hexPassword, oppositeOpenKey);

return NK_EOK;

}

nk_err_t DisposeImpl(

struct auth_service_ILoginResultForm *self,

const auth_service_ILoginResultForm_Dispose_req *req,

const nk_arena *reqArr,

auth_service_ILoginResultForm_Dispose_res *res,

nk_arena *resArena)

{

auto impl = self != nullptr ?

static_cast<const LoginResultHandlerImpl *>(self->ops) :

nullptr;

if (impl == nullptr)

{

return NK_ENOENT;

}

impl->handler->Close();

return NK_EOK;

}

nk_err_t ReadImpl(

struct auth_service_ILoginResultForm *self,

const auth_service_ILoginResultForm_Read_req *req,

const nk_arena *reqArr,

auth_service_ILoginResultForm_Read_res *res,

nk_arena *resArena)

{

auto impl = self != nullptr ?

static_cast<const LoginResultHandlerImpl *>(self->ops) :

nullptr;

if (impl == nullptr)

{

return NK_ENOENT;

}

std::string htmlLine;

if (!impl->handler->ReadLine(htmlLine))

{

std::cerr << app::AppTag << __func__

<< " Error: Unable to read html line from file!" << std::endl;

}

nk_char_t *str =

nk_arena_alloc(nk_char_t, resArena, &res->line, htmlLine.length() + 1);

rtl_strncpy(str, htmlLine.c_str(), htmlLine.length());

return NK_EOK;

}

auth_service_ILoginResultForm *

LoginResultHandler::CreateImpl(ISecretDataPtr secret)

{

static LoginResultHandler reader(secret);

static LoginResultHandlerImpl ops {};

static auth_service_ILoginResultForm impl = {.ops = &ops};

ops.Open = OpenImpl;

ops.Read = ReadImpl;

ops.Dispose = DisposeImpl;

ops.handler = &reader;

return &impl;

}

LoginResultHandler::LoginResultHandler(ISecretDataPtr secret) : m_secret(secret)

{}

bool LoginResultHandler::OpenStream(

const std::string &userName,

const std::string &hexPasswordStr,

const std::string &cryptoBstr)

{

std::cout << app::AppTag << "Auth request: userName = " << userName

<< " hexPasswordStr = " << hexPasswordStr

<< " client open key = " << cryptoBstr

<< std::endl;

std::string password;

if (m_secret->IsInit())

{

uint64_t oppKey;

std::istringstream iss(cryptoBstr);

iss >> oppKey;

auto cryptoKey = m_secret->GetSecret(oppKey);

password = DecryptHexString(hexPasswordStr, cryptoKey);

}

AuthService auth;

m_isAuthentified = auth.Authentication(userName, password);

m_htmlFile.close();

auto fileName = m_isAuthentified ? consts::AuthResultOkHtmlFile :

consts::AuthResultErrHtmlFile;

m_htmlFile.open(fileName, std::ifstream::in);

if (!m_htmlFile.is_open())

{

std::cerr << app::AppTag << "Error: File '" << fileName << "' not found"

<< std::endl;

return 0;

}

return m_htmlFile.is_open();

}

bool LoginResultHandler::ReadLine(std::string &htmlLine)

{

try

{

html_helper::ReadHtmlLine(m_htmlFile, htmlLine);

}

catch (const std::exception &e)

{

std::cerr << app::AppTag << "Error: " << e.what() << std::endl;

return false;

}

return true;

}

void LoginResultHandler::Close()

{

m_htmlFile.close();

m_secret->Dispose();

}

bool LoginResultHandler::HexStrToNum(

const std::string &hexValueString, char &value)

{

char *p_end;

value = static_cast<char>(std::strtol(hexValueString.c_str(), &p_end, 16));

if (p_end == hexValueString.c_str())

{

return false;

}

return true;

}

std::string

LoginResultHandler::DecryptHexString(const std::string &input, long secret)

{

std::string strKey = std::to_string(secret);

while (strKey.length() < input.length())

{

strKey += strKey;

}

std::string result;

size_t j = 0;

for (size_t i = 0; i < input.length(); i += 2)

{

auto hexValue1String = input.substr(i, 2);

char value1;

if (!HexStrToNum(hexValue1String, value1))

{

return "";

}

char value2 = strKey[j++];

char xorValue = value1 ^ value2;

result += xorValue;

}

return result;

};

Page top