Updating the license system
This commit is contained in:
		
							parent
							
								
									244b7dc0cb
								
							
						
					
					
						commit
						647248009d
					
				@ -85,6 +85,8 @@ target_link_libraries(TeaLicenseClient
 | 
			
		||||
		${LIBRARY_TOM_MATH}
 | 
			
		||||
		${LIBRARY_TOM_CRYPT}
 | 
			
		||||
		stdc++fs.a
 | 
			
		||||
		${LIBRARY_PATH_TERMINAL}
 | 
			
		||||
		${LIBRARY_PATH_ED255}
 | 
			
		||||
 | 
			
		||||
		${LIBRARY_PATH_BORINGSSL_SSL}
 | 
			
		||||
		${LIBRARY_PATH_BORINGSSL_CRYPTO}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,8 @@
 | 
			
		||||
#include <shared/License.h>
 | 
			
		||||
#include <shared/LicenseRequest.h>
 | 
			
		||||
#include <event2/thread.h>
 | 
			
		||||
#include <random>
 | 
			
		||||
#include <ed25519/ed25519.h>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace std::chrono;
 | 
			
		||||
@ -24,7 +26,33 @@ using namespace license;
 | 
			
		||||
int main(int ac, char** av){
 | 
			
		||||
	auto state = evthread_use_pthreads();
 | 
			
		||||
	assert(state == 0);
 | 
			
		||||
	string error;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	std::array<uint8_t, 32> private_key, public_key;
 | 
			
		||||
 | 
			
		||||
	std::random_device rd;
 | 
			
		||||
	std::uniform_int_distribution<uint8_t> d;
 | 
			
		||||
 | 
			
		||||
	uint8_t root_seed[64];
 | 
			
		||||
	for(auto& e : root_seed)
 | 
			
		||||
		e = d(rd);
 | 
			
		||||
	ed25519_create_keypair(public_key.data(), private_key.data(), root_seed);
 | 
			
		||||
 | 
			
		||||
	cout << "Key Pair generated:" << endl;
 | 
			
		||||
	cout << "Private Key:" << hex;
 | 
			
		||||
	for(auto& e : private_key)
 | 
			
		||||
		cout << " 0x" << (e <= 0xF ? "0" : "") << (uint32_t) e << ",";
 | 
			
		||||
	cout << endl;
 | 
			
		||||
 | 
			
		||||
	cout << "Public Key :" << hex;
 | 
			
		||||
	for(auto& e : public_key)
 | 
			
		||||
		cout << " 0x" << (e <= 0xF ? "0" : "") << (uint32_t) e << ",";
 | 
			
		||||
	cout << endl;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	srand(system_clock::now().time_since_epoch().count());
 | 
			
		||||
	cout << "Generating new license" << endl;
 | 
			
		||||
 | 
			
		||||
@ -83,5 +111,6 @@ int main(int ac, char** av){
 | 
			
		||||
			cerr << "Could not load info after throwing: " << endl << ex.what() << endl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@ -13,7 +13,6 @@ message Blacklist {
 | 
			
		||||
    optional string reason          = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
message LicenseInfo {
 | 
			
		||||
    required bytes  key             = 1;
 | 
			
		||||
    required string username        = 2;
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,14 @@
 | 
			
		||||
#include <google/protobuf/message.h>
 | 
			
		||||
#include <misc/base64.h>
 | 
			
		||||
#include <random>
 | 
			
		||||
#include <ed25519/ed25519.h>
 | 
			
		||||
 | 
			
		||||
//#define NO_OPEN_SSL
 | 
			
		||||
#include <misc/digest.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <ed25519/ge.h>
 | 
			
		||||
#include <ed25519/sc.h>
 | 
			
		||||
#include "crypt.h"
 | 
			
		||||
#include "License.h"
 | 
			
		||||
 | 
			
		||||
@ -100,3 +104,716 @@ namespace license {
 | 
			
		||||
        this->data = "";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::array<uint8_t, 32> license::v2::public_root_key = {
 | 
			
		||||
	0x84, 0x54, 0x2c, 0x2b, 0x46, 0x19, 0x05, 0x6c,
 | 
			
		||||
	0x01, 0xd8, 0x61, 0x49, 0x4e, 0x48, 0x47, 0x1e,
 | 
			
		||||
	0x6c, 0x61, 0xfa, 0x6a, 0xde, 0x6b, 0x1c, 0x76,
 | 
			
		||||
	0x3a, 0xeb, 0x2f, 0x39, 0x49, 0x3d, 0x71, 0x35
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace license::v2 {
 | 
			
		||||
	License::~License() {
 | 
			
		||||
		if(this->private_buffer)
 | 
			
		||||
			::free(this->private_buffer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::shared_ptr<License> License::create(const std::vector<std::shared_ptr<const license::v2::HierarchyEntry>> &hierarchy, const std::array<uint8_t, 32> &prv_key) {
 | 
			
		||||
		assert(!hierarchy.empty());
 | 
			
		||||
		auto result = shared_ptr<License>(new License{});
 | 
			
		||||
 | 
			
		||||
		result->_version = 2;
 | 
			
		||||
		result->crypt_seed = std::mt19937_64{std::random_device{}()}();
 | 
			
		||||
		result->_hierarchy = hierarchy;
 | 
			
		||||
		result->private_data = LicensePrivate::create(result, hierarchy.size() - 1, prv_key.data());
 | 
			
		||||
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::shared_ptr<License> License::read(const uint8_t *buffer, size_t length, uint8_t &error) {
 | 
			
		||||
		auto result = shared_ptr<License>(new License{});
 | 
			
		||||
 | 
			
		||||
		LicenseHeader header{};
 | 
			
		||||
		BodyHeader body_header{};
 | 
			
		||||
		if(length < sizeof(header) + sizeof(body_header)) {
 | 
			
		||||
			error = 2; /* buffer too small */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
		memcpy(&header, buffer, sizeof(header));
 | 
			
		||||
 | 
			
		||||
		if(header.version != 2) {
 | 
			
		||||
			error = 3; /* invalid version */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
		result->_version = header.version;
 | 
			
		||||
 | 
			
		||||
		std::mt19937_64 crypt_key_gen{header.crypt_key_seed};
 | 
			
		||||
 | 
			
		||||
		/* verify the crypt key gen */
 | 
			
		||||
		{
 | 
			
		||||
			crypt_key_gen.discard(header.crypt_key_verify_offset);
 | 
			
		||||
			uint64_t expected = 0;
 | 
			
		||||
			memcpy(&expected, header.crypt_key_verify, 5);
 | 
			
		||||
 | 
			
		||||
			uint64_t received = crypt_key_gen();
 | 
			
		||||
			received = received ^ (received >> 40UL);
 | 
			
		||||
			received &= 0xFFFFFFFFFFULL;
 | 
			
		||||
 | 
			
		||||
			if(expected != received) {
 | 
			
		||||
				error = 4; /* invalid key sequence */
 | 
			
		||||
				return nullptr;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		crypt_key_gen.seed(header.crypt_key_seed);
 | 
			
		||||
		auto decoded_buffer_length = length - sizeof(header);
 | 
			
		||||
		auto decoded_buffer = unique_ptr<uint8_t, decltype(::free)*>{(uint8_t*) malloc(decoded_buffer_length), ::free};
 | 
			
		||||
		if(!decoded_buffer) {
 | 
			
		||||
			error = 1; /* out of memory */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* "decode" the data */
 | 
			
		||||
		{
 | 
			
		||||
			auto index = 0;
 | 
			
		||||
			while(index + 4 < decoded_buffer_length) {
 | 
			
		||||
				auto& memory = *(uint32_t*) (&*decoded_buffer + index);
 | 
			
		||||
				memory = *(uint32_t*) (buffer + index);
 | 
			
		||||
				memory ^= (uint32_t) crypt_key_gen();
 | 
			
		||||
				index += 4;
 | 
			
		||||
			}
 | 
			
		||||
			while(index < decoded_buffer_length) {
 | 
			
		||||
				auto& memory = *(uint8_t*) (&*decoded_buffer + index);
 | 
			
		||||
				memory = *(uint8_t*) (buffer + index);
 | 
			
		||||
				memory ^= (uint8_t) crypt_key_gen();
 | 
			
		||||
				index++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		memcpy(&body_header, &*decoded_buffer, sizeof(body_header));
 | 
			
		||||
		if(decoded_buffer_length < sizeof(body_header) + body_header.length_hierarchy + body_header.length_private_data) {
 | 
			
		||||
			error = 2; /* buffer too small */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto hierarchy_buffer = &*decoded_buffer + sizeof(body_header) + body_header.length_private_data;
 | 
			
		||||
		/* test the checksum for the hierarchy (license data indirectly verified via data_sign) */
 | 
			
		||||
		{
 | 
			
		||||
			uint8_t sha_buffer[20];
 | 
			
		||||
			digest::sha1((char*) hierarchy_buffer, body_header.length_hierarchy, sha_buffer);
 | 
			
		||||
			if(memcmp(sha_buffer, body_header.checksum_hierarchy, 20) != 0) {
 | 
			
		||||
				error = 5; /* checksum does not match */
 | 
			
		||||
				return nullptr;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* now lets read the hierarchy data */
 | 
			
		||||
		{
 | 
			
		||||
			size_t offset = 0, length = body_header.length_hierarchy;
 | 
			
		||||
			while(offset < length) {
 | 
			
		||||
				auto entry = HierarchyEntry::read(hierarchy_buffer, offset, length);
 | 
			
		||||
				if(!entry) {
 | 
			
		||||
					error = 6; /* failed to read an entry */
 | 
			
		||||
					return nullptr;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				result->_hierarchy.push_back(entry);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* verify the given data */
 | 
			
		||||
		auto public_key = result->generate_public_key(public_root_key.data());
 | 
			
		||||
		if(!ed25519_verify(body_header.private_data_sign, &*decoded_buffer + sizeof(body_header), body_header.length_private_data, public_key.data())) {
 | 
			
		||||
			error = 7; /* failed to verify private data */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memcpy(result->private_buffer_sign.data(), body_header.private_data_sign, 64);
 | 
			
		||||
 | 
			
		||||
		/* copy the private data */
 | 
			
		||||
		result->private_buffer = (uint8_t*) malloc(body_header.length_private_data);
 | 
			
		||||
		result->private_buffer_length = body_header.length_private_data;
 | 
			
		||||
		memcpy(result->private_buffer, &*decoded_buffer + sizeof(body_header), body_header.length_private_data);
 | 
			
		||||
 | 
			
		||||
		/* let parse the private data */
 | 
			
		||||
		result->private_data = LicensePrivate::read(result, result->_version, result->private_buffer, result->private_buffer_length, error);
 | 
			
		||||
		if(!result->private_data) {
 | 
			
		||||
			error = 7; /* failed to parse private data */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string License::write(int &error) {
 | 
			
		||||
		if(!this->private_data || !this->private_buffer_length) {
 | 
			
		||||
			error = 2; /* missing private data */
 | 
			
		||||
			return "";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* lets estimate a buffer size */
 | 
			
		||||
		auto buffer_size = sizeof(LicenseHeader) + sizeof(BodyHeader) + this->private_buffer_length;
 | 
			
		||||
 | 
			
		||||
		for(auto& he : this->_hierarchy)
 | 
			
		||||
			if(!he->write(nullptr, buffer_size, 0)) {
 | 
			
		||||
				error = 3; /* failed to estimate buffer size */
 | 
			
		||||
				return "";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		auto buffer = unique_ptr<uint8_t, decltype(::free)*>{(uint8_t*) malloc(buffer_size), ::free};
 | 
			
		||||
		LicenseHeader license_header{};
 | 
			
		||||
		BodyHeader body_header{};
 | 
			
		||||
 | 
			
		||||
		/* first copy the private data */
 | 
			
		||||
		{
 | 
			
		||||
			memcpy(body_header.private_data_sign, this->private_buffer_sign.data(), this->private_buffer_sign.size());
 | 
			
		||||
			memcpy(&*buffer + sizeof(license_header) + sizeof(body_header), this->private_buffer, this->private_buffer_length);
 | 
			
		||||
			body_header.length_private_data = this->private_buffer_length;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* lets write the hierarchy */
 | 
			
		||||
		{
 | 
			
		||||
			auto offset = sizeof(license_header) + sizeof(body_header) + this->private_buffer_length;
 | 
			
		||||
			const auto begin_offset = offset;
 | 
			
		||||
			for(auto& he : this->_hierarchy)
 | 
			
		||||
				if(!he->write(&*buffer, offset, buffer_size)) {
 | 
			
		||||
					error = 3; /* failed to write hierarchy */
 | 
			
		||||
					return "";
 | 
			
		||||
				}
 | 
			
		||||
			body_header.length_hierarchy = offset - begin_offset;
 | 
			
		||||
 | 
			
		||||
			digest::sha1((char*) &*buffer + begin_offset, body_header.length_hierarchy, body_header.checksum_hierarchy);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* write the body header */
 | 
			
		||||
		memcpy(&*buffer + sizeof(license_header), &body_header, sizeof(body_header));
 | 
			
		||||
 | 
			
		||||
		/* lets generate the license header */
 | 
			
		||||
		{
 | 
			
		||||
			std::mt19937_64 rnd{std::random_device{}()};
 | 
			
		||||
 | 
			
		||||
			license_header.version = 2;
 | 
			
		||||
			license_header.crypt_key_seed = rnd();
 | 
			
		||||
			license_header.crypt_key_verify_offset = std::uniform_int_distribution<uint8_t>{}(rnd);
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				rnd.seed(license_header.crypt_key_seed);
 | 
			
		||||
				rnd.discard(license_header.crypt_key_verify_offset);
 | 
			
		||||
 | 
			
		||||
				uint64_t expected = rnd();
 | 
			
		||||
				expected = expected ^ (expected >> 40UL);
 | 
			
		||||
				expected &= 0xFFFFFFFFFFULL;
 | 
			
		||||
 | 
			
		||||
				memcpy(license_header.crypt_key_verify, &expected, 5);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* now lets "encrypt" the body */
 | 
			
		||||
		{
 | 
			
		||||
			std::mt19937_64 crypt_key_gen{license_header.crypt_key_seed};
 | 
			
		||||
 | 
			
		||||
			auto index = sizeof(license_header);
 | 
			
		||||
			while(index + 4 < buffer_size) {
 | 
			
		||||
				auto& memory = *(uint32_t*) (&*buffer + index);
 | 
			
		||||
				memory = *(uint32_t*) (&*buffer + index);
 | 
			
		||||
				memory ^= (uint32_t) crypt_key_gen();
 | 
			
		||||
				index += 4;
 | 
			
		||||
			}
 | 
			
		||||
			while(index < buffer_size) {
 | 
			
		||||
				auto& memory = *(uint8_t*) (&*buffer + index);
 | 
			
		||||
				memory = *(uint8_t*) (&*buffer + index);
 | 
			
		||||
				memory ^= (uint8_t) crypt_key_gen();
 | 
			
		||||
				index++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* write the license header */
 | 
			
		||||
		memcpy(&*buffer, &license_header, sizeof(license_header));
 | 
			
		||||
		return std::string((char*) &*buffer, buffer_size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool License::private_data_editable() const {
 | 
			
		||||
		return this->private_data->private_key_calculable(this->_hierarchy.size() - 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool License::write_private_data(const LicensePrivateWriteOptions& write_options) {
 | 
			
		||||
		uint8_t private_key[64]; //ed25519_sign requires 64 bytes (may it expects a public key in front?)
 | 
			
		||||
		if(!this->private_data->calculate_private_key(private_key, this->_hierarchy.size() - 1))
 | 
			
		||||
			return false;
 | 
			
		||||
		memcpy(private_key + 32, private_key, 32);
 | 
			
		||||
 | 
			
		||||
		auto public_key = this->generate_public_key(public_root_key.data());
 | 
			
		||||
 | 
			
		||||
		size_t length = 0, offset = 0;
 | 
			
		||||
		if(!this->private_data->write(nullptr, length, 65536, write_options))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		if(this->private_buffer)
 | 
			
		||||
			::free(this->private_buffer);
 | 
			
		||||
		this->private_buffer_length = length;
 | 
			
		||||
		this->private_buffer = (uint8_t*) malloc(length);
 | 
			
		||||
		if(!this->private_buffer) return false;
 | 
			
		||||
 | 
			
		||||
		if(!this->private_data->write(this->private_buffer, offset, length, write_options))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		ed25519_sign(this->private_buffer_sign.data(), this->private_buffer, length, public_key.data(), private_key);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::array<uint8_t, 32> License::generate_public_key(const uint8_t* root_key, int length) const {
 | 
			
		||||
		uint8_t hash_buffer[64];
 | 
			
		||||
 | 
			
		||||
		ge_p3 parent_key{};
 | 
			
		||||
		ge_cached parent_cached{};
 | 
			
		||||
 | 
			
		||||
		/* import the main parent key */
 | 
			
		||||
		ge_frombytes_negate_vartime(&parent_key, root_key);
 | 
			
		||||
 | 
			
		||||
		/* undo the negate */
 | 
			
		||||
		fe_neg(parent_key.X, parent_key.X);
 | 
			
		||||
		fe_neg(parent_key.T, parent_key.T);
 | 
			
		||||
 | 
			
		||||
		for(const auto& entry : this->_hierarchy) {
 | 
			
		||||
			if(length-- == 0) continue;
 | 
			
		||||
 | 
			
		||||
			ge_p3 e_pub_key{};
 | 
			
		||||
			ge_frombytes_negate_vartime(&e_pub_key, entry->public_key().data());
 | 
			
		||||
 | 
			
		||||
			ge_p3_to_cached(&parent_cached, &parent_key);
 | 
			
		||||
 | 
			
		||||
			/* malloc could fail, but we ignore this for now */
 | 
			
		||||
			if(!entry->hash(hash_buffer)) /* */;
 | 
			
		||||
 | 
			
		||||
			/* import hash (convert to a valid coordinate) */
 | 
			
		||||
			memset(hash_buffer + 32, 0, 32); /* yes, we have to drop half of the SHA512 hash :( */
 | 
			
		||||
			hash_buffer[0]  &= 0xF8U;
 | 
			
		||||
			hash_buffer[31] &= 0x3FU;
 | 
			
		||||
			hash_buffer[31] |= 0x40U;
 | 
			
		||||
			sc_reduce(hash_buffer);
 | 
			
		||||
 | 
			
		||||
			/* import the clamp data */
 | 
			
		||||
			ge_p3 p3_clamp_mul_pKey{};
 | 
			
		||||
			ge_p2 p2_clamp_mul_pKey{};
 | 
			
		||||
			ge_scalarmult_vartime(&p2_clamp_mul_pKey, hash_buffer, &e_pub_key);
 | 
			
		||||
			ge_p2_to_p3(&p3_clamp_mul_pKey, &p2_clamp_mul_pKey);
 | 
			
		||||
 | 
			
		||||
			/* add parent with the clamp data */
 | 
			
		||||
			ge_p1p1 a{};
 | 
			
		||||
			ge_add(&a, &p3_clamp_mul_pKey, &parent_cached);
 | 
			
		||||
 | 
			
		||||
			/* convert stuff back */
 | 
			
		||||
			ge_p3 r2{};
 | 
			
		||||
			ge_p1p1_to_p3(&r2, &a);
 | 
			
		||||
 | 
			
		||||
			parent_key = r2;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::array<uint8_t, 32> result{};
 | 
			
		||||
		ge_p3_tobytes((uint8_t*) result.data(), &parent_key);
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool License::push_entry(const std::shared_ptr<const HierarchyEntry> &entry, size_t* index) {
 | 
			
		||||
		assert(this->private_data);
 | 
			
		||||
 | 
			
		||||
		auto idx = this->_hierarchy.size();
 | 
			
		||||
		if(idx > 0 && !this->private_data->private_key_calculable(idx - 1))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		if(index)
 | 
			
		||||
			*index = idx;
 | 
			
		||||
		this->_hierarchy.push_back(entry);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typedef duration<int64_t, ratio<hours::period::num * 24, hours::period::den>>  days;
 | 
			
		||||
	typedef duration<int64_t, ratio<days::period::num * 365, days::period::den>>  years;
 | 
			
		||||
 | 
			
		||||
	bool License::hierarchy_timestamps_valid() {
 | 
			
		||||
		system_clock::time_point time_begin{};
 | 
			
		||||
		system_clock::time_point time_end = system_clock::time_point{} + years{5000};
 | 
			
		||||
 | 
			
		||||
		for(const auto& entry : this->_hierarchy) {
 | 
			
		||||
			auto end = entry->end_timestamp();
 | 
			
		||||
			auto begin = entry->begin_timestamp();
 | 
			
		||||
			if(begin < time_begin)
 | 
			
		||||
				return false;
 | 
			
		||||
			if(end > time_end)
 | 
			
		||||
				return false;
 | 
			
		||||
 | 
			
		||||
			time_begin = begin;
 | 
			
		||||
			if(end.time_since_epoch().count() != 0) time_end = end;
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void License::_register_raw_private_key(size_t index, uint8_t *data) {
 | 
			
		||||
		assert(this->private_data);
 | 
			
		||||
		this->private_data->register_raw_private_key(index, data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool License::generate_keypair(uint8_t *prv, uint8_t *pbl) {
 | 
			
		||||
		std::random_device rd;
 | 
			
		||||
		std::uniform_int_distribution<uint8_t> d;
 | 
			
		||||
 | 
			
		||||
		uint8_t root_seed[64];
 | 
			
		||||
		for(auto& e : root_seed)
 | 
			
		||||
			e = d(rd);
 | 
			
		||||
 | 
			
		||||
		ed25519_create_keypair(pbl, prv, root_seed);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::shared_ptr<LicensePrivate> LicensePrivate::create(const std::shared_ptr<license::v2::License> &handle, int key_index, const uint8_t *key) {
 | 
			
		||||
		auto result = shared_ptr<LicensePrivate>(new LicensePrivate{});
 | 
			
		||||
		result->_handle = handle;
 | 
			
		||||
		result->precalculated_private_key_index = key_index;
 | 
			
		||||
		if(key) {
 | 
			
		||||
			memcpy(result->precalculated_private_key.data(), key, result->precalculated_private_key.size());
 | 
			
		||||
		} else {
 | 
			
		||||
			assert(key_index < -1);
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::shared_ptr<LicensePrivate> LicensePrivate::read(const std::shared_ptr<License>& handle, uint8_t version, const uint8_t *buffer, size_t length, uint8_t& error) {
 | 
			
		||||
		if(version != 2) {
 | 
			
		||||
			error = 2; /* invalid version */
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto result = LicensePrivate::create(handle, -2, nullptr);
 | 
			
		||||
 | 
			
		||||
		size_t offset = 0;
 | 
			
		||||
		if((offset + 1) > length)
 | 
			
		||||
			return nullptr;
 | 
			
		||||
 | 
			
		||||
		/* read the precalculated private key */
 | 
			
		||||
		if(*(buffer + offset++)) {
 | 
			
		||||
			if((offset + 1 + result->precalculated_private_key.size()) > length)
 | 
			
		||||
				return nullptr;
 | 
			
		||||
 | 
			
		||||
			result->precalculated_private_key_index = *(buffer + offset++);
 | 
			
		||||
			memcpy(result->precalculated_private_key.data(), buffer + offset, result->precalculated_private_key.size());
 | 
			
		||||
			offset += result->precalculated_private_key.size();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* read raw private keys */
 | 
			
		||||
		{
 | 
			
		||||
			if((offset + 1) > length)
 | 
			
		||||
				return nullptr;
 | 
			
		||||
 | 
			
		||||
			auto private_key_count = *(buffer + offset++);
 | 
			
		||||
 | 
			
		||||
			if((offset + private_key_count * 33) > length)
 | 
			
		||||
				return nullptr;
 | 
			
		||||
			while(private_key_count-- > 0) {
 | 
			
		||||
				auto index = *(buffer + offset++);
 | 
			
		||||
				result->register_raw_private_key(index, buffer + offset);
 | 
			
		||||
				offset += 32;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* read the metadata */
 | 
			
		||||
		{
 | 
			
		||||
			if((offset + 4) > length)
 | 
			
		||||
				return nullptr;
 | 
			
		||||
 | 
			
		||||
			auto meta_data_length = *(uint32_t*) (buffer + offset);
 | 
			
		||||
			offset += 4;
 | 
			
		||||
 | 
			
		||||
			while(meta_data_length-- > 0) {
 | 
			
		||||
				if((offset + 3) > length)
 | 
			
		||||
					return nullptr;
 | 
			
		||||
				auto key_length = *(buffer + offset++);
 | 
			
		||||
				auto value_length = *(uint16_t*) (buffer + offset);
 | 
			
		||||
				offset += 2;
 | 
			
		||||
 | 
			
		||||
				if((offset + key_length + value_length) > length)
 | 
			
		||||
					return nullptr;
 | 
			
		||||
 | 
			
		||||
				result->meta_data[{(char*) buffer + offset, key_length}] = {(char*) buffer + offset + key_length, value_length};
 | 
			
		||||
				offset += key_length;
 | 
			
		||||
				offset += value_length;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool LicensePrivate::write(uint8_t *buffer, size_t &offset, size_t length, const LicensePrivateWriteOptions& options) {
 | 
			
		||||
		if(options.precalculated_key_index < -1) {
 | 
			
		||||
			if(buffer) {
 | 
			
		||||
				if((offset + 2) < length) return false;
 | 
			
		||||
				*(buffer + offset++) = 0; /* no precalculated private key */
 | 
			
		||||
				*(buffer + offset++) = 0; /* no raw private keys */
 | 
			
		||||
			} else {
 | 
			
		||||
				offset += 2;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			auto index = options.precalculated_key_index == -1 ? this->precalculated_private_key_index : options.precalculated_key_index;
 | 
			
		||||
			if(index < 0) return false; /* we will NEVER write the root key */
 | 
			
		||||
 | 
			
		||||
			if(buffer) {
 | 
			
		||||
				if((offset + 2 + 32) < length) return false;
 | 
			
		||||
				*(buffer + offset++) = 1;
 | 
			
		||||
				{
 | 
			
		||||
					*(buffer + offset++) = index;
 | 
			
		||||
 | 
			
		||||
					if(!this->calculate_private_key(buffer + offset, index))
 | 
			
		||||
						return false;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if((offset + 1) < length) return false;
 | 
			
		||||
				auto& private_key_count = *(buffer + offset++);
 | 
			
		||||
				private_key_count = 0;
 | 
			
		||||
				for(auto& [key_index, key] : this->private_keys) {
 | 
			
		||||
					if(key_index <= index)
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					if((offset + 1 + key.size()) < length) return false;
 | 
			
		||||
					*(buffer + offset++) = key_index;
 | 
			
		||||
					memcpy(buffer + offset, key.data(), key.size());
 | 
			
		||||
 | 
			
		||||
					private_key_count++;
 | 
			
		||||
					offset += key.size();
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				/* private precalc key */
 | 
			
		||||
				offset += 2 + 32;
 | 
			
		||||
 | 
			
		||||
				/* raw keys */
 | 
			
		||||
				offset += 1;
 | 
			
		||||
				for(auto& [key_index, key] : this->private_keys) {
 | 
			
		||||
					if(key_index <= index)
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					offset += 1 + key.size();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(buffer) {
 | 
			
		||||
			if((offset + 4) < length) return false;
 | 
			
		||||
			*(uint32_t*) (buffer + offset) = this->meta_data.size();
 | 
			
		||||
			offset += 4;
 | 
			
		||||
 | 
			
		||||
			for(auto& [key, value] : this->meta_data) {
 | 
			
		||||
				if((offset + 3 + key.length() + value.length()) < length) return false;
 | 
			
		||||
 | 
			
		||||
				*(buffer + offset++) = key.length();
 | 
			
		||||
				*(uint16_t*)(buffer + offset) = value.length();
 | 
			
		||||
				offset += 2;
 | 
			
		||||
 | 
			
		||||
				memcpy(buffer + offset, key.data(), key.length());
 | 
			
		||||
				offset += key.length();
 | 
			
		||||
 | 
			
		||||
				memcpy(buffer + offset, value.data(), value.length());
 | 
			
		||||
				offset += value.length();
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			offset += 4;
 | 
			
		||||
			for(auto& [key, value] : this->meta_data)
 | 
			
		||||
				offset += 3 + key.length() + value.length();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool LicensePrivate::private_key_chain_valid() {
 | 
			
		||||
		auto handle = this->_handle.lock();
 | 
			
		||||
		if(!handle) return false;
 | 
			
		||||
 | 
			
		||||
		auto hierarchy = handle->hierarchy();
 | 
			
		||||
 | 
			
		||||
		auto base_index = this->precalculated_private_key_index;
 | 
			
		||||
		if(base_index >= hierarchy.size()) return false;
 | 
			
		||||
		if(base_index < -1) return true; /* means we don't have a private key */
 | 
			
		||||
 | 
			
		||||
		while(base_index < hierarchy.size()) {
 | 
			
		||||
			if(!this->has_raw_private_key(base_index++))
 | 
			
		||||
				return false;
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool LicensePrivate::private_key_calculable(uint8_t index) const {
 | 
			
		||||
		auto handle = this->_handle.lock();
 | 
			
		||||
		if(!handle) return false;
 | 
			
		||||
 | 
			
		||||
		auto hierarchy = handle->hierarchy();
 | 
			
		||||
		if(index >= hierarchy.size()) return false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		auto base_index = this->precalculated_private_key_index;
 | 
			
		||||
		if(base_index > index) return false;
 | 
			
		||||
		if(base_index < -1) return false;
 | 
			
		||||
 | 
			
		||||
		while(base_index < index) {
 | 
			
		||||
			base_index++;
 | 
			
		||||
			if(this->private_keys.count(base_index) < 1)
 | 
			
		||||
				return false; /* we're missing a private key here, how is this even possible? */
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool LicensePrivate::calculate_private_key(uint8_t *buffer, uint8_t index) const {
 | 
			
		||||
		auto handle = this->_handle.lock();
 | 
			
		||||
		if(!handle) return false;
 | 
			
		||||
 | 
			
		||||
		auto hierarchy = handle->hierarchy();
 | 
			
		||||
		if(index >= hierarchy.size()) return false;
 | 
			
		||||
 | 
			
		||||
		auto base_index = this->precalculated_private_key_index;
 | 
			
		||||
		if(base_index > index) return false;
 | 
			
		||||
		if(base_index < -1) return false;
 | 
			
		||||
 | 
			
		||||
		uint8_t hash_buffer[64];
 | 
			
		||||
		memcpy(buffer, this->precalculated_private_key.data(), this->precalculated_private_key.size());
 | 
			
		||||
		while(base_index < index) {
 | 
			
		||||
			base_index++;
 | 
			
		||||
			if(this->private_keys.count(base_index) < 1)
 | 
			
		||||
				return false; /* we're missing a private key here, how is this even possible? */
 | 
			
		||||
 | 
			
		||||
			if(!hierarchy[index]->hash(hash_buffer)) return false;
 | 
			
		||||
 | 
			
		||||
			/* import hash (convert to a valid coordinate) */
 | 
			
		||||
			memset(hash_buffer + 32, 0, 32); /* yes, we have to drop half of the SHA512 hash :( */
 | 
			
		||||
			hash_buffer[0]  &= 0xF8U;
 | 
			
		||||
			hash_buffer[31] &= 0x3FU;
 | 
			
		||||
			hash_buffer[31] |= 0x40U;
 | 
			
		||||
			sc_reduce(hash_buffer);
 | 
			
		||||
 | 
			
		||||
			sc_muladd(buffer, this->private_keys.at(base_index).data(), hash_buffer, buffer);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void LicensePrivate::register_raw_private_key(uint8_t index, const uint8_t *buffer) {
 | 
			
		||||
		auto& target = this->private_keys[index];
 | 
			
		||||
		memcpy(target.data(), buffer, target.size());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool LicensePrivate::has_raw_private_key(uint8_t index) const {
 | 
			
		||||
		return this->private_keys.count(index) > 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HierarchyEntry::~HierarchyEntry() {
 | 
			
		||||
		this->allocate_read_body(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::shared_ptr<const HierarchyEntry> HierarchyEntry::read(const uint8_t *buffer, size_t &offset, size_t length) {
 | 
			
		||||
		auto result = shared_ptr<HierarchyEntry>(new HierarchyEntry{});
 | 
			
		||||
		if((offset + 43) > length) return nullptr;
 | 
			
		||||
 | 
			
		||||
		result->_entry_type = *(buffer + offset);
 | 
			
		||||
		offset++;
 | 
			
		||||
 | 
			
		||||
		memcpy(result->_public_key.data(), buffer + offset, 32);
 | 
			
		||||
		offset += 32;
 | 
			
		||||
 | 
			
		||||
		memcpy(&result->_timestamp_begin, buffer + offset, 4);
 | 
			
		||||
		offset += 4;
 | 
			
		||||
 | 
			
		||||
		memcpy(&result->_timestamp_end, buffer + offset, 4);
 | 
			
		||||
		offset += 4;
 | 
			
		||||
 | 
			
		||||
		uint16_t body_length;
 | 
			
		||||
		memcpy(&body_length, buffer + offset, 2);
 | 
			
		||||
		offset += 2;
 | 
			
		||||
 | 
			
		||||
		if(body_length > length) return nullptr;
 | 
			
		||||
		if(!result->allocate_read_body(body_length)) return nullptr;
 | 
			
		||||
 | 
			
		||||
		if(body_length > 0) {
 | 
			
		||||
			memcpy(result->read_body, buffer + offset, body_length);
 | 
			
		||||
			offset += body_length;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		result->_hash_set = false;
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HierarchyEntry::write(uint8_t *buffer, size_t &offset, size_t length) const {
 | 
			
		||||
		if(buffer && (offset + 43 + this->read_body_length) > length) return false;
 | 
			
		||||
 | 
			
		||||
		if(buffer) *(buffer + offset) = this->_entry_type;
 | 
			
		||||
		offset++;
 | 
			
		||||
 | 
			
		||||
		if(buffer) memcpy(buffer + offset, this->_public_key.data(), 32);
 | 
			
		||||
		offset += 32;
 | 
			
		||||
 | 
			
		||||
		if(buffer) memcpy(buffer + offset, &this->_timestamp_begin, 4);
 | 
			
		||||
		offset += 4;
 | 
			
		||||
 | 
			
		||||
		if(buffer) memcpy(buffer + offset, &this->_timestamp_end, 4);
 | 
			
		||||
		offset += 4;
 | 
			
		||||
 | 
			
		||||
		if(buffer) memcpy(buffer + offset, &this->read_body_length, 2);
 | 
			
		||||
		offset += 2;
 | 
			
		||||
 | 
			
		||||
		if(this->read_body_length > 0) {
 | 
			
		||||
			if(buffer) memcpy(buffer + offset, this->read_body, this->read_body_length);
 | 
			
		||||
			offset += this->read_body_length;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HierarchyEntry::allocate_read_body(size_t size) {
 | 
			
		||||
		if(this->read_body) {
 | 
			
		||||
			::free(this->read_body);
 | 
			
		||||
			this->read_body = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(size > 0) {
 | 
			
		||||
			this->read_body = (uint8_t*) malloc(size);
 | 
			
		||||
			if(!this->read_body) return false;
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HierarchyEntry::hash(uint8_t *target_buffer) const {
 | 
			
		||||
		if(this->_hash_set) {
 | 
			
		||||
			memcpy(target_buffer, this->_hash.data(), this->_hash.size());
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t length = 42 + this->read_body_length, offset = 0;
 | 
			
		||||
		auto buffer = (uint8_t*) malloc(length);
 | 
			
		||||
		if(!buffer) return false;
 | 
			
		||||
		if(this->write(buffer, offset, length)) {
 | 
			
		||||
			digest::sha512((char*) buffer, length, this->_hash.data());
 | 
			
		||||
			this->_hash_set = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return this->_hash_set ? this->hash(target_buffer) : false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace hierarchy {
 | 
			
		||||
		std::string_view Intermediate::description() {
 | 
			
		||||
			if(this->_length == 0)
 | 
			
		||||
				return {};
 | 
			
		||||
 | 
			
		||||
			return std::string_view{(const char*) this->_memory + 1, (size_t) *this->_memory};
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::shared_ptr<const HierarchyEntry> Intermediate::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end, const std::string &description) {
 | 
			
		||||
			assert(description.size() < 256);
 | 
			
		||||
			auto result = shared_ptr<HierarchyEntry>(new HierarchyEntry{hierarchy::Intermediate::type, pub_key, begin, end});
 | 
			
		||||
 | 
			
		||||
			if(!result || !result->allocate_read_body(description.size() + 1)) return nullptr;
 | 
			
		||||
			memcpy(result->read_body + 1, description.data(), description.length());
 | 
			
		||||
			*result->read_body = (uint8_t) description.length();
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static int test() {
 | 
			
		||||
		uint8_t errc;
 | 
			
		||||
		auto license = License::read(nullptr, 0, errc);
 | 
			
		||||
		license->push_entry<hierarchy::Intermediate>(system_clock::now(), system_clock::now(), "test!");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -32,46 +32,208 @@ namespace license {
 | 
			
		||||
        const char cryptKey[64]; //The dummy key for data de/encryption
 | 
			
		||||
    } __attribute__ ((__packed__));
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    namespace v2 {
 | 
			
		||||
    	namespace data {
 | 
			
		||||
    		struct ChainHead {
 | 
			
		||||
				uint32_t chain_version;
 | 
			
		||||
				uint32_t chain_magic;
 | 
			
		||||
				uint8_t sign[32];
 | 
			
		||||
    	struct LicenseHeader {
 | 
			
		||||
		    uint16_t version; /* first 16 bytes const version */
 | 
			
		||||
		    uint64_t crypt_key_seed; /* the seed */
 | 
			
		||||
		    uint8_t crypt_key_verify_offset; /*  the first 8 bits determine how much generations (n * 3) and the rest what value is expeted. Due to the loss of 8 bits does the highest 8 bits get xored with the lowerst and 56 bits get compared */
 | 
			
		||||
		    uint8_t crypt_key_verify[5];
 | 
			
		||||
    	} __attribute__ ((__packed__));
 | 
			
		||||
    	static_assert(sizeof(LicenseHeader) == 16);
 | 
			
		||||
 | 
			
		||||
    	struct BodyHeader {
 | 
			
		||||
			uint16_t length_private_data;
 | 
			
		||||
		    uint16_t length_hierarchy; /* contains all public data */
 | 
			
		||||
 | 
			
		||||
		    uint8_t checksum_hierarchy[20]; /* SHA1 */
 | 
			
		||||
		    uint8_t private_data_sign[64]; /* ed sign from the hierarchy created public key */
 | 
			
		||||
    	} __attribute__ ((__packed__));
 | 
			
		||||
 | 
			
		||||
    		struct ChainEntryHead {
 | 
			
		||||
                uint8_t entry_type; //sign bit = contains private
 | 
			
		||||
				int64_t entry_begin;
 | 
			
		||||
				int32_t entry_length;
 | 
			
		||||
                uint8_t key[32];
 | 
			
		||||
    		} __attribute__ ((__packed__));
 | 
			
		||||
    	}
 | 
			
		||||
    	extern std::array<uint8_t, 32> public_root_key;
 | 
			
		||||
 | 
			
		||||
    	struct LicenseIssuer {
 | 
			
		||||
    	    std::string name;
 | 
			
		||||
    	    std::string email;
 | 
			
		||||
    	};
 | 
			
		||||
 | 
			
		||||
    	struct LicenseChainEntry {
 | 
			
		||||
    	    data::ChainEntryHead head;
 | 
			
		||||
            struct {
 | 
			
		||||
                bool contains;
 | 
			
		||||
                uint8_t key[32];
 | 
			
		||||
            } prv_key;
 | 
			
		||||
            LicenseIssuer issuer;
 | 
			
		||||
    	};
 | 
			
		||||
 | 
			
		||||
        struct LicenseChain {
 | 
			
		||||
    	struct HierarchyEntry;
 | 
			
		||||
    	struct LicensePrivate;
 | 
			
		||||
    	struct LicensePrivateWriteOptions;
 | 
			
		||||
    	struct License {
 | 
			
		||||
    		public:
 | 
			
		||||
                data::ChainHead head;
 | 
			
		||||
                std::deque<LicenseChainEntry> entries;
 | 
			
		||||
    			static std::shared_ptr<License> read(const uint8_t* /* buffer */, size_t /* max size */, uint8_t& /* error */);
 | 
			
		||||
    			static std::shared_ptr<License> create(const std::vector<std::shared_ptr<const HierarchyEntry>>& hierarchy, const std::array<uint8_t, 32>& /* precalculated last entry */);
 | 
			
		||||
    			/* Note for the write method: Make the private version index configurable, so we could "export" the license */
 | 
			
		||||
    			/* Note for the write method: Write "private_buffer" if we're not able to resign the private data. As well enfore (assert) that we have a private buffer (e.g. by the read method) */
 | 
			
		||||
 | 
			
		||||
			    ~License();
 | 
			
		||||
			    const std::vector<std::shared_ptr<const HierarchyEntry>> hierarchy() const { return this->_hierarchy; }
 | 
			
		||||
				bool push_entry(const std::shared_ptr<const HierarchyEntry>& /* entry */, size_t* /* index */ = nullptr);
 | 
			
		||||
 | 
			
		||||
			    bool hierarchy_timestamps_valid();
 | 
			
		||||
 | 
			
		||||
			    template <typename I, typename... Args>
 | 
			
		||||
			    bool push_entry(Args&&... args) {
 | 
			
		||||
			    	std::array<uint8_t, 32> key_private{}, key_public{};
 | 
			
		||||
			    	if(!this->generate_keypair(key_private.data(), key_public.data())) return false;
 | 
			
		||||
 | 
			
		||||
			    	auto entry = I::create(key_public.data(), std::forward<Args>(args)...);
 | 
			
		||||
			    	if(!entry) return false;
 | 
			
		||||
 | 
			
		||||
			    	size_t index;
 | 
			
		||||
				    if(!this->push_entry(entry, &index)) return false;
 | 
			
		||||
 | 
			
		||||
			    	this->_register_raw_private_key(index, key_private.data());
 | 
			
		||||
			    	return true;
 | 
			
		||||
			    }
 | 
			
		||||
 | 
			
		||||
			    std::array<uint8_t, 32> generate_public_key(const uint8_t* /* public key root */, int /* length */ = -1) const;
 | 
			
		||||
 | 
			
		||||
			    std::string write(int& /* error */);
 | 
			
		||||
 | 
			
		||||
			    bool private_data_editable() const;
 | 
			
		||||
			    bool write_private_data(const LicensePrivateWriteOptions& /* write options */);
 | 
			
		||||
 | 
			
		||||
			    inline uint8_t version() const { return this->_version; }
 | 
			
		||||
		    private:
 | 
			
		||||
			    License() = default;
 | 
			
		||||
 | 
			
		||||
			    uint8_t _version = 0;
 | 
			
		||||
 | 
			
		||||
			    std::array<uint8_t, 64> private_buffer_sign{};
 | 
			
		||||
			    uint8_t* private_buffer = nullptr;
 | 
			
		||||
			    size_t private_buffer_length = 0;
 | 
			
		||||
			    std::shared_ptr<LicensePrivate> private_data{};
 | 
			
		||||
 | 
			
		||||
			    uint64_t crypt_seed = 0;
 | 
			
		||||
    	        std::vector<std::shared_ptr<const HierarchyEntry>> _hierarchy{};
 | 
			
		||||
 | 
			
		||||
    	        bool generate_keypair(uint8_t* /* private key */, uint8_t* /* public key */);
 | 
			
		||||
    	        void _register_raw_private_key(size_t /* index */, uint8_t* /* key */);
 | 
			
		||||
    	};
 | 
			
		||||
 | 
			
		||||
	    struct LicensePrivateWriteOptions {
 | 
			
		||||
		    int precalculated_key_index = -2; /* -2 => Do not write any private keys; -1 => Write everything */
 | 
			
		||||
	    };
 | 
			
		||||
 | 
			
		||||
    	struct LicensePrivate {
 | 
			
		||||
		    public:
 | 
			
		||||
    			static std::shared_ptr<LicensePrivate> read(const std::shared_ptr<License>& /* handle */, uint8_t /* version */, const uint8_t* /* buffer */, size_t /* length */, uint8_t& /* error */);
 | 
			
		||||
			    static std::shared_ptr<LicensePrivate> create(const std::shared_ptr<License>& /* handle */, int /* precalculated private key index */, const uint8_t* /* precalculated key */);
 | 
			
		||||
 | 
			
		||||
			    bool private_key_chain_valid();
 | 
			
		||||
 | 
			
		||||
			    bool has_meta(const std::string& key) const { return this->meta_data.count(key) > 0; }
 | 
			
		||||
			    std::string get_meta(const std::string& key) const { return this->meta_data.at(key); }
 | 
			
		||||
			    void set_meta(const std::string& key, const std::string& value) { this->meta_data[key] = value; }
 | 
			
		||||
 | 
			
		||||
			    /* if target is null just increase the offset! */
 | 
			
		||||
			    bool write(uint8_t* /* target */, size_t& /* offset */, size_t /* length */, const LicensePrivateWriteOptions& /* options */);
 | 
			
		||||
 | 
			
		||||
			    void register_raw_private_key(uint8_t /* index */, const uint8_t* /* key */);
 | 
			
		||||
    			bool has_raw_private_key(uint8_t /* index */) const;
 | 
			
		||||
 | 
			
		||||
    			bool private_key_calculable(uint8_t /* index */) const;
 | 
			
		||||
    			bool calculate_private_key(uint8_t* /* response */, uint8_t /* index */) const;
 | 
			
		||||
		    private:
 | 
			
		||||
    			std::weak_ptr<License> _handle;
 | 
			
		||||
			    LicensePrivate() = default;
 | 
			
		||||
 | 
			
		||||
			    int precalculated_private_key_index = -2; /* -2 means not set, -1 means root key */
 | 
			
		||||
			    std::array<uint8_t, 32> precalculated_private_key{};
 | 
			
		||||
			    std::map<uint8_t, std::array<uint8_t, 32>> private_keys{};
 | 
			
		||||
			    std::map<std::string, std::string> meta_data{};
 | 
			
		||||
    	};
 | 
			
		||||
 | 
			
		||||
    	namespace hierarchy {
 | 
			
		||||
    		struct Intermediate;
 | 
			
		||||
    	}
 | 
			
		||||
 | 
			
		||||
    	struct HierarchyEntry {
 | 
			
		||||
    		friend struct hierarchy::Intermediate;
 | 
			
		||||
    		public:
 | 
			
		||||
    			~HierarchyEntry();
 | 
			
		||||
 | 
			
		||||
				static std::shared_ptr<const HierarchyEntry> read(const uint8_t* /* buffer */, size_t& /* offset */, size_t /* max size */);
 | 
			
		||||
			    bool write(uint8_t* /* buffer */, size_t& /* offset */, size_t /* max size */) const;
 | 
			
		||||
			    inline size_t write_length() const { return 43 + this->read_body_length; }
 | 
			
		||||
 | 
			
		||||
			    inline uint8_t entry_type() const { return this->_entry_type; }
 | 
			
		||||
 | 
			
		||||
			    template <typename T = std::chrono::system_clock>
 | 
			
		||||
			    inline typename T::time_point begin_timestamp() const { return typename T::time_point{} + std::chrono::minutes(this->_timestamp_begin); }
 | 
			
		||||
 | 
			
		||||
			    template <typename T = std::chrono::system_clock>
 | 
			
		||||
			    inline typename T::time_point end_timestamp() const { return typename T::time_point{} + std::chrono::minutes(this->_timestamp_end); }
 | 
			
		||||
 | 
			
		||||
			    inline const std::array<uint8_t, 32>& public_key() const { return this->_public_key; }
 | 
			
		||||
			    inline std::array<uint8_t, 32>& public_key() {
 | 
			
		||||
				    this->_hash_set = false;
 | 
			
		||||
			    	return this->_public_key;
 | 
			
		||||
			    }
 | 
			
		||||
 | 
			
		||||
			    inline const uint8_t* body() const { return this->read_body; }
 | 
			
		||||
			    inline const size_t body_length() const { return this->read_body_length; }
 | 
			
		||||
 | 
			
		||||
			    template <typename I>
 | 
			
		||||
			    inline I interpret_as() const {
 | 
			
		||||
				    assert(this->interpret_as<I>());
 | 
			
		||||
				    return I{this->read_body, this->read_body_length};
 | 
			
		||||
			    }
 | 
			
		||||
 | 
			
		||||
			    template <typename I>
 | 
			
		||||
			    inline bool interpretable_as() const { return I::type == this->_entry_type; }
 | 
			
		||||
 | 
			
		||||
			    inline bool hash(uint8_t* /* hash result [64] */) const;
 | 
			
		||||
		    protected:
 | 
			
		||||
			    template <typename T = std::chrono::system_clock>
 | 
			
		||||
			    HierarchyEntry(uint8_t type, const uint8_t* public_key, const typename T::time_point& begin, const typename T::time_point& end) {
 | 
			
		||||
			    	this->_entry_type = type;
 | 
			
		||||
			    	memcpy(this->_public_key.data(), public_key, this->_public_key.size());
 | 
			
		||||
				    this->_timestamp_begin = std::chrono::floor<std::chrono::minutes>(begin.time_since_epoch()).count();
 | 
			
		||||
				    this->_timestamp_end = std::chrono::floor<std::chrono::minutes>(end.time_since_epoch()).count();
 | 
			
		||||
			    }
 | 
			
		||||
		    private:
 | 
			
		||||
			    HierarchyEntry() = default;
 | 
			
		||||
 | 
			
		||||
			    mutable std::array<uint8_t, 64> _hash{};
 | 
			
		||||
			    mutable bool _hash_set = false;
 | 
			
		||||
 | 
			
		||||
			    uint8_t _entry_type = 0;
 | 
			
		||||
    			std::array<uint8_t, 32> _public_key{};
 | 
			
		||||
 | 
			
		||||
    			uint32_t _timestamp_begin = 0; /* Minutes since epoch! */
 | 
			
		||||
			    uint32_t _timestamp_end = 0; /* Minutes since epoch! */
 | 
			
		||||
 | 
			
		||||
			    uint8_t* read_body = nullptr;
 | 
			
		||||
			    size_t read_body_length = 0;
 | 
			
		||||
 | 
			
		||||
			    bool allocate_read_body(size_t);
 | 
			
		||||
    	};
 | 
			
		||||
 | 
			
		||||
    	namespace hierarchy {
 | 
			
		||||
    		struct BodyInterpreter {
 | 
			
		||||
			    public:
 | 
			
		||||
				    BodyInterpreter() = delete;
 | 
			
		||||
 | 
			
		||||
    			protected:
 | 
			
		||||
				    BodyInterpreter(const uint8_t* memory, size_t length) { this->_memory = memory; this->_length = length; }
 | 
			
		||||
				    const uint8_t* _memory = nullptr;
 | 
			
		||||
				    size_t _length = 0;
 | 
			
		||||
    		};
 | 
			
		||||
 | 
			
		||||
    		struct Intermediate : public BodyInterpreter {
 | 
			
		||||
				    friend struct HierarchyEntry;
 | 
			
		||||
    			public:
 | 
			
		||||
    				static constexpr uint8_t type = 1;
 | 
			
		||||
				    static std::shared_ptr<const HierarchyEntry> create(
 | 
			
		||||
						    const uint8_t* /* public key */,
 | 
			
		||||
						    const std::chrono::system_clock::time_point& /* begin */,
 | 
			
		||||
						    const std::chrono::system_clock::time_point& /* end */,
 | 
			
		||||
						    const std::string& /* description */
 | 
			
		||||
				    );
 | 
			
		||||
 | 
			
		||||
					std::string_view description();
 | 
			
		||||
    			private:
 | 
			
		||||
				    Intermediate(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {}
 | 
			
		||||
    		};
 | 
			
		||||
    	}
 | 
			
		||||
    */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum LicenseType : uint8_t {
 | 
			
		||||
        INVALID,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										114
									
								
								server/dependency_resolver.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										114
									
								
								server/dependency_resolver.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,114 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
#WARNING: Any spaces within the path will cause trouble!
 | 
			
		||||
 | 
			
		||||
#ldd -d
 | 
			
		||||
if [[ ! -f "$1" ]]; then
 | 
			
		||||
    echo "Missing target file ($1)"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# This is a multidimensional array
 | 
			
		||||
# key /* library file */ => (library name; library file; dependencies ())
 | 
			
		||||
declare -A collected_libraries
 | 
			
		||||
 | 
			
		||||
# This is a one dimensional array
 | 
			
		||||
# key /* library file */ => use count
 | 
			
		||||
declare -A collected_libraries_use_count
 | 
			
		||||
 | 
			
		||||
declare -A libraries_of_result
 | 
			
		||||
function libraries_of {
 | 
			
		||||
    local buffer
 | 
			
		||||
    local index
 | 
			
		||||
    local data
 | 
			
		||||
 | 
			
		||||
    buffer=$(ldd -d "$1")
 | 
			
		||||
    index=0
 | 
			
		||||
 | 
			
		||||
    libraries_of_result=()
 | 
			
		||||
 | 
			
		||||
    IFS=$'\n'
 | 
			
		||||
    for line in ${buffer}; do
 | 
			
		||||
        index=$(($index + 1))
 | 
			
		||||
        [[ ${index} == 1 ]] && continue
 | 
			
		||||
        IFS=$' ' data=(${line})
 | 
			
		||||
 | 
			
		||||
        # We trim the leading and tailing white spaces
 | 
			
		||||
        _key=$(echo "${data[0]}" | sed -e 's/^[[:space:]]*//')
 | 
			
		||||
        _value=$(echo "${data[2]}" | sed -e 's/^[[:space:]]*//')
 | 
			
		||||
        libraries_of_result["${_key}"]="${_value}"
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    [[ $? -ne 0 ]] && return 1
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function print_lookup_stack {
 | 
			
		||||
    index_max=${#@}
 | 
			
		||||
    index_args="$@"
 | 
			
		||||
 | 
			
		||||
    #echo -n -e "\r\033[K"
 | 
			
		||||
    echo ""
 | 
			
		||||
    for (( index = 0; index < $index_max; ++index )); do
 | 
			
		||||
        arg=$(eval echo \$$((${index} + 1)))
 | 
			
		||||
        echo -n $(basename ${arg})
 | 
			
		||||
        [[ $(($index + 1)) -lt ${index_max} ]] && echo -n " => "
 | 
			
		||||
    done
 | 
			
		||||
    #sleep 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare -a libraries_of_deep_stack
 | 
			
		||||
function libraries_of_deep {
 | 
			
		||||
    local IFS
 | 
			
		||||
    local valid_libraries
 | 
			
		||||
    local inner_array
 | 
			
		||||
    local result_array
 | 
			
		||||
 | 
			
		||||
    libraries_of $1
 | 
			
		||||
 | 
			
		||||
    valid_libraries=()
 | 
			
		||||
    #echo "Gathered libraries for $1:"
 | 
			
		||||
    for library_name in "${!libraries_of_result[@]}"; do
 | 
			
		||||
        [[ -z "${libraries_of_result[$library_name]}" ]] && {
 | 
			
		||||
            #echo "  The dependency $library_name for $1 could not be resolved"
 | 
			
		||||
            continue
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #echo "  $library_name at ${libraries_of_result[$library_name]}";
 | 
			
		||||
        valid_libraries+=("${libraries_of_result[$library_name]}")
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    IFS=$';' inner_array="${valid_libraries[*]}"
 | 
			
		||||
    IFS=$' ' result_array=("$(basename $1)" "$1" "$inner_array")
 | 
			
		||||
    collected_libraries[$1]="${result_array[@]}"
 | 
			
		||||
 | 
			
		||||
    libraries_of_deep_stack+=($1)
 | 
			
		||||
    print_lookup_stack ${libraries_of_deep_stack[@]}
 | 
			
		||||
 | 
			
		||||
    for library_path in "${valid_libraries[@]}"; do
 | 
			
		||||
        # echo "Looking up library path $library_path"
 | 
			
		||||
        [[ ! -z "${collected_libraries[$library_path]}" ]] && {
 | 
			
		||||
            #echo "Library $library_path already resolved"
 | 
			
		||||
            collected_libraries_use_count[$library_path]=$((${collected_libraries_use_count[$library_path]} + 1))
 | 
			
		||||
            continue
 | 
			
		||||
        }
 | 
			
		||||
        #echo "Resolving libraries for path $library_path"
 | 
			
		||||
        collected_libraries_use_count[$library_path]=1
 | 
			
		||||
        libraries_of_deep ${library_path}
 | 
			
		||||
        #library_name
 | 
			
		||||
    done
 | 
			
		||||
    unset 'libraries_of_deep_stack[${#libraries_of_deep_stack[@]}-1]';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
libraries_of_deep $1
 | 
			
		||||
echo -e -n "\r\033[K" #Clear the stack
 | 
			
		||||
 | 
			
		||||
for key in "${!collected_libraries[@]}"; do
 | 
			
		||||
    IFS=$' ' library_data=(${collected_libraries[$key]})
 | 
			
		||||
    IFS=$';' libraries=(${library_data[2]})
 | 
			
		||||
 | 
			
		||||
    echo "Got library ${library_data[0]} (${library_data[1]}) directly used ${collected_libraries_use_count[$key]} times:"
 | 
			
		||||
    for library in "${libraries[@]}"; do
 | 
			
		||||
        echo "  $library"
 | 
			
		||||
    done
 | 
			
		||||
done
 | 
			
		||||
@ -14,6 +14,7 @@ ServerManager::ServerManager(InstanceHandler* handle) : handle(handle) {
 | 
			
		||||
    this->puzzles = new protocol::PuzzleManager();
 | 
			
		||||
	this->handshakeTickers = new threads::Scheduler(1, "handshake ticker");
 | 
			
		||||
    this->execute_loop = new event::EventExecutor("executor #");
 | 
			
		||||
	//this->join_loop = new event::EventExecutor("joiner #");
 | 
			
		||||
    this->_ioManager = new io::VoiceIOManager();
 | 
			
		||||
 | 
			
		||||
	this->handshakeTickers->schedule("ticker", [&](){ this->tickHandshakeClients(); }, seconds(1));
 | 
			
		||||
@ -41,6 +42,11 @@ ServerManager::~ServerManager() {
 | 
			
		||||
    delete this->execute_loop;
 | 
			
		||||
    this->execute_loop = nullptr;
 | 
			
		||||
 | 
			
		||||
    if(this->join_loop)
 | 
			
		||||
    	this->join_loop->shutdown();
 | 
			
		||||
    delete this->join_loop;
 | 
			
		||||
    this->join_loop = nullptr;
 | 
			
		||||
 | 
			
		||||
    if(this->handshakeTickers) {
 | 
			
		||||
        this->handshakeTickers->shutdown();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,7 @@ namespace ts {
 | 
			
		||||
 | 
			
		||||
                protocol::PuzzleManager* rsaPuzzles() { return this->puzzles; }
 | 
			
		||||
 | 
			
		||||
		        event::EventExecutor* get_join_loop() { return this->join_loop; }
 | 
			
		||||
                event::EventExecutor* get_executor_loop() { return this->execute_loop; }
 | 
			
		||||
 | 
			
		||||
                inline void adjust_executor_threads() {
 | 
			
		||||
@ -84,6 +85,7 @@ namespace ts {
 | 
			
		||||
                protocol::PuzzleManager* puzzles = nullptr;
 | 
			
		||||
 | 
			
		||||
                event::EventExecutor* execute_loop = nullptr;
 | 
			
		||||
		        event::EventExecutor* join_loop = nullptr;
 | 
			
		||||
		        threads::Scheduler* handshakeTickers = nullptr;
 | 
			
		||||
                io::VoiceIOManager* _ioManager = nullptr;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7583,6 +7583,7 @@ CommandResult ConnectedClient::handleCommandDummy_IpChange(ts::Command &cmd) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp();
 | 
			
		||||
	return CommandResult::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								shared
									
									
									
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								shared
									
									
									
									
									
								
							@ -1 +1 @@
 | 
			
		||||
Subproject commit 4a9d8f132fe9ca3411e2e3c245922ad778c56aa0
 | 
			
		||||
Subproject commit 837852114f92d0f3acb94b5e0807dee265bf40e5
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user