first prototype

This commit is contained in:
Peter Sykora 2021-08-29 23:21:55 +02:00
parent d2051dcb28
commit e36f85cc44
21 changed files with 1905 additions and 185 deletions

103
CMakePresets.json Normal file
View File

@ -0,0 +1,103 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "linux-gcc-debug",
"displayName": "Linux GCC Debug",
"description": "Sets Ninja generator, compilers, build and install directory, debug build type",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "c++"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": [ "Linux" ]
},
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": {
"sourceDir": "$env{HOME}/.vs/$ms{projectDirName}"
}
}
},
{
"name": "linux-gcc-release",
"displayName": "Linux GCC Release",
"description": "Sets Ninja generator, compilers, build and install directory, debug build type",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "c++"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": [ "Linux" ]
},
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": {
"sourceDir": "$env{HOME}/.vs/$ms{projectDirName}"
}
}
},
{
"name": "WSL-gcc-debug",
"displayName": "Linux Debug",
"description": "Target the Windows Subsystem for Linux (WSL) or a remote Linux system.",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Linux" ] },
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" }
}
},
{
"name": "win64-debug",
"displayName": "Windows x64 Debug",
"description": "Target Windows with the Visual Studio development environment.",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
"CMAKE_SYSTEM_VERSION": "8.1"
},
"vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } }
}
],
"buildPresets": [
{
"name": "linux-gcc-debug",
"configurePreset": "linux-gcc-debug"
},
{
"name": "linux-gcc-release",
"configurePreset": "linux-gcc-release"
},
{
"name": "win64-debug",
"configurePreset": "win64-debug"
}
],
"vendor": {
"example.com/ExampleIDE/1.0": {
"autoFormat": false
}
}
}

View File

@ -22,8 +22,15 @@ conan_cmake_run(CONANFILE conanfile.txt
BUILD missing
BASIC_SETUP)
if(UNIX)
set_source_files_properties(OSUtils_win.cpp PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties(SystemParamsProvider_win.cpp PROPERTIES HEADER_FILE_ONLY TRUE)
else(WIN32)
set_source_files_properties(SystemParamsProvider_linux.cpp PROPERTIES HEADER_FILE_ONLY TRUE)
endif()
# Add source to this project's executable.
add_executable (CMakeProject1 "CCServer.cpp" "CCServer.h" "CryptoUtils.cpp" "CryptoUtils.h" "CMakeProject1.cpp" "CMakeProject1.h")
add_executable (CMakeProject1 "CCServer.cpp" "CCServer.h" "CryptoUtils.cpp" "CryptoUtils.h" "CMakeProject1.cpp" "CMakeProject1.h" "HashUtils.cpp" "HashUtils.h" "JSONSerialization.cpp" "JSONSerialization.h" "LicenseClient.cpp" "LicenseClient.h" "SystemParams.cpp" "SystemParams.h" "SystemParamsProvider.h" "SystemParamsProvider_win.cpp" "SystemParamsProvider_win.h" "SystemParamsProvider_linux.cpp" "SystemParamsProvider_win.cpp" "OSUtils.h" "OSUtils_win.cpp")
target_link_libraries(CMakeProject1 ${CONAN_LIBS})

View File

@ -1,5 +1,8 @@
#include "CCServer.h"
#include "LicenseClient.h"
#include "SystemParamsProvider.h"
#include <boost/program_options.hpp>
#include <boost/asio/signal_set.hpp>
#include <iostream>
@ -39,6 +42,16 @@ int main(int argc, const char* argv[])
return 1;
}
SystemParamsProvider systemParamsProvider;
LicenseClient licenseClient(systemParamsProvider, "license.dat");
licenseClient.init();
if (!licenseClient.isActivated()) {
std::cerr << "Application is not activated. Please contact Catalogue of Currencies support. Exiting..." << std::endl;
std::cout << "Activation request: " << licenseClient.buildActivationRequest() << std::endl;
return -2;
}
if (vm.count("port")) {
std::cout << "Port "
<< vm["port"].as<uint16_t>() << ".\n";

View File

@ -29,14 +29,15 @@ std::string base64Decode(const std::string& encoded)
return decoded;
}
std::string base64Encode(const std::string& source)
std::string base64Encode(const std::string& source, std::optional<int> maxLineLength)
{
std::string encoded;
StringSource ss(source, true,
new Base64Encoder(
new StringSink(encoded),
false /* no line breaks */
maxLineLength.has_value() /* no line breaks */,
maxLineLength.value_or(64)
)
);

View File

@ -1,10 +1,11 @@
#pragma once
#include <string>
#include <memory>
#include <optional>
#include <string>
std::string base64Decode(const std::string& encoded);
std::string base64Encode(const std::string& source);
std::string base64Encode(const std::string& source, std::optional<int> maxLineLength = {});
std::unique_ptr<uint8_t[]> base64Encode(const uint8_t * source, size_t sourceSize);
std::string base32Decode(const std::string& encoded);
std::string base32Encode(const std::string& data);

View File

@ -0,0 +1,44 @@
#include "HashUtils.h"
#include <cryptopp/sha.h>
#include <cryptopp/files.h>
#include <cryptopp/hex.h>
using CryptoPP::SHA256;
using CryptoPP::FileSource;
using CryptoPP::HashFilter;
using CryptoPP::HexEncoder;
using CryptoPP::StringSink;
namespace fs = boost::filesystem;
std::string calcSHA256(const fs::path& file)
{
SHA256 hash;
std::string digest;
FileSource f(
file.native().c_str(),
true,
new HashFilter(
hash,
new HexEncoder(
new StringSink(
digest))));
return digest;
}
uint64_t fletcher64(uint32_t *data, int count)
{
uint64_t sum1 = 0;
uint64_t sum2 = 0;
int index;
for (index = 0; index < count; ++index)
{
sum1 = (sum1 + data[index]) % std::numeric_limits<uint32_t>::max();
sum2 = (sum2 + sum1) % std::numeric_limits<uint32_t>::max();
}
return (sum2 << std::numeric_limits<uint32_t>::digits) | sum1;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include <boost/filesystem.hpp>
#include <string>
std::string calcSHA256(const boost::filesystem::path& file);
uint64_t fletcher64(uint32_t * data, int count);

View File

@ -0,0 +1,15 @@
#include "JSONSerialization.h"
namespace pt = boost::property_tree;
void serialize(std::ostream & os, const std::string & str)
{
os << '"'
<< str
<< '"';
}
void deserialize(const pt::ptree & tree, std::string & stringVal)
{
stringVal = tree.get_value<std::string>();
}

View File

@ -0,0 +1,118 @@
#pragma once
#include <boost/property_tree/ptree.hpp>
#include <map>
#include <ostream>
#include <set>
#include <string>
#include <vector>
void serialize(std::ostream& os, const std::string& str);
template<class T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void serialize(std::ostream& os, T intVal)
{
os << intVal;
}
template <typename T>
void serialize(std::ostream& os, const std::vector<T>& vec)
{
os << "[";
if (vec.size() > 0)
{
serialize(os, vec[0]);
std::for_each(std::begin(vec) + 1, std::end(vec), [&os](const auto& el)
{
os << ',';
serialize(os, el);
});
}
os << "]";
}
template <typename T>
void serialize(std::ostream& os, const std::set<T>& setVal)
{
bool first = true;
os << "[";
std::for_each(std::begin(setVal), std::end(setVal), [&os, &first](const auto& el)
{
if (!first)
{
os << ',';
}
serialize(os, el);
first = false;
});
os << "]";
}
template <typename T>
void serialize(std::ostream& os, const std::map<std::string, T>& m)
{
bool first = true;
os << "{";
std::for_each(std::begin(m), std::end(m), [&os, &first](const auto& pair)
{
if (!first)
{
os << ',';
}
serialize(os, pair.first);
os << ':';
serialize(os, pair.second);
first = false;
});
os << "}";
}
void deserialize(const boost::property_tree::ptree& tree, std::string& stringVal);
template<class T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void deserialize(const boost::property_tree::ptree & tree, T& intVal)
{
intVal = tree.get_value<T>();
}
template <typename T>
void deserialize(const boost::property_tree::ptree& tree, std::vector<T>& vec)
{
vec.clear();
for (const boost::property_tree::ptree::value_type &el : tree)
{
// el.first contain the string "" if it was JSON array
T val;
deserialize(el.second, val);
vec.push_back(std::move(val));
}
}
template <typename T>
void deserialize(const boost::property_tree::ptree& tree, std::set<T>& setVal)
{
setVal.clear();
for (const boost::property_tree::ptree::value_type &el : tree)
{
// el.first contain the string "" if it was JSON array
T val;
deserialize(el.second, val);
setVal.insert(std::move(val));
}
}
template <typename T>
void deserialize(const boost::property_tree::ptree& tree, std::map<std::string, T>& mapVal)
{
mapVal.clear();
for (const boost::property_tree::ptree::value_type &el : tree)
{
std::string key;
T val;
key = el.first;
deserialize(el.second, val);
mapVal.insert(std::make_pair(std::move(key), std::move(val)));
}
}

View File

@ -0,0 +1,533 @@
#include "LicenseClient.h"
#include "CryptoUtils.h"
#include "HashUtils.h"
#include "JSONSerialization.h"
#include <cryptopp/cryptlib.h>
#include <cryptopp/aes.h>
#include <cryptopp/gcm.h>
#include <cryptopp/osrng.h>
#include <cryptopp/eccrypto.h>
#include <cryptopp/oids.h>
#include <cryptopp/files.h>
#include <cryptopp/hex.h>
#include <cryptopp/dsa.h>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <streambuf>
#include <string>
#include <vector>
namespace pt = boost::property_tree;
namespace fs = boost::filesystem;
using CryptoPP::AES;
using CryptoPP::GCM;
using CryptoPP::byte;
using CryptoPP::SecByteBlock;
using CryptoPP::AutoSeededRandomPool;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::FileSource;
using CryptoPP::Redirector;
using CryptoPP::ECDSA;
using CryptoPP::EC2N;
using CryptoPP::SHA256;
using CryptoPP::HexDecoder;
namespace ASN1 = CryptoPP::ASN1;
namespace
{
static const std::string productId = "coc";
static const uint32_t initializationVectorSize = AES::BLOCKSIZE;
static const uint32_t macTagSize = 16;
static const uint32_t ecdsaSignatureSize = 72;
template< typename T >
std::string intToHex(T i)
{
std::stringstream stream;
stream
<< std::setfill('0') << std::setw(sizeof(T) * 2)
<< std::hex << i;
return stream.str();
}
typedef std::string ParamHash;
ParamHash fletcher64(const std::string& input)
{
std::vector<uint32_t> buf((input.size() + sizeof(uint32_t) - 1) / sizeof(uint32_t), 0);
std::memcpy((std::string::pointer)buf.data(), input.data(), input.size());
auto resInt = ::fletcher64(buf.data(), static_cast<int>(buf.size()));
return intToHex(resInt);
}
int countUniqueCharacters(std::string_view str)
{
std::set<char> chars;
for (auto ch : str)
{
chars.insert(ch);
}
return static_cast<int>(chars.size());
}
std::string filterPunctuation(std::string input)
{
boost::remove_erase_if(input, boost::is_any_of(". :;-_/"));
return input;
}
SystemParams skipEmptyParams(const SystemParams& systemParams)
{
SystemParams result;
for (const auto& entry : systemParams)
{
if (!entry.second.empty() && entry.second.length() > 1 && countUniqueCharacters(filterPunctuation(entry.second)) > 1)
{
result[entry.first] = entry.second;
}
}
return result;
}
SystemParams hashParams(const SystemParams& systemParams)
{
SystemParams result;
for (const auto& entry : systemParams)
{
result[entry.first] = fletcher64(entry.second);
}
return result;
}
typedef std::string Signature;
struct SignedData
{
std::string data;
std::string signature;
};
std::string readBinaryFile(const std::string& filename)
{
std::ifstream t(filename, std::istream::binary);
std::string str;
t.seekg(0, std::ios::end);
str.reserve(static_cast<size_t>(t.tellg()));
t.seekg(0, std::ios::beg);
str.assign((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
return str;
}
struct PreactivationRequest
{
std::string productId = productId;
SystemParams systemParams;
};
struct ActivationRequest
{
std::string productId = productId;
SystemParams systemParams;
std::string licenseNumber;
};
struct ActivationResponse
{
bool success;
std::optional<std::string> licenseFile;
};
} // anonymous namespace
void serialize(std::ostream& os, const ActivationData& a)
{
os << "{";
os << "\"activationId\":";
serialize(os, a.activationId);
os << ",\"productId\":";
serialize(os, a.productId);
os << ",\"systemParams\":";
serialize(os, a.systemParams);
os << ",\"licensedModules\":";
serialize(os, a.licensedModules);
os << "}";
}
void deserialize(const pt::ptree& tree, ActivationData& a)
{
deserialize(tree.get_child("activationId"), a.activationId);
deserialize(tree.get_child("productId"), a.productId);
deserialize(tree.get_child("systemParams"), a.systemParams);
deserialize(tree.get_child("licensedModules"), a.licensedModules);
}
void serialize(std::ostream& os, const SignedData& d)
{
os << "{";
os << "\"data\":";
serialize(os, d.data);
os << ",\"signature\":";
serialize(os, d.signature);
os << "}";
}
void deserialize(const pt::ptree& tree, SignedData& d)
{
deserialize(tree.get_child("data"), d.data);
deserialize(tree.get_child("signature"), d.signature);
}
void serialize(std::ostream& os, const PreactivationRequest& a)
{
os << "{";
os << "\"productId\":";
::serialize(os, a.productId);
os << ",\"systemParams\":";
::serialize(os, a.systemParams);
os << "}";
}
void serialize(std::ostream& os, const ActivationRequest& a)
{
os << "{";
os << "\"productId\":";
::serialize(os, a.productId);
os << ",\"systemParams\":";
::serialize(os, a.systemParams);
os << ",\"licenseNumber\":";
::serialize(os, a.licenseNumber);
os << "}";
}
void deserialize(const pt::ptree& tree, ActivationResponse& a)
{
deserialize(tree.get_child("success"), a.success);
auto licenseFileOpt = tree.get_child_optional("licenseFile");
if (licenseFileOpt)
{
std::string res;
deserialize(licenseFileOpt.value(), res);
a.licenseFile = base64Decode(res);
}
else
{
a.licenseFile = {};
}
}
std::optional<std::string> validateLicenseKey(const std::string& licenseKey)
{
auto result = base32Decode(licenseKey);
if (result.size() != 15)
return {};
return base32Encode(result);
}
LicenseClient::LicenseClient(SystemParamsProvider& systemParamsProvider, const std::string& licenseFile)
: m_systemParamsProvider(systemParamsProvider)
, m_licenseFile(licenseFile)
{
}
LicenseClient::~LicenseClient()
{
}
void LicenseClient::init()
{
const auto systemParams = skipEmptyParams(m_systemParamsProvider.retrieveSystemParams());
std::cout << "Collected params: " << std::endl;
for (const auto& entry : systemParams)
{
std::cout << "\t" << entry.first << " = " << entry.second << " ~= " << fletcher64(entry.second) << std::endl;
}
m_systemParams = hashParams(systemParams);
loadActivationData();
}
std::string LicenseClient::buildActivationRequest()
{
PreactivationRequest req{ productId, m_systemParams };
std::string jsonReq;
{
std::ostringstream ss1;
serialize(ss1, req);
jsonReq = ss1.str();
}
boost::iostreams::array_source src{ jsonReq.data(), jsonReq.size() };
boost::iostreams::filtering_istream is;
boost::iostreams::zlib_params zlibParams;
zlibParams.noheader = true;
is.push(boost::iostreams::zlib_compressor{ zlibParams });
is.push(src);
std::string out;
AutoSeededRandomPool prng;
unsigned char password[] = {
0xe7, 0x3d, 0xb5, 0x72, 0x34, 0x90, 0x05, 0xf1, 0xc4, 0x19, 0x79, 0xba, 0xf8, 0x16, 0x6a, 0x09,
0x00, 0x74, 0x51, 0x19, 0xfa, 0x09, 0x6b, 0x9c, 0x3e, 0xfb, 0xce, 0xe1, 0x1d, 0xdd, 0x8b, 0x88 };
SecByteBlock key(sizeof(password));
key.Assign(password, sizeof(password));
CryptoPP::byte iv[initializationVectorSize] = { 0 };
prng.GenerateBlock(iv, sizeof(iv));
GCM<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), iv, initializationVectorSize);
std::string cipher;
std::string compressed(std::istreambuf_iterator<char>(is), {});
StringSource stringSource(compressed, true,
new AuthenticatedEncryptionFilter(e,
new StringSink(cipher), false, macTagSize
) // AuthenticatedEncryptionFilter
); // StringSource
return std::string("-----BEGIN ACTIVATION REQUEST-----\n") +
base64Encode(cipher, std::make_optional(64)) +
std::string("-----END ACTIVATION REQUEST-----\n");
}
bool LicenseClient::loadActivationData()
{
if (fs::is_regular_file(m_licenseFile))
{
// Encrypt in nodejs
/*
let activationData = {
activationId: '1234567890',
systemParams: {
biosSerialNum: '1234567797980980',
diskSerialNum: '1234567857845764'
},
licensedModules: ['ccengine', 'cc-data-usd', 'cc-data-rub']
};
let algorithm = 'aes-256-gcm';
let password = Buffer.from('e73db572349005f1c41979baf8166a0900745119fa096b9c3efbcee11ddd8b88', 'hex');
let privateKey = '-----BEGIN EC PRIVATE KEY-----' + "\n" +
'MIGAAgEBBCQBPIZnOt/mEsgtH3S9XZMGRuHkB5hYbMJ/BxcGmAc/pZLdxDWgBwYF' + "\n" +
'K4EEABGhTANKAAQHyyrnJFywb+B0pcaVRHIOcEao3OtSMSJJZiluIMme1aE+20UA' + "\n" +
'0c0+2u+M6bMi072XrXLf8KudcAxihG/aqCqbVVZS6i10SSM=' + "\n" +
'-----END EC PRIVATE KEY-----';
crypto.randomBytes(16, (err, nonce) => {
activationData.nonce = nonce.toString('base64');
let data = JSON.stringify(activationData);
let sign = crypto.createSign('SHA256');
sign.write(data);
sign.end();
let signature = sign.sign(privateKey, 'hex');
data = JSON.stringify({ data, signature });
crypto.randomBytes(16, (err, iv) => {
zlib.deflateRaw(data, (err, compressed) => {
let cipher = crypto.createCipheriv(algorithm, password, iv);
let encrypted = cipher.update(compressed);
encrypted = Buffer.concat([encrypted, cipher.final()]);
let tag = cipher.getAuthTag();
let output = Buffer.concat([iv, tag, encrypted]);
console.log(output.toString('hex'));
});
});
});
*/
/*unsigned char publicKey[] = {
0x04, 0x07, 0xcb, 0x2a, 0xe7, 0x24, 0x5c, 0xb0, 0x6f, 0xe0, 0x74, 0xa5, 0xc6, 0x95, 0x44, 0x72,
0x0e, 0x70, 0x46, 0xa8, 0xdc, 0xeb, 0x52, 0x31, 0x22, 0x49, 0x66, 0x29, 0x6e, 0x20, 0xc9, 0x9e,
0xd5, 0xa1, 0x3e, 0xdb, 0x45, 0x00, 0xd1, 0xcd, 0x3e, 0xda, 0xef, 0x8c, 0xe9, 0xb3, 0x22, 0xd3,
0xbd, 0x97, 0xad, 0x72, 0xdf, 0xf0, 0xab, 0x9d, 0x70, 0x0c, 0x62, 0x84, 0x6f, 0xda, 0xa8, 0x2a,
0x9b, 0x55, 0x56, 0x52, 0xea, 0x2d, 0x74, 0x49, 0x23 }; */
unsigned char publicKey[] = {
0x30, 0x5e, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
0x81, 0x04, 0x00, 0x11, 0x03, 0x4a, 0x00, 0x04, 0x07, 0xcb, 0x2a, 0xe7, 0x24, 0x5c, 0xb0, 0x6f,
0xe0, 0x74, 0xa5, 0xc6, 0x95, 0x44, 0x72, 0x0e, 0x70, 0x46, 0xa8, 0xdc, 0xeb, 0x52, 0x31, 0x22,
0x49, 0x66, 0x29, 0x6e, 0x20, 0xc9, 0x9e, 0xd5, 0xa1, 0x3e, 0xdb, 0x45, 0x00, 0xd1, 0xcd, 0x3e,
0xda, 0xef, 0x8c, 0xe9, 0xb3, 0x22, 0xd3, 0xbd, 0x97, 0xad, 0x72, 0xdf, 0xf0, 0xab, 0x9d, 0x70,
0x0c, 0x62, 0x84, 0x6f, 0xda, 0xa8, 0x2a, 0x9b, 0x55, 0x56, 0x52, 0xea, 0x2d, 0x74, 0x49, 0x23 };
unsigned char password[] = {
0xe7, 0x3d, 0xb5, 0x72, 0x34, 0x90, 0x05, 0xf1, 0xc4, 0x19, 0x79, 0xba, 0xf8, 0x16, 0x6a, 0x09,
0x00, 0x74, 0x51, 0x19, 0xfa, 0x09, 0x6b, 0x9c, 0x3e, 0xfb, 0xce, 0xe1, 0x1d, 0xdd, 0x8b, 0x88 };
/* unsigned char encrypted[] = {
0x5d, 0xc1, 0x4e, 0xaf, 0x95, 0xf0, 0x1d, 0x84, 0x09, 0x71, 0x66, 0x0f, 0x87, 0x19, 0x7a, 0xa1,
0x6a, 0x77, 0x39, 0x1e, 0x0a, 0xde, 0x93, 0x0c, 0xda, 0xa8, 0x62, 0x76, 0x53, 0xcb, 0xa7, 0x9f,
0x8d, 0x36, 0x2a, 0x74, 0xcd, 0x5d, 0x78, 0x6e, 0x83, 0x14, 0xa4, 0x21, 0x3c }; */
/* {
unsigned char encrypted[] = {
0x88, 0x5d, 0x38, 0xfe, 0xfc, 0x51, 0x7d, 0x3c, 0xb5, 0x95, 0x49, 0xae, 0xa4, 0x6a, 0xa4, 0x7e,
0xda, 0x5d, 0x29, 0x84, 0xc2, 0x85, 0xb6, 0x18, 0x6b, 0xd6, 0x40, 0x77, 0x28, 0xc3, 0xa4, 0x0c,
0xd1, 0x47, 0x78, 0xf9, 0xce, 0xe4, 0x22, 0xec, 0x68, 0x3f, 0x34, 0xe3, 0xa0, 0x23, 0x42, 0xcc,
0x35, 0x50, 0x2a, 0x34, 0xa5, 0xc3, 0x0b, 0x77, 0xa6, 0xb1, 0x00, 0x53, 0xf7, 0x86, 0x08, 0x94,
0x72, 0x99, 0x88, 0xc6, 0x07, 0x18, 0x2b, 0xb0, 0xd9, 0xd2, 0x1d, 0xea, 0x5c, 0x96, 0x14, 0x25,
0x70, 0xd8, 0x02, 0xb6, 0xc7, 0xa2, 0xae, 0x9e, 0x89, 0x87, 0xb9, 0x9f, 0xad, 0xd6, 0xc6, 0x8a,
0xb2, 0x53, 0x8f, 0xfb, 0x3d, 0x4b, 0x21, 0xd3, 0xa1, 0x43, 0x88, 0xef, 0x16, 0x20, 0x19, 0xa2,
0x6c, 0x36, 0xc4, 0xfd, 0x17, 0x0c, 0xad, 0x30, 0xef, 0xfc, 0x6c, 0xe8, 0x2c, 0x3a, 0x55, 0x18,
0x00, 0x8a, 0x15, 0x46, 0xd6, 0x36, 0x03, 0xb6, 0x8f, 0xb9, 0x86, 0x29, 0x1f, 0x9e, 0xc2, 0x89,
0xa2, 0x71, 0x49, 0x64, 0xc7, 0xa6, 0x70, 0x80, 0x00, 0x4c, 0x5d, 0x7c, 0x22, 0x6b, 0xdd, 0x0e,
0x2d, 0x17, 0xab, 0xe6, 0xf8, 0x75, 0x8b, 0xd2, 0x5d, 0x2d, 0x40, 0xd6, 0xea, 0x1b, 0x4f, 0xca,
0x02, 0x2e, 0x98, 0x16, 0x99, 0xdb, 0x14, 0x67, 0x90, 0xd6, 0x8f, 0xbf, 0xc6, 0x4d, 0xd2, 0x92,
0xd2, 0x7b, 0x37, 0x5c, 0x60, 0x7b, 0x78, 0x90, 0x47, 0x73, 0x0a, 0xda, 0x4d, 0xa5, 0x31, 0x51,
0x0c, 0xb6, 0x88, 0x93, 0x37, 0x4e, 0x39, 0x5c, 0x06, 0x90, 0x49, 0xd7, 0x48, 0x67, 0x60, 0xfc,
0x9f, 0x40, 0xaf, 0x50, 0x67, 0xc0, 0xf5, 0xb4, 0xab, 0xac, 0xa1, 0x1c, 0x95, 0xd8, 0x57, 0x15,
0x7d, 0xe8, 0xa7, 0x7f, 0x1a, 0xad, 0x64, 0x7d, 0xa9, 0x3d, 0x38, 0xa6, 0x06, 0xc2, 0x5a, 0x46,
0xae, 0x07, 0x53, 0x97, 0x68, 0x6c, 0xc5, 0xf8, 0x2a, 0xb4, 0x86, 0x8e, 0x9a, 0x7b, 0x48, 0x51,
0xb4, 0x76, 0x8d, 0x9e, 0x6d, 0x47, 0xa8, 0x55, 0x39, 0x73, 0x1d, 0x35, 0x7c, 0xd2, 0xc1, 0x6a,
0x22, 0x91, 0x59, 0x4d, 0xaa, 0x69, 0x11, 0xdf, 0xf3, 0x4f, 0x41, 0x04, 0xff, 0xb4, 0x5d, 0x42,
0x42, 0x73, 0x07, 0xc4, 0xfc, 0xac, 0xa4, 0x98, 0x40, 0x24, 0x1f, 0x0a, 0x86, 0xda, 0x06 };
std::ofstream ofs("license.dat", std::ofstream::binary);
ofs.write((const char*)encrypted, sizeof(encrypted));
} */
SecByteBlock key(sizeof(password));
key.Assign(password, sizeof(password));
std::string encrypted = readBinaryFile(m_licenseFile);
if (std::size(encrypted) < initializationVectorSize)
{
throw std::runtime_error("Invalid license file");
}
std::string recoveredData;
GCM<AES>::Decryption d;
d.SetKeyWithIV(key, key.size(), (const CryptoPP::byte*)encrypted.data(), initializationVectorSize);
AuthenticatedDecryptionFilter df(d,
new StringSink(recoveredData),
AuthenticatedDecryptionFilter::MAC_AT_BEGIN |
AuthenticatedDecryptionFilter::THROW_EXCEPTION, macTagSize
); // AuthenticatedDecryptionFilter
// The StringSource dtor will be called immediately
// after construction below. This will cause the
// destruction of objects it owns. To stop the
// behavior so we can get the decoding result from
// the DecryptionFilter, we must use a redirector
// or manually Put(...) into the filter without
// using a StringSource.
StringSource ss2((const CryptoPP::byte*)encrypted.data() + initializationVectorSize, encrypted.size() - initializationVectorSize, true,
new Redirector(df /*, PASS_EVERYTHING */)
); // StringSource
if (!df.GetLastResult())
{
throw std::runtime_error("Unable to decrypt data");
}
boost::iostreams::array_source src{ recoveredData.data(), recoveredData.size() };
boost::iostreams::filtering_istream is;
boost::iostreams::zlib_params zlibParams;
zlibParams.noheader = true;
is.push(boost::iostreams::zlib_decompressor{zlibParams});
is.push(src);
SignedData signedData;
{
pt::ptree root;
pt::read_json(is, root);
deserialize(root, signedData);
}
ECDSA<EC2N, SHA256>::PublicKey pubKey;
//CryptoPP::FileSource fs("c:\\work\\ccengine\\openssl\\bin\\ccengine-pub.der", true /*binary*/);
CryptoPP::ArraySource arraySource(static_cast<const CryptoPP::byte*>(publicKey), sizeof(publicKey), true);
pubKey.Load(arraySource);
ECDSA<EC2N, SHA256>::Verifier verifier(pubKey);
std::string signatureDer;
StringSource ss(signedData.signature, true,
new HexDecoder(
new StringSink(signatureDer)
) // HexDecoder
); // StringSource
byte signature[ecdsaSignatureSize] = { 0 };
size_t signLength = CryptoPP::DSAConvertSignatureFormat(signature, sizeof(signature), CryptoPP::DSA_P1363,
(const CryptoPP::byte*)signatureDer.data(), signatureDer.size(), CryptoPP::DSA_DER);
bool result = verifier.VerifyMessage((const byte*)signedData.data.data(), signedData.data.size(), signature, signLength);
if (!result)
{
throw std::runtime_error("Signature could not be verified");
}
ActivationData activationData;
std::istringstream iss(signedData.data);
{
pt::ptree root;
pt::read_json(iss, root);
deserialize(root, activationData);
}
if (!validateActivationData(activationData))
{
throw std::runtime_error("You system is not genuine. Please contact support!");
}
m_activationData = std::move(activationData);
return true;
}
return false;
}
bool LicenseClient::validateActivationData(const ActivationData & activationData)
{
if (activationData.systemParams.empty())
{
return false;
}
if (activationData.productId != productId)
{
return false;
}
// activation parameters must match system parameters
for (const auto& entry : activationData.systemParams)
{
auto it = m_systemParams.find(entry.first);
if (it == m_systemParams.end() || it->second != entry.second)
{
return false;
}
}
return true;
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "SystemParamsProvider.h"
#include <optional>
#include <set>
#include <string>
#include <stdexcept>
struct ActivationData
{
std::string activationId;
std::string productId;
SystemParams systemParams;
std::set<std::string> licensedModules;
};
std::optional<std::string> validateLicenseKey(const std::string& licenseKey);
class LicenseClient
{
public:
explicit LicenseClient(SystemParamsProvider& systemParamsProvider, const std::string& licenseFile);
virtual ~LicenseClient();
public:
void init();
std::string buildActivationRequest();
bool isActivated() const { return m_activationData.has_value(); }
std::optional<std::string> activationNumber() const { return m_activationData.has_value() ? m_activationData->activationId : std::optional<std::string>(); }
auto licensedModules() { if (!isActivated()) { throw std::runtime_error("Not active"); } return m_activationData->licensedModules; }
private:
bool loadActivationData();
bool validateActivationData(const ActivationData& activationData);
private:
SystemParamsProvider& m_systemParamsProvider;
SystemParams m_systemParams;
std::string m_licenseFile;
std::optional<ActivationData> m_activationData;
};

View File

@ -1,180 +0,0 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.16
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
.SUFFIXES: .hpux_make_needs_suffix_list
# Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
# Set environment variables for the build.
# The shell in which to execute make rules.
SHELL = /bin/sh
# The CMake executable.
CMAKE_COMMAND = /usr/bin/cmake
# The command to remove a file.
RM = /usr/bin/cmake -E remove -f
# Escaping for special characters.
EQUALS = =
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /mnt/c/Users/petos/source/repos/CMakeProject1
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /mnt/c/Users/petos/source/repos/CMakeProject1
#=============================================================================
# Targets provided globally by CMake.
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
/usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# The main all target
all: cmake_check_build_system
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(CMAKE_COMMAND) -E cmake_progress_start /mnt/c/Users/petos/source/repos/CMakeProject1/CMakeFiles /mnt/c/Users/petos/source/repos/CMakeProject1/CMakeProject1/CMakeFiles/progress.marks
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/all
$(CMAKE_COMMAND) -E cmake_progress_start /mnt/c/Users/petos/source/repos/CMakeProject1/CMakeFiles 0
.PHONY : all
# The main clean target
clean:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/preinstall
.PHONY : preinstall/fast
# clear depends
depend:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend
# Convenience name for target.
CMakeProject1/CMakeFiles/CMakeProject1.dir/rule:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/CMakeFiles/CMakeProject1.dir/rule
.PHONY : CMakeProject1/CMakeFiles/CMakeProject1.dir/rule
# Convenience name for target.
CMakeProject1: CMakeProject1/CMakeFiles/CMakeProject1.dir/rule
.PHONY : CMakeProject1
# fast build rule for target.
CMakeProject1/fast:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/build
.PHONY : CMakeProject1/fast
CMakeProject1.o: CMakeProject1.cpp.o
.PHONY : CMakeProject1.o
# target to build an object file
CMakeProject1.cpp.o:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/CMakeProject1.cpp.o
.PHONY : CMakeProject1.cpp.o
CMakeProject1.i: CMakeProject1.cpp.i
.PHONY : CMakeProject1.i
# target to preprocess a source file
CMakeProject1.cpp.i:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/CMakeProject1.cpp.i
.PHONY : CMakeProject1.cpp.i
CMakeProject1.s: CMakeProject1.cpp.s
.PHONY : CMakeProject1.s
# target to generate assembly for a file
CMakeProject1.cpp.s:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/CMakeProject1.cpp.s
.PHONY : CMakeProject1.cpp.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@echo "... all (the default if no target is provided)"
@echo "... clean"
@echo "... depend"
@echo "... rebuild_cache"
@echo "... edit_cache"
@echo "... CMakeProject1"
@echo "... CMakeProject1.o"
@echo "... CMakeProject1.i"
@echo "... CMakeProject1.s"
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system

5
CMakeProject1/OSUtils.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <boost/filesystem/path.hpp>
boost::filesystem::path ensureLogFolder(const std::string& appName);

View File

@ -0,0 +1,34 @@
#include "OSUtils.h"
struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive-
#include <ShlObj.h>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
boost::filesystem::path ensureLogFolder(const std::string& appName)
{
char szPath[MAX_PATH] = { 0 };
if (SUCCEEDED(SHGetFolderPathA(NULL,
CSIDL_APPDATA | CSIDL_FLAG_CREATE,
NULL,
0,
szPath)))
{
auto logPath = fs::path(szPath) / appName / "Log";
if (!fs::exists(logPath))
{
fs::create_directories(logPath);
}
if (!fs::is_directory(logPath))
{
throw std::runtime_error("Could not access log directory");
}
return logPath;
}
throw std::runtime_error("Could not find application data folder");
}

View File

@ -0,0 +1,11 @@
#include "SystemParams.h"
const std::string SystemParamTypes::biosSerialNum{ "biosSerialNum" };
const std::string SystemParamTypes::computerUUID{ "computerUUID" };
const std::string SystemParamTypes::computerSerial{ "computerSerial" };
const std::string SystemParamTypes::diskSerialNum{ "diskSerialNum" };
const std::string SystemParamTypes::osId{ "osId" };
const std::string SystemParamTypes::nicMac{ "nicMac" };
const std::string SystemParamTypes::mainboardSerialNum{ "mainboardSerialNum" };
const std::string SystemParamTypes::cpuIdModel{ "cpuIdModel" };
const std::string SystemParamTypes::cpuIdHypervisor{ "cpuIdHypervisor" };

View File

@ -0,0 +1,19 @@
#pragma once
#include <map>
#include <string>
struct SystemParamTypes
{
static const std::string biosSerialNum;
static const std::string computerUUID;
static const std::string computerSerial;
static const std::string diskSerialNum;
static const std::string osId;
static const std::string nicMac;
static const std::string mainboardSerialNum;
static const std::string cpuIdModel;
static const std::string cpuIdHypervisor;
};
typedef std::map<std::string, std::string> SystemParams;

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef WIN32
#include "SystemParamsProvider_win.h"
typedef SystemParamsProvider_win SystemParamsProvider;
#else
#include "SystemParamsProvider_linux.h"
typedef SystemParamsProvider_linux SystemParamsProvider;
#endif // WIN32

View File

@ -0,0 +1,468 @@
#include "SystemParamsProvider_linux.h"
#include <boost/process.hpp>
#include <cpuid.h>
#include <future>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <regex>
#include <sstream>
namespace bp = boost::process; //we will assume this for all further examples
namespace
{
template< typename T >
std::string intToHex( T i )
{
std::ostringstream stream;
stream << std::setfill ('0') << std::setw(sizeof(T)*2)
<< std::hex << i;
return stream.str();
}
std::vector<std::string> splitString(const std::string& str, char c = ' ')
{
std::vector<std::string> result;
auto it = str.cbegin();
do
{
auto firstIt = it;
while(*it != c && it != str.cend()) {
++it;
}
result.emplace_back(firstIt, it);
} while (it++ != str.cend());
return result;
}
std::optional<std::string> readFileLine(const std::string& fileName)
{
std::string line;
std::ifstream myfile(fileName);
if (myfile.is_open()) {
std::cout << "File " << fileName << " successfully open" << std::endl;
if (getline (myfile,line)) {
return line;
}
myfile.close();
}
return {};
}
std::optional<std::string> loadOsId()
{
auto machineId = readFileLine("/var/lib/dbus/machine-id");
if (machineId.value_or("").empty()) {
machineId = readFileLine("/etc/machine-id");
}
return machineId;
}
std::optional<std::string> loadBoardSerial()
{
return readFileLine("/sys/class/dmi/id/board_serial");
}
std::optional<std::string> loadComputerUUID()
{
return readFileLine("/sys/class/dmi/id/product_uuid");
}
std::optional<std::string> loadComputerSerial()
{
auto productSerial = readFileLine("/sys/class/dmi/id/product_serial");
if(!productSerial.value_or("").empty()) {
auto productFamily = readFileLine("/sys/class/dmi/id/product_family");
auto productName = readFileLine("/sys/class/dmi/id/product_name");
std::ostringstream os;
os << productFamily.value() << "/" << productName.value() << "/" << productSerial.value();
return os.str();
}
return {};
}
struct CpuIdInfo {
uint32_t vendorId;
std::string vendorString;
uint32_t modelId;
bool isHypervisor;
std::string serialNumber;
};
CpuIdInfo loadCpuId()
{
uint32_t a, b, c, d;
__cpuid (0 /* vendor string */, a, b, c, d);
std::cout << "EAX: " << std::hex << std::setw(8) << std::setfill('0') << a << "\nEBX: " << b << "\nECX: " << c << "\nEDX: " << d << "\n";
uint32_t vendorId = a;
uint32_t vendorString[] = {b, d, c, 0};
__cpuid (1 /* Processor Info and Feature Bits */, a, b, c, d);
std::cout << "EAX: " << std::hex << std::setw(8) << std::setfill('0') << a << "\nEBX: " << b << "\nECX: " << c << "\nEDX: " << d << "\n";
bool isHypervisor = c & 0x80000000;
uint32_t modelId = a & 0b00001111111111110011111111111111;
__cpuid (3 /* Processor Serial Number */, a, b, c, d);
std::ostringstream oss;
oss << std::hex << std::setw(8) << std::setfill('0') << a << "-"
<< std::hex << std::setw(8) << std::setfill('0') << b << "-"
<< std::hex << std::setw(8) << std::setfill('0') << c << "-"
<< std::hex << std::setw(8) << std::setfill('0') << d;
return CpuIdInfo {vendorId, std::string((const char*)vendorString), modelId, isHypervisor, oss.str()};
}
std::optional<std::pair<std::string, std::string>> matchParameter(
const std::string& line,
const std::string& parameterRegex,
const std::string& valueRegex = "([0-9a-fA-F\\-_]+)$"
)
{
std::regex line_regex(parameterRegex + ": " + valueRegex);
std::smatch line_match;
if(std::regex_search(line, line_match, line_regex)) {
std::cout << "DEBUG: Found " << line_match[1] << ": " << line_match[2] << std::endl;
return std::make_pair<std::string, std::string>(line_match[1], line_match[2]);
}
return {};
}
struct DmiDecodeParamTypes
{
static const std::string biosSerialNum;
static const std::string systemUUID;
static const std::string systemSerialNum;
static const std::string systemFamily;
static const std::string systemProductNumber;
static const std::string baseboardSerialNumber;
};
const std::string DmiDecodeParamTypes::systemUUID = "systemUUID";
const std::string DmiDecodeParamTypes::systemSerialNum = "systemSerialNum";
const std::string DmiDecodeParamTypes::systemFamily = "systemFamily";
const std::string DmiDecodeParamTypes::systemProductNumber = "systemProductNumber";
const std::string DmiDecodeParamTypes::baseboardSerialNumber = "baseboardSerialNumber";
std::map<std::string, std::string> read_dmidecode()
{
std::error_code ec;
std::map<std::string, std::string> output;
bp::ipstream is; //reading pipe-stream
bp::child c("/usr/sbin/dmidecode", ec, bp::std_out > is, bp::std_err > bp::null);
if(ec) {
return output;
}
std::regex section_regex("DMI type ([0-9]+)");
std::string line;
int section = -1;
while (std::getline(is, line)) {
if (!line.empty() && line[0] != '\t') {
std::smatch section_match;
if(std::regex_search(line, section_match, section_regex)) {
section = std::atoi(section_match[1].str().c_str());
std::cout << "DEBUG: Found section: " << section_match[1] << std::endl;
}
continue;
}
if (section == 1) {
{
const auto matchedParameter = matchParameter(line, "(UUID)");
if (matchedParameter.has_value()) {
output["systemUUID"] = matchedParameter.value().second;
continue;
}
}
{
const auto matchedParameter = matchParameter(line, "(Serial Number)", "([0-9a-zA-Z\\-_]+)$");
if (matchedParameter.has_value()) {
output["systemSerialNumber"] = matchedParameter.value().second;
continue;
}
}
{
const auto matchedParameter = matchParameter(line, "(Family)", "([0-9a-zA-Z\\-_ ]+)$");
if (matchedParameter.has_value()) {
output["systemFamily"] = matchedParameter.value().second;
continue;
}
}
{
const auto matchedParameter = matchParameter(line, "(Product name)", "([0-9a-zA-Z\\-_ ]+)$");
if (matchedParameter.has_value()) {
output["systemProductName"] = matchedParameter.value().second;
continue;
}
}
}
else if (section == 2) {
{
const auto matchedParameter = matchParameter(line, "(Serial Number)", "([0-9a-zA-Z\\-_]+)$");
if (matchedParameter.has_value()) {
output["baseboardSerialNumber"] = matchedParameter.value().second;
continue;
}
}
}
}
c.wait(ec);
return output;
}
std::map<std::string, std::string> readMacAddresses()
{
std::error_code ec;
std::map<std::string, std::string> output;
bp::ipstream is; //reading pipe-stream
bp::child c("/usr/bin/find /sys/class/net -mindepth 1 -maxdepth 1 ! -name lo -printf \"%P: \" -execdir /usr/bin/cat {}/address ;", ec, bp::std_out > is, bp::std_err > bp::null);
if (ec) {
return output;
}
std::regex section_regex("([0-9a-zA-Z\\-_]+): ([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})");
std::string line;
int section = -1;
while (std::getline(is, line)) {
if (!line.empty() && line[0] == 'e') {
std::smatch section_match;
if(std::regex_match(line, section_match, section_regex)) {
std::cout << "DEBUG: Found device: " << section_match[1] << " with MAC: " << section_match[2] << std::endl;
output[section_match[1]] = section_match[2];
}
continue;
}
}
c.wait(ec);
return output;
}
struct LsBlkResultEntry{
std::string mountPoint;
std::string parentName;
std::string name;
std::string model;
std::string serial;
std::string type;
};
std::optional<LsBlkResultEntry> parseLsBlkResultEntry(const std::string& line)
{
auto cols = splitString(line, ' ');
if(cols.size() == 6) {
return LsBlkResultEntry{
cols[0],cols[1],cols[2],cols[3],cols[4],cols[5]
};
}
return {};
}
std::optional<std::string> readDiskSerial()
{
std::error_code ec;
bp::ipstream is; //reading pipe-stream
bp::child c("/usr/bin/lsblk -oMOUNTPOINT,PKNAME,NAME,MODEL,SERIAL,TYPE --raw", ec, bp::std_out > is, bp::std_err > bp::null);
if (ec) {
return {};
}
std::vector<LsBlkResultEntry> cmdOut;
std::string line;
int section = -1;
while (std::getline(is, line)) {
if (!line.empty()) {
const auto entry = parseLsBlkResultEntry(line);
if (entry.has_value()) {
cmdOut.push_back(entry.value());
}
}
}
c.wait(ec);
if(ec) {
return {};
}
// Find best mountpoint
LsBlkResultEntry const* bestEntry = nullptr;
for (const auto& entry: cmdOut) {
if(entry.mountPoint == "/") {
bestEntry = &entry;
}
else if(entry.mountPoint == "/boot" && (bestEntry == nullptr || bestEntry->mountPoint == "/sysroot")) {
bestEntry = &entry;
}
else if(entry.mountPoint == "/sysroot" && bestEntry == nullptr) {
bestEntry = &entry;
}
}
if(bestEntry == nullptr)
{
return {};
}
std::cout << "Found best mount point: " << bestEntry->mountPoint << std::endl;
int depth = 0;
while(depth < 10 && !bestEntry->parentName.empty()) {
++depth;
bool found = false;
for (const auto& entry: cmdOut) {
if (entry.name == bestEntry->parentName) {
bestEntry = &entry;
found = true;
break;
}
}
if (!found) {
break;
}
}
if (bestEntry->type == "disk" && !bestEntry->serial.empty()) {
return bestEntry->serial;
}
return {};
}
}
namespace detail
{
class SystemParamsProvider_linuxImpl final
{
public:
SystemParamsProvider_linuxImpl()
{
}
~SystemParamsProvider_linuxImpl()
{
}
public:
SystemParams retrieveSystemParams()
{
SystemParams result;
// result[SystemParamTypes::biosSerialNum] = processiosData(biosFuture.get());
{
const auto val = loadComputerUUID();
if (val.has_value()) {
result[SystemParamTypes::computerUUID] = val.value();
}
}
{
const auto val = loadComputerSerial();
if (val.has_value()) {
result[SystemParamTypes::computerSerial] = val.value();
}
}
{
const auto val = loadOsId();
if (val.has_value()) {
result[SystemParamTypes::osId] = val.value();
}
}
{
const auto val = loadBoardSerial();
if (val.has_value()) {
result[SystemParamTypes::mainboardSerialNum] = val.value();
}
}
auto cpuIdInfo = loadCpuId();
std::cout << "Vendor ID: " << cpuIdInfo.vendorId << std::endl;
std::cout << "Model ID: " << cpuIdInfo.modelId << std::endl;
std::cout << "Vendor String: " << cpuIdInfo.vendorString << std::endl;
std::cout << "Is hypervisor: " << cpuIdInfo.isHypervisor << std::endl;
std::cout << "Serial number: " << cpuIdInfo.serialNumber << std::endl;
result[SystemParamTypes::cpuIdModel] = intToHex(cpuIdInfo.vendorId) + intToHex(cpuIdInfo.modelId);
if(cpuIdInfo.isHypervisor) {
result[SystemParamTypes::cpuIdHypervisor] = "true";
}
const auto macAddresses = readMacAddresses();
if(macAddresses.size() > 0)
{
result[SystemParamTypes::nicMac] = macAddresses.begin()->second;
}
const auto diskSerial = readDiskSerial();
if (diskSerial.has_value()) {
result[SystemParamTypes::diskSerialNum] = diskSerial.value();
}
auto result2 = read_dmidecode();
for (auto it = result2.cbegin(); it != result2.cend(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
if (result.find(SystemParamTypes::mainboardSerialNum) == result.end() &&
result2.find(DmiDecodeParamTypes::baseboardSerialNumber) != result2.end()) {
result[SystemParamTypes::mainboardSerialNum] = result2[DmiDecodeParamTypes::baseboardSerialNumber];
}
if (result.find(SystemParamTypes::computerUUID) == result.end() &&
result2.find(DmiDecodeParamTypes::systemUUID) != result2.end()) {
result[SystemParamTypes::computerUUID] = result2[DmiDecodeParamTypes::systemUUID];
}
if (result.find(SystemParamTypes::computerSerial) == result.end() &&
result2.find(DmiDecodeParamTypes::systemSerialNum) != result2.end()) {
if(!result2[DmiDecodeParamTypes::systemUUID].empty()) {
auto productFamily = result2.find(DmiDecodeParamTypes::systemFamily);
auto productName = result2.find(DmiDecodeParamTypes::systemProductNumber);
std::ostringstream os;
os << ((productFamily != result2.end()) ? productFamily->second : "")
<< "/" << ((productName != result2.end()) ? productName->second : "")
<< "/" << result2[DmiDecodeParamTypes::systemUUID];
result[SystemParamTypes::computerSerial] = os.str();
}
}
return result;
}
private:
private:
private:
};
}
SystemParamsProvider_linux::SystemParamsProvider_linux()
: m_impl(std::make_unique<detail::SystemParamsProvider_linuxImpl>())
{
}
SystemParamsProvider_linux::~SystemParamsProvider_linux()
{
}
SystemParams SystemParamsProvider_linux::retrieveSystemParams()
{
return m_impl->retrieveSystemParams();
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "SystemParams.h"
#include <memory>
#include <string>
namespace detail
{
class SystemParamsProvider_linuxImpl;
}
class SystemParamsProvider_linux final
{
public:
SystemParamsProvider_linux();
~SystemParamsProvider_linux();
public:
SystemParams retrieveSystemParams();
private:
std::unique_ptr<detail::SystemParamsProvider_linuxImpl> m_impl;
};

View File

@ -0,0 +1,420 @@
#include "SystemParamsProvider_win.h"
struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive-
#define _WIN32_DCOM
#include <comdef.h>
#include <Wbemidl.h>
#include <boost/algorithm/string/join.hpp>
#include <future>
#include <iostream>
#include <sstream>
#pragma comment(lib, "wbemuuid.lib")
namespace
{
std::string convertWCSToMBS(const wchar_t* pstr, long wslen)
{
int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);
std::string dblstr(len, '\0');
len = ::WideCharToMultiByte(CP_ACP, 0 /* no flags */,
pstr, wslen /* not necessary NULL-terminated */,
&dblstr[0], len,
NULL, NULL /* no default char */);
return dblstr;
}
std::string convertBSTRToMBS(BSTR bstr)
{
int wslen = ::SysStringLen(bstr);
return convertWCSToMBS((wchar_t*)bstr, wslen);
}
BSTR convertMBSToBSTR(const std::string& str)
{
int wslen = ::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
str.data(), static_cast<int>(str.length()),
NULL, 0);
BSTR wsdata = ::SysAllocStringLen(NULL, wslen);
::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
str.data(), static_cast<int>(str.length()),
wsdata, wslen);
return wsdata;
}
class QuerySink final : public IWbemObjectSink
{
public:
typedef std::vector<std::string> Fields_type;
typedef std::vector<std::vector<std::string>> Result_type;
public:
QuerySink(const std::string& wmiClass, const Fields_type& fields)
: m_wmiClass(wmiClass)
, m_fields(fields)
{
m_lRef = 0;
m_bDone = false;
InitializeCriticalSection(&m_threadLock);
}
~QuerySink() {
m_bDone = true;
DeleteCriticalSection(&m_threadLock);
}
public:
virtual ULONG STDMETHODCALLTYPE AddRef() override
{
return InterlockedIncrement(&m_lRef);
}
virtual ULONG STDMETHODCALLTYPE Release() override
{
LONG lRef = InterlockedDecrement(&m_lRef);
if (lRef == 0)
delete this;
return lRef;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override
{
if (riid == IID_IUnknown || riid == IID_IWbemObjectSink)
{
*ppv = (IWbemObjectSink *)this;
AddRef();
return WBEM_S_NO_ERROR;
}
else return E_NOINTERFACE;
}
virtual HRESULT STDMETHODCALLTYPE Indicate(
LONG lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
) override
{
for (int i = 0; i < lObjectCount; i++)
{
std::vector<std::string> lineResult;
lineResult.reserve(m_fields.size());
for (const auto& field : m_fields)
{
HRESULT hres = S_OK;
VARIANT varName;
hres = apObjArray[i]->Get(convertMBSToBSTR(field),
0, &varName, 0, 0);
if (FAILED(hres))
{
std::ostringstream ostr;
ostr << "Failed to get the data from the query. Error code = 0x"
<< std::hex << hres;
std::cerr << ostr.str() << std::endl;
return WBEM_E_FAILED; // Program has failed.
}
lineResult.push_back(convertBSTRToMBS(V_BSTR(&varName)));
}
m_result.push_back(lineResult);
}
return WBEM_S_NO_ERROR;
}
virtual HRESULT STDMETHODCALLTYPE SetStatus(
/* [in] */ LONG lFlags,
/* [in] */ HRESULT hResult,
/* [in] */ BSTR strParam,
/* [in] */ IWbemClassObject __RPC_FAR *pObjParam
) override
{
if (lFlags == WBEM_STATUS_COMPLETE)
{
// Call complete
EnterCriticalSection(&m_threadLock);
m_bDone = true;
LeaveCriticalSection(&m_threadLock);
m_resultPromise.set_value(std::move(m_result));
}
else if (lFlags == WBEM_STATUS_PROGRESS)
{
// Call in progress...
}
return WBEM_S_NO_ERROR;
}
std::future<Result_type> getFuture()
{
return m_resultPromise.get_future();
}
bool IsDone() const
{
bool done = true;
EnterCriticalSection(&m_threadLock);
done = m_bDone;
LeaveCriticalSection(&m_threadLock);
return done;
}
private:
LONG m_lRef;
bool m_bDone;
mutable CRITICAL_SECTION m_threadLock; // for thread safety
const std::string m_wmiClass;
const Fields_type m_fields;
std::vector<std::vector<std::string>> m_result;
std::promise<Result_type> m_resultPromise;
};
void initializeCOM()
{
// We need to initialize COM for SystemParamsProvider
HRESULT hres = 0;
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
std::ostringstream ostr;
ostr << "Failed to initialize COM library. Error code = 0x" << std::hex << hres;
throw std::runtime_error(ostr.str());
}
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL); // Reserved
if (FAILED(hres) && hres != RPC_E_TOO_LATE) // Ignore error if the security has already been initialized
{
CoUninitialize();
std::ostringstream ostr;
ostr << "Failed to initialize security. Error code = 0x" << std::hex << hres;
throw std::runtime_error(ostr.str());
}
}
}
namespace detail
{
class SystemParamsProvider_winImpl final
{
public:
SystemParamsProvider_winImpl()
{
HRESULT hres = 0;
initializeCOM();
// Obtain the initial locator to WMI -------------------------
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *)&m_pLoc);
if (FAILED(hres))
{
CoUninitialize();
std::ostringstream ostr;
ostr << "Failed to create IWbemLocator object. Err code = 0x" << std::hex << hres;
throw std::runtime_error(ostr.str());
}
// Connect to WMI through the IWbemLocator::ConnectServer method
// Connect to the local root\cimv2 namespace
// and obtain pointer pSvc to make IWbemServices calls.
hres = m_pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&m_pSvc);
if (FAILED(hres))
{
m_pLoc->Release();
CoUninitialize();
std::ostringstream ostr;
ostr << "Could not connect. Error code = 0x" << std::hex << hres;
throw std::runtime_error(ostr.str());
}
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(m_pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE); // proxy capabilities
if (FAILED(hres))
{
m_pSvc->Release();
m_pLoc->Release();
CoUninitialize();
std::ostringstream ostr;
ostr << "Could not set proxy blanket. Error code = 0x" << std::hex << hres;
throw std::runtime_error(ostr.str());
}
}
~SystemParamsProvider_winImpl()
{
m_pSvc->Release();
m_pLoc->Release();
CoUninitialize();
}
public:
SystemParams retrieveSystemParams()
{
auto biosFuture = queryWmiInfo("Win32_BIOS", { "SerialNumber" });
auto computerSystemProductFuture = queryWmiInfo("Win32_ComputerSystemProduct", { "UUID" });
auto diskDriveFuture = queryWmiInfo("Win32_DiskDrive", { "SerialNumber" }, "DeviceId = \"\\\\\\\\.\\\\PHYSICALDRIVE0\"");
auto osFuture = queryWmiInfo("Win32_OperatingSystem", { "SerialNumber" });
auto nicFuture = queryWmiInfo("Win32_NetworkAdapter", { "MACAddress" }, "PhysicalAdapter = TRUE");
auto mainboardFuture = queryWmiInfo("Win32_BaseBoard", { "SerialNumber" });
SystemParams result;
result[SystemParamTypes::biosSerialNum] = processBiosData(biosFuture.get());
result[SystemParamTypes::computerUUID] = processComputerSystemProductData(computerSystemProductFuture.get());
result[SystemParamTypes::diskSerialNum] = processDiskDrive(diskDriveFuture.get());
result[SystemParamTypes::osId] = processOs(osFuture.get());
result[SystemParamTypes::nicMac] = processNic(nicFuture.get());
result[SystemParamTypes::mainboardSerialNum] = processMainBoard(mainboardFuture.get());
return result;
}
private:
typedef std::vector<std::vector<std::string>> QueryResult_type;
private:
std::future<QueryResult_type> queryWmiInfo(const std::string& wmiClass, const std::vector<std::string>& fieldNames, const std::string& condition = "")
{
HRESULT hres = 0;
std::ostringstream queryStr;
queryStr << "SELECT " << boost::algorithm::join(fieldNames, ",") << " FROM " << wmiClass;
if (!condition.empty())
{
queryStr << " WHERE " << condition;
}
// Use the IWbemServices pointer to make requests of WMI ----
QuerySink* pResponseSink = new QuerySink(wmiClass, fieldNames);
pResponseSink->AddRef();
auto resultFuture = pResponseSink->getFuture();
hres = m_pSvc->ExecQueryAsync(bstr_t("WQL"),
convertMBSToBSTR(queryStr.str()),
WBEM_FLAG_BIDIRECTIONAL,
NULL,
pResponseSink);
if (FAILED(hres))
{
pResponseSink->Release();
std::ostringstream ostr;
ostr << "Query for " << boost::algorithm::join(fieldNames, ", ") << " failed. Error code = 0x" << std::hex << hres;
throw std::runtime_error(ostr.str());
}
return resultFuture;
}
static std::string processBiosData(const QueryResult_type& biosInfo)
{
if (biosInfo.size() > 0)
{
return biosInfo[0][0];
}
return {};
}
static std::string processComputerSystemProductData(const QueryResult_type& csInfo)
{
if (csInfo.size() > 0)
{
return csInfo[0][0];
}
return {};
}
static std::string processDiskDrive(const QueryResult_type& csInfo)
{
if (csInfo.size() > 0)
{
return csInfo[0][0];
}
return {};
}
static std::string processOs(const QueryResult_type& csInfo)
{
if (csInfo.size() > 0)
{
return csInfo[0][0];
}
return {};
}
static std::string processNic(const QueryResult_type& csInfo)
{
if (csInfo.size() > 0)
{
return csInfo[0][0];
}
return {};
}
static std::string processMainBoard(const QueryResult_type& csInfo)
{
if (csInfo.size() > 0)
{
return csInfo[0][0];
}
return {};
}
private:
IWbemLocator* m_pLoc = nullptr;
IWbemServices *m_pSvc = nullptr;
};
}
SystemParamsProvider_win::SystemParamsProvider_win()
: m_impl(std::make_unique<detail::SystemParamsProvider_winImpl>())
{
}
SystemParamsProvider_win::~SystemParamsProvider_win()
{
}
SystemParams SystemParamsProvider_win::retrieveSystemParams()
{
return m_impl->retrieveSystemParams();
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "SystemParams.h"
#include <memory>
#include <string>
namespace detail
{
class SystemParamsProvider_winImpl;
}
class SystemParamsProvider_win final
{
public:
SystemParamsProvider_win();
~SystemParamsProvider_win();
public:
SystemParams retrieveSystemParams();
private:
std::unique_ptr<detail::SystemParamsProvider_winImpl> m_impl;
};