Teaspeak-Server/client/src/Identity.cpp

218 lines
6.5 KiB
C++

//
// Created by wolverindev on 07.10.17.
//
#include <iostream>
#include <openssl/sha.h>
#include "misc/base64.h"
#include "Identity.h"
#define ECC_TYPE_INDEX 5
using namespace std;
static const char *TSKEY =
"b9dfaa7bee6ac57ac7b65f1094a1c155"
"e747327bc2fe5d51c512023fe54a2802"
"01004e90ad1daaae1075d53b7d571c30"
"e063b5a62a4a017bb394833aa0983e6e";
static int obfuscateInplace(char *data, uint32_t length) {
int dataSize = min((uint32_t) 100, length);
for (int i = 0; i < dataSize; i++) {
data[i] ^= TSKEY[i];
}
char hash[20];
hash_state ctx;
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
{ return -1; }
for (int i = 0; i < 20; i++) {
data[i] ^= hash[i];
}
return 0;
}
static int deObfuscateInplace(char *data, uint32_t length) {
char hash[20];
hash_state ctx;
if (sha1_init(&ctx) != CRYPT_OK)
{ return -1; }
if (sha1_process(&ctx, (uint8_t*)data + 20, strlen(data + 20)) != CRYPT_OK)
{ return -1; }
if (sha1_done(&ctx, (uint8_t*)hash) != CRYPT_OK)
{ return -1; }
for (int i = 0; i < 20; i++) {
data[i] ^= hash[i];
}
int dataSize = min((uint32_t) 100, length);
for (int i = 0; i < dataSize; i++) {
data[i] ^= TSKEY[i];
}
return 0;
}
namespace ts {
Identity* Identity::createNew() {
auto result = new Identity();
prng_state rndState{};
memset(&rndState, 0, sizeof(prng_state));
int err;
result->keyPair = new ecc_key;
cout << " -> " << find_prng("sprng") << endl;
if((err = ecc_make_key_ex(&rndState, find_prng("sprng"), result->keyPair, &ltc_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK) {
cerr << "Cant create a new identity (Keygen)" << endl;
cerr << "Message: " << error_to_string(err) << endl;
delete result;
return nullptr;
}
return result;
}
Identity::Identity(std::string asnStruct, int64_t keyOffset, int64_t lastCheckedOffset) {
this->keyOffset = keyOffset;
this->lastCheckedOffset = lastCheckedOffset;
importKey(asnStruct);
}
Identity::Identity(std::string data) : Identity() {
int vindex = data.find('V');
assert(vindex > 0);
auto slevel = data.substr(0, vindex);
assert(slevel.find_first_not_of("0123456789") == std::string::npos);
this->keyOffset = stol(slevel);
data = data.substr(vindex + 1);
data = base64::decode(data);
if(deObfuscateInplace((char *) data.data(), data.length()) < 0) {
cerr << "Cand decript identitry data" << endl;
return;
}
importKey(base64::decode(data));
}
Identity::Identity() {
this->keyOffset = 0;
this->lastCheckedOffset = 0;
this->keyPair = nullptr;
}
Identity::~Identity() {
delete this->keyPair;
this->keyPair = nullptr;
}
void Identity::importKey(std::string asnStruct) {
this->keyPair = new ecc_key;
int err;
if((err = ecc_import_ex((const unsigned char *) asnStruct.data(), asnStruct.length(), this->keyPair, &ltc_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){
delete this->keyPair;
this->keyPair = nullptr;
cerr << "Cant import identity from asn structure" << endl;
cerr << "Message: " << error_to_string(err) << endl;
return;
}
}
std::string Identity::exportIdentity() {
string data = privateKey();
obfuscateInplace((char *) data.data(), data.length());
return to_string(this->lastValidKeyOffset()) + "V" + base64_encode(data);
}
std::string Identity::publicKey() {
assert(this->keyPair);
size_t bufferLength = 1028;
char buffer[bufferLength];
ecc_export((unsigned char *) buffer, &bufferLength, PK_PUBLIC, this->keyPair);
return base64_encode(string(buffer, bufferLength));
}
std::string Identity::privateKey() {
assert(this->keyPair);
size_t bufferLength = 1028;
char buffer[bufferLength];
ecc_export((unsigned char *) buffer, &bufferLength, PK_PRIVATE, this->keyPair);
return base64_encode(string(buffer, bufferLength));
}
ecc_key& Identity::getPrivateKey() {
return *keyPair;
}
#define MaxUlongString 20
bool Identity::improveSecurityLevel(int target) {
auto publicKey = this->publicKey();
char hashBuffer[publicKey.length() + MaxUlongString];
memcpy(hashBuffer, publicKey.data(), publicKey.length());
this->lastCheckedOffset = max(this->lastCheckedOffset, this->keyOffset);
int best = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
while(true){
if(best >= target) return true;
int currentLevel = getSecurityLevel(hashBuffer, publicKey.length(), this->lastCheckedOffset);
if(currentLevel >= best){
this->keyOffset = this->lastCheckedOffset;
best = currentLevel;
}
this->lastCheckedOffset++;
}
}
int Identity::getSecurityLevel() {
auto length = publicKey().length();
char hashBuffer[length + MaxUlongString];
auto publicKey = this->publicKey();
memcpy(hashBuffer, publicKey.data(), publicKey.length());
return getSecurityLevel(hashBuffer, publicKey.length(), this->keyOffset);
}
int Identity::getSecurityLevel(char *hashBuffer, size_t keyLength, int64_t offset) {
char numBuffer[MaxUlongString];
int numLen = 0;
do {
numBuffer[numLen] = '0' + (offset % 10);
offset /= 10;
numLen++;
} while(offset > 0);
for(int i = 0; i < numLen; i++)
hashBuffer[keyLength + i] = numBuffer[numLen - (i + 1)];
char shaBuffer[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *) hashBuffer, keyLength + numLen, (unsigned char *) shaBuffer);
//Leading zero bits
register int zeroBits = 0;
register int i;
for(i = 0; i < SHA_DIGEST_LENGTH; i++)
if(shaBuffer[i] == 0) zeroBits += 8;
else break;
if(i < SHA_DIGEST_LENGTH)
for(int bit = 0; bit < 8; bit++)
if((shaBuffer[i] & (1 << bit)) == 0) zeroBits++;
else break;
return zeroBits;
}
}