You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
winix/winixd/core/sessionidmanager.cpp

560 lines
12 KiB

/*
* This file is a part of Winix
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2014-2021, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>
#include "sessionidmanager.h"
#include "space/spaceparser.h"
#include "utf8/utf8.h"
#include "date/date.h"
#include "misc.h"
namespace Winix
{
SessionIdManager::SessionIdManager()
{
algorithm_type = 'a';
key_tab_size = 256;
key_index = 0;
last_key_generated = 0;
key_renew_time = 60;
was_inited = false;
}
void SessionIdManager::Init(const std::wstring & keys_file)
{
was_inited = true;
key_tab1.resize(key_tab_size);
key_tab2.resize(key_tab_size);
aes1.resize(key_tab_size);
aes2.resize(key_tab_size);
key_file_name = keys_file;
ReadKeysFromFile(key_file_name);
}
void SessionIdManager::SetKeyRenewTime(time_t renew_time)
{
key_renew_time = renew_time;
if( key_renew_time < 10 )
key_renew_time = 10;
time_t one_month = 60 * 60 * 24 * 31;
if( key_renew_time > one_month )
key_renew_time = one_month;
}
void SessionIdManager::ReadKey(const wchar_t * name, pt::Space & space, std::vector<std::string> & dest_key)
{
std::vector<std::wstring> keys;
std::string key_ascii, key_base64_decoded;
space.to_list(name, keys);
for(size_t i=0 ; i<key_tab_size ; ++i)
dest_key[i].clear();
for(size_t i=0 ; i<keys.size() && i<key_tab_size ; ++i)
{
dest_key[i].clear();
if( pt::wide_to_utf8(keys[i], key_ascii) )
{
if( base64.Decode(key_ascii, key_base64_decoded) )
{
size_t len = key_base64_decoded.size();
if( len == 16 || len == 24 || len == 32 )
{
dest_key[i] = key_base64_decoded;
}
}
}
}
}
void SessionIdManager::InitializeAesKeys(size_t index)
{
if( !aes1[index].Key((unsigned char*)key_tab1[index].c_str(), key_tab1[index].size()) )
log << log1 << "SIM: I cannot initialize a key1, size of the key: " << key_tab1[index].size() << logend;
if( !aes2[index].Key((unsigned char*)key_tab2[index].c_str(), key_tab2[index].size()) )
log << log1 << "SIM: I cannot initialize a key2, size of the key: " << key_tab2[index].size() << logend;
}
void SessionIdManager::InitializeAesKeys()
{
for(size_t i=0 ; i < key_tab_size ; ++i)
{
if( AreKeysCorrect(i) )
InitializeAesKeys(i);
}
}
bool SessionIdManager::ReadKeysFromFile(const wchar_t * file)
{
pt::Space space;
pt::SpaceParser parser;
pt::Date date;
pt::SpaceParser::Status status = parser.parse_space_file(file, space);
if( status == pt::SpaceParser::ok )
{
key_index = space.to_ulong(L"key_index");
if( key_index >= 256 )
key_index = 0;
if( date.Parse(space.to_wstr(L"last_key_generated", L"0")) )
last_key_generated = date.ToTime();
ReadKey(L"key_tab1", space, key_tab1);
ReadKey(L"key_tab2", space, key_tab2);
InitializeAesKeys();
}
else
{
log << log1 << "SIM: I cannot read the session keys from: " << file << logend;
}
return status == pt::SpaceParser::ok;
}
bool SessionIdManager::ReadKeysFromFile(const std::wstring & file)
{
return ReadKeysFromFile(file.c_str());
}
void SessionIdManager::SaveKeysToFile(std::vector<std::string> & keys)
{
out_file << "(\n";
for(size_t i=0 ; i<keys.size() ; ++i)
{
base64.Encode(keys[i], tmp_key_base64_encoded);
out_file << '\"' << tmp_key_base64_encoded << "\"\n";
}
out_file << ")\n\n";
tmp_key_base64_encoded.clear();
}
bool SessionIdManager::SaveKeysToFile(const wchar_t * file)
{
pt::Date date = last_key_generated;
pt::wide_to_utf8(file, file_name_ascii);
out_file.clear();
out_file.open(file_name_ascii, std::ios_base::binary | std::ios_base::out);
if( out_file )
{
out_file << "key_index = " << key_index << "\n";
out_file << "last_key_generated = \"";
date.Serialize(out_file);
out_file << "\"\n\n";
out_file << "key_tab1 = ";
SaveKeysToFile(key_tab1);
out_file << "key_tab2 = ";
SaveKeysToFile(key_tab2);
out_file.flush();
}
out_file.close();
file_name_ascii.clear();
SetPriv(file, 0600);
return !out_file.fail();
}
bool SessionIdManager::SaveKeysToFile(const std::wstring & file)
{
return SaveKeysToFile(file.c_str());
}
bool SessionIdManager::AreKeysCorrect(size_t index)
{
if( index >= 256 )
return false;
size_t len = key_tab1[index].size();
if( len != 16 && len != 24 && len != 32 )
return false;
len = key_tab2[index].size();
if( len != 16 && len != 24 && len != 32 )
return false;
return true;
}
/*
* IMPROVE ME
* we need a better algorithm
*/
void SessionIdManager::GenerateKey(std::string & key, time_t cur_utc_time)
{
unsigned int pid = (unsigned int)getpid();
unsigned int t = (unsigned int)cur_utc_time;
key.clear();
for(size_t i=0 ; i<16 ; ++i)
{
unsigned int r = rand();
unsigned int v = r ^ pid ^ t;
v = ((v >> 24) ^ (v >> 16) ^ (v >> 8) ^ v) & 0xff;
key.push_back((unsigned char)v);
}
}
void SessionIdManager::GenerateKeys(size_t index, time_t cur_utc_time)
{
GenerateKey(key_tab1[index], cur_utc_time);
GenerateKey(key_tab2[index], cur_utc_time);
InitializeAesKeys(index);
last_key_generated = cur_utc_time;
if( !SaveKeysToFile(key_file_name) )
log << log2 << "SIM: I cannot save the session keys to: " << key_file_name << logend;
}
void SessionIdManager::CheckKeys(time_t cur_utc_time)
{
if( !AreKeysCorrect(key_index) )
{
log << log1 << "SIM: keys with index: " << key_index << " are incorrect, generating new keys" << logend;
GenerateKeys(key_index, cur_utc_time);
}
else
if( last_key_generated + key_renew_time < cur_utc_time )
{
key_index += 1;
if( key_index >= key_tab_size )
key_index = 0;
log << log2 << "SIM: generating new AES keys with index: " << key_index << logend;
GenerateKeys(key_index, cur_utc_time);
}
}
void SessionIdManager::RandPadding(size_t & pad_top_size, char & pad_top_value,
size_t & pad_bottom_size, char & pad_bottom_value)
{
pad_top_size = (std::rand() * 5) / RAND_MAX; // multiply by 5 (not by 4)
if( pad_top_size > 4 )
pad_top_size = 4;
pad_top_size += 5; // now pad_top_size is from <5;9>
pad_top_value = (char)std::rand();
pad_bottom_size = 14 - pad_top_size; // pad_bottom_size is from <5;9> too
pad_bottom_value = (char)std::rand();
}
void SessionIdManager::AppendSum(std::string & str)
{
int s = 0;
for(size_t i=0 ; i<str.size() ; ++i)
s += (int)(unsigned char)str[i];
str += (unsigned char)s;
}
void SessionIdManager::AppendXor(std::string & str)
{
int s = 0;
for(size_t i=0 ; i<str.size() ; ++i)
s ^= (int)(unsigned char)str[i];
str += (unsigned char)s;
}
void SessionIdManager::CopyString(const std::string & in, std::wstring & out)
{
out.clear();
if( out.capacity() < in.size() )
out.reserve(in.size());
for(size_t i=0 ; i<in.size() ; ++i)
out += in[i];
}
void SessionIdManager::CopyString(const std::wstring & in, std::string & out)
{
out.clear();
if( out.capacity() < in.size() )
out.reserve(in.size());
for(size_t i=0 ; i<in.size() ; ++i)
out += in[i];
}
bool SessionIdManager::Encode(std::string & str)
{
if( str.size() != 34 )
return false;
if( !aes1[key_index].Encode((unsigned char*)string_token.c_str()+2, 16) )
{
log << log1 << "SIM: I cannot AES encode the first part of the token" << logend;
return false;
}
if( !aes2[key_index].Encode((unsigned char*)string_token.c_str()+16+2, 16) )
{
log << log1 << "SIM: I cannot AES encode the second part of the token" << logend;
return false;
}
return true;
}
bool SessionIdManager::EncodeToken(size_t id, unsigned int index, time_t cur_utc_time, std::wstring & token)
{
size_t pad_top_size;
size_t pad_bottom_size;
char pad_top_value;
char pad_bottom_value;
string_token.clear();
string_token.reserve(50);
if( !was_inited )
return false;
CheckKeys(cur_utc_time);
RandPadding(pad_top_size, pad_top_value, pad_bottom_size, pad_bottom_value);
string_token += algorithm_type;
string_token += (unsigned char)key_index;
string_token += pad_top_value;
string_token += pad_bottom_value;
string_token += (unsigned char)pad_top_size;
string_token += (unsigned char)pad_bottom_size;
string_token.append(pad_top_size, pad_top_value);
Append(string_token, id);
Append(string_token, index);
string_token.append(pad_bottom_size, pad_bottom_value);
AppendSum(string_token);
AppendXor(string_token);
if( !Encode(string_token) )
return false;
base64.Encode(string_token, string_token_base64);
CopyString(string_token_base64, token);
return true;
}
bool SessionIdManager::IsPaddingCorrect(const char * str, size_t len, char val)
{
if( len < 5 || len > 9 )
return false;
for(size_t i=0 ; i<len ; ++i)
if( str[i] != val )
return false;
return true;
}
bool SessionIdManager::DecodeAES(const char * str, size_t key)
{
if( !aes1[key].Decode((unsigned char*)str, 16) )
{
log << log1 << "SIM: I cannot AES decode the first block" << logend;
return false;
}
if( !aes2[key].Decode((unsigned char*)str + 16, 16) )
{
log << log1 << "SIM: I cannot AES decode the second block" << logend;
return false;
}
return true;
}
bool SessionIdManager::CheckControlSums(const char * str)
{
char old_sum = *(str++);
char old_xor = *(str++);
string_token.erase(string_token.size()-2);
AppendSum(string_token);
AppendXor(string_token);
if( old_sum != string_token[string_token.size()-2] ||
old_xor != string_token[string_token.size()-1] )
return false;
return true;
}
bool SessionIdManager::DecodeTokenA(size_t & id, unsigned int & index)
{
size_t pad_top_size;
size_t pad_bottom_size;
char pad_top_value;
char pad_bottom_value;
const char * str = string_token.c_str() + 1;
size_t key = (unsigned char)(*str);
str += 1;
if( !DecodeAES(str, key) )
return false;
pad_top_value = *(str++);
pad_bottom_value = *(str++);
pad_top_size = (unsigned char)*(str++);
pad_bottom_size = (unsigned char)*(str++);
if( pad_bottom_size != 14 - pad_top_size )
return false;
if( !IsPaddingCorrect(str, pad_top_size, pad_top_value) )
return false;
str += pad_top_size;
Read(str, id);
str += 8; // sizeof(id), it's better to use constant '8' instead of sizeof() operator
// because at the beginning we are making a test whether the string size is equal to 34
// (in the future sizeof(size_t) can be different from 8)
Read(str, index);
str += 4; // sizeof(index)
if( !IsPaddingCorrect(str, pad_bottom_size, pad_bottom_value) )
return false;
str += pad_bottom_size;
return CheckControlSums(str);
}
bool SessionIdManager::DecodeToken(const std::wstring & token, size_t & id, unsigned int & index)
{
if( !was_inited )
return false;
CopyString(token, string_token_base64);
if( !base64.Decode(string_token_base64, string_token) )
return false;
if( string_token.size() != 34 )
return false;
if( string_token[0] == 'a' )
return DecodeTokenA(id, index);
return false;
}
} // namespace Winix