From 79d5f48454fb84b4752c329dbdc529aaf6ea7746 Mon Sep 17 00:00:00 2001 From: Manuel Dobusch Date: Tue, 17 Jan 2017 16:46:24 +0100 Subject: [PATCH] logic: licence error handling Fixed bug #277 and improved error handling in licence code --- src/lib/LicenseChecker.cpp | 18 +++ src/lib_license/Generator.cpp | 78 ++++++++-- src/lib_license/Generator.h | 2 +- src/lib_license/License.cpp | 273 ++++++++++++++++++++++++++-------- src/lib_license/License.h | 4 +- 5 files changed, 297 insertions(+), 78 deletions(-) diff --git a/src/lib/LicenseChecker.cpp b/src/lib/LicenseChecker.cpp index c09867e4..55286a40 100644 --- a/src/lib/LicenseChecker.cpp +++ b/src/lib/LicenseChecker.cpp @@ -51,6 +51,13 @@ void LicenseChecker::saveCurrentLicenseString(const std::string& licenseString) } ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); + + if (appSettings == NULL) + { + LOG_ERROR_STREAM(<< "Unable to retrieve app settings"); + return; + } + std::string appPath(AppPath::getAppPath()); appSettings->setLicenseString(license.getLicenseEncodedString(appPath)); @@ -66,10 +73,21 @@ bool LicenseChecker::isCurrentLicenseValid() LicenseChecker::LicenseState LicenseChecker::checkCurrentLicense() const { ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); + if (appSettings == NULL) + { + LOG_ERROR_STREAM(<< "Unable to retrieve app settings"); + return LICENSE_EMPTY; + } std::string licenseCheck = appSettings->getLicenseCheck(); std::string appPath(AppPath::getAppPath()); + if (appPath.size() <= 0) + { + LOG_ERROR_STREAM(<< "Failed to retrieve app path"); + return LICENSE_EMPTY; + } + std::string licenseString = appSettings->getLicenseString(); if (licenseString.size() == 0) { diff --git a/src/lib_license/Generator.cpp b/src/lib_license/Generator.cpp index 7000435c..448a181a 100644 --- a/src/lib_license/Generator.cpp +++ b/src/lib_license/Generator.cpp @@ -64,14 +64,30 @@ std::string Generator::encodeLicense(const std::string& user, const int days) std::string Generator::encodeLicense(const std::string& user, const std::string& licenseType) { + if (user.size() <= 0) + { + std::cout << "No user given" << std::endl; + return ""; + } + + if (licenseType.size() <= 0) + { + std::cout << "No licence type given" << std::endl; + return ""; + } + License license; //load private key std::string filename = getPrivateKeyFilename(); + std::shared_ptr privateKey( Botan::PKCS8::load_key(filename, m_rng, PRIVATE_KEY_PASSWORD)); + Botan::RSA_PrivateKey *rsaKey = dynamic_cast(privateKey.get()); - if (!rsaKey) { + + if (!rsaKey) + { std::cout << "The key is not a RSA key" << std::endl; } @@ -114,51 +130,85 @@ std::string Generator::getPublicKeyPEMFileAsString() std::string Generator::getPrivateKeyPEMFileAsString() { + if (m_privateKey == NULL) + { + std::cout << "m_privateKey is NULL" << std::endl; + return ""; + } + return Botan::PKCS8::PEM_encode(*m_privateKey, m_rng, PRIVATE_KEY_PASSWORD); } void Generator::writeKeysToFiles() { - //public key - std::string filename = getPublicKeyFilename(); - std::cout << "publickey filename: " << filename << std::endl; - std::ofstream pub(filename); + std::string publicKeyFilename = getPublicKeyFilename(); + if (publicKeyFilename.size() <= 0) + { + std::cout << "Failed to retrieve file name for public key" << std::endl; + return; + } + + std::string privateKeyFilename = getPrivateKeyFilename(); + if (privateKeyFilename.size() <= 0) + { + std::cout << "Failed to retrieve file name for private key" << std::endl; + return; + } + + + std::cout << "public key filename: " << publicKeyFilename << std::endl; + std::ofstream pub(publicKeyFilename); pub << getPublicKeyPEMFileAsString(); std::cout << "public key created" << std::endl; - // private key - filename = getPrivateKeyFilename(); - std::cout << "publickey filename: " << filename << std::endl; - std::ofstream priv(filename); + + std::cout << "private key filename: " << privateKeyFilename << std::endl; + std::ofstream priv(privateKeyFilename); priv << getPrivateKeyPEMFileAsString(); std::cout << "private key created" << std::endl; } bool Generator::loadPrivateKeyFromFile() { - boost::filesystem::exists(getPrivateKeyFilename()); + if (boost::filesystem::exists(getPrivateKeyFilename()) == false) + { + return false; + } + Botan::Private_Key* privateKey = Botan::PKCS8::load_key(getPrivateKeyFilename(), m_rng, PRIVATE_KEY_PASSWORD); Botan::RSA_PrivateKey *rsaKey = dynamic_cast(privateKey); - if (!rsaKey) { + + if (!rsaKey) + { std::cout << "The key is not a RSA key" << std::endl; return false; } + m_privateKey = std::shared_ptr(rsaKey); - return true; + return (m_privateKey != NULL); } bool Generator::loadPrivateKeyFromString(const std::string& key) { + if (key.size() <= 0) + { + std::cout << "No key string given" << std::endl; + return false; + } + Botan::DataSource_Memory in(key); Botan::Private_Key* privateKey= Botan::PKCS8::load_key(in, m_rng, PRIVATE_KEY_PASSWORD); Botan::RSA_PrivateKey *rsaKey = dynamic_cast(privateKey); - if (!rsaKey) { + + if (!rsaKey) + { std::cout << "The key is not a RSA key" << std::endl; return false; } + m_privateKey = std::shared_ptr(rsaKey); - return true; + return (m_privateKey != NULL); } Botan::RSA_PrivateKey *Generator::getPrivateKey() const diff --git a/src/lib_license/Generator.h b/src/lib_license/Generator.h index 318221a4..6959b55c 100644 --- a/src/lib_license/Generator.h +++ b/src/lib_license/Generator.h @@ -31,7 +31,7 @@ public: bool loadPrivateKeyFromFile(); bool loadPrivateKeyFromString(const std::string& key); - void PrintLicense(); + // void PrintLicense(); // not implemented, is this deprecated or something? Botan::RSA_PrivateKey* getPrivateKey() const; diff --git a/src/lib_license/License.cpp b/src/lib_license/License.cpp index 34b16903..6767dac9 100644 --- a/src/lib_license/License.cpp +++ b/src/lib_license/License.cpp @@ -21,7 +21,7 @@ namespace { - std::string trim(const std::string &str) + std::string trimWhiteSpaces(const std::string &str) { auto wsfront = std::find_if_not(str.begin(), str.end(), [](int c){ return std::isspace(c); }); auto wsback = std::find_if_not(str.rbegin(), str.rend(), [](int c){ return std::isspace(c); }).base(); @@ -40,54 +40,112 @@ License::~License() std::string License::getHashLine() const { - return lines[4]; + if (m_lines.size() >= 5) + { + return m_lines[4]; + } + else + { + return ""; + } } std::string License::getMessage() const { std::string message = ""; - for(int i = 1; i < 5; ++i) + + if (m_lines.size() >= 5) { - message += lines[i]; + for (int i = 1; i < 5; ++i) + { + message += m_lines[i]; + } } + else + { + message = ""; + } + return message; } std::string License::getSignature() const { std::string signatue = ""; - for(int i = 5; i < 12; ++i) + + if (m_lines.size() >= 12) { - signatue += lines[i]; + for (int i = 5; i < 12; ++i) + { + signatue += m_lines[i]; + } } + else + { + signatue = ""; + } + return signatue; } std::string License::getVersionLine() const { - return lines[3]; + if (m_lines.size() >= 3) + { + return m_lines[3]; + } + else + { + return ""; + } } std::string License::getOwnerLine() const { - return lines[1]; + if (m_lines.size() >= 1) + { + return m_lines[1]; + } + else + { + return ""; + } } std::string License::getLicenseTypeLine() const { - return lines[2]; + if (m_lines.size() >= 2) + { + return m_lines[2]; + } + else + { + return ""; + } } int License::getTimeLeft() const { const std::string testText = "Test License - valid till "; - if(getLicenseTypeLine().substr(0, testText.length()) == testText) + const int leMagicNumber = 11; + + std::string licenceType = getLicenseTypeLine(); + + if (licenceType.size() <= (testText.length() + leMagicNumber)) { + return -2; // well, since it's unclear what type of licence...?? + } + + if(licenceType.substr(0, testText.length()) == testText) + { + std::string dateString = licenceType.substr(testText.length(), leMagicNumber); + boost::gregorian::date expireDate( - boost::gregorian::from_simple_string(getLicenseTypeLine().substr(testText.length(),11)) - ); + boost::gregorian::from_simple_string(dateString)); + boost::gregorian::date today = boost::gregorian::day_clock::local_day(); boost::gregorian::days daysLeft = expireDate - today; + return (daysLeft.days() < 0 ? -1 : daysLeft.days()); } else @@ -106,30 +164,33 @@ void License::create( //encode message Botan::PK_Signer signer(*privateKey, *(m_rng.get()), "EMSA4(SHA-256)"); Botan::DataSource_Memory in(getMessage()); - Botan::byte buf[4096] = {0}; - while (size_t got = in.read(buf, sizeof(buf))) { - signer.update(buf, got); + Botan::byte buffer[4096] = {0}; + + while (size_t got = in.read(buffer, sizeof(buffer))) + { + signer.update(buffer, got); // the hell does 'got' stand for? } + std::string signature = Botan::base64_encode(signer.signature(*(m_rng.get()))); addSignature(signature); } void License::createMessage(const std::string& user, const std::string& version, const std::string& type) { - lines.clear(); - lines.push_back(BEGIN_LICENSE); - lines.push_back(user); - lines.push_back(type); + m_lines.clear(); + m_lines.push_back(BEGIN_LICENSE); + m_lines.push_back(user); + m_lines.push_back(type); std::string versionstring = "Coati " + getVersion(); - lines.push_back(versionstring); + m_lines.push_back(versionstring); std::string pass9 = Botan::generate_passhash9(versionstring, *(m_rng.get())); - lines.push_back(pass9); + m_lines.push_back(pass9); } void License::writeToFile(const std::string& filename) { std::ofstream licenseFile(filename); - for(std::string line : lines) + for(std::string line : m_lines) { licenseFile << line << std::endl; } @@ -137,38 +198,44 @@ void License::writeToFile(const std::string& filename) bool License::loadFromString(const std::string& licenseText) { - lines.clear(); + m_lines.clear(); std::istringstream license(licenseText); return load(license); } bool License::load(std::istream& stream) { - lines.clear(); + m_lines.clear(); std::string line; while (getline(stream, line, '\n')) { - std::string l = trim(line); + std::string l = trimWhiteSpaces(line); if (l.size()) { - lines.push_back(l); + m_lines.push_back(l); } } - if (lines.front() != BEGIN_LICENSE) + if (m_lines.size() <= 0) + { + std::cout << "Could not read licence, possible empty string or only white spaces" << std::endl; + return false; + } + + if (m_lines.front() != BEGIN_LICENSE) { std::cout << "No License Header" << std::endl; - lines.insert(lines.begin(), BEGIN_LICENSE); + m_lines.insert(m_lines.begin(), BEGIN_LICENSE); } - if (lines.back() != END_LICENSE) + if (m_lines.back() != END_LICENSE) { std::cout << "No License Footer" << std::endl; - lines.push_back(END_LICENSE); + m_lines.push_back(END_LICENSE); } - if (lines.size() != 13) + if (m_lines.size() != 13) { return false; } @@ -178,7 +245,7 @@ bool License::load(std::istream& stream) bool License::loadFromFile(const std::string& filename) { - lines.clear(); + m_lines.clear(); std::ifstream sigfile(filename); return load(sigfile); } @@ -190,26 +257,31 @@ void License::print() void License::addSignature(const std::string& signature) { - if(lines.size() > 5) + if(m_lines.size() > 5) { std::cout << "signature already there" << std::endl; return; } - if (!signature.size()) { + if (signature.size() <= 0) + { std::cout << "signature is empty." << std::endl; return; } std::stringstream ss; - for (size_t i = 0; i < signature.size(); i++) { - if(i % 55 == 0 && i != 0) + + const int leMagicNumber = 55; // what does this number mean? signature length? + for (size_t i = 0; i < signature.size(); i++) + { + if(i % leMagicNumber == 0 && i != 0) { - lines.push_back(ss.str()); + m_lines.push_back(ss.str()); ss.str(""); } ss << signature[i]; } - lines.push_back(ss.str()); - lines.push_back(END_LICENSE); + + m_lines.push_back(ss.str()); + m_lines.push_back(END_LICENSE); // why is this done here? the function name indicates only adding of signature } bool License::isValid() const @@ -219,33 +291,46 @@ bool License::isValid() const std::cout << "No public key loaded" << std::endl; return false; } - try + try // lol, now we try to handle errors? { - + // why is this still here? if(Botan::check_passhash9("Coati "+ getVersion(), getHashLine())) { // std::cout << "Hash from Coati "+ getVersion() + " confirmed" << std::endl; } - Botan::secure_vector sig = Botan::base64_decode(getSignature()); + std::string signatureString = getSignature(); + if (signatureString.size() <= 0) + { + std::cout << "Could not read signature" << std::endl; + return false; + } + + Botan::secure_vector signature = Botan::base64_decode(signatureString); + + if (m_publicKey == NULL) + { + std::cout << "Public key is NULL" << std::endl; + return false; + } Botan::PK_Verifier verifier(*m_publicKey.get(), "EMSA4(SHA-256)"); Botan::DataSource_Memory in(getMessage()); - Botan::byte buf[4096] = {0}; - while(size_t got = in.read(buf, sizeof(buf))) + Botan::byte buffer[4096] = {0}; + while(size_t got = in.read(buffer, sizeof(buffer))) { - verifier.update(buf, got); + verifier.update(buffer, got); // what does 'got' stand for? } - const bool ok = verifier.check_signature(sig); + const bool ok = verifier.check_signature(signature); if (isExpired()) { return false; } return ok; } - catch(...) + catch(...) // invalid character, or something else... why not check what the exception is? { std::cout << "Invalid character in Licensekey" << std::endl; } @@ -277,30 +362,45 @@ std::string License::getVersion() const bool License::loadPublicKeyFromFile(const std::string& filename) { - if (!filename.empty()) { + if (!filename.empty()) + { m_publicKeyFilename = filename; } + if(boost::filesystem::exists(getPublicKeyFilename())) { Botan::RSA_PublicKey *rsaPublicKey = dynamic_cast(Botan::X509::load_key(getPublicKeyFilename())); - if (!rsaPublicKey) { + + if (!rsaPublicKey) + { std::cout << "The loaded key is not a RSA key" << std::endl; return false; } + m_publicKey = std::shared_ptr(rsaPublicKey); return true; } + return false; } bool License::loadPublicKeyFromString(const std::string& publicKey) { + if (publicKey.empty()) + { + std::cout << "Public key is empty" << std::endl; + return false; + } + Botan::DataSource_Memory in(publicKey); Botan::RSA_PublicKey *rsaPublicKey = dynamic_cast(Botan::X509::load_key(in)); - if (!rsaPublicKey) { + + if (!rsaPublicKey) + { std::cout << "The loaded key is not a RSA key" << std::endl; return false; } + m_publicKey = std::shared_ptr(rsaPublicKey); return true; } @@ -316,7 +416,7 @@ void License::setVersion(const std::string& version) std::string License::getLicenseString() const { std::stringstream license; - for(std::string line : lines) + for(std::string line : m_lines) { license << line << std::endl; } @@ -325,60 +425,111 @@ std::string License::getLicenseString() const std::string License::hashLocation(const std::string& location) { + if (m_rng == NULL || location.size() <= 0) + { + return ""; + } + return Botan::generate_passhash9(location, *(m_rng.get())); } bool License::checkLocation(const std::string& location, const std::string& hash) { + if (location.size() <= 0 || hash.size() <= 0) + { + return ""; + } + return Botan::check_passhash9(location, hash); } std::string License::getLicenseEncodedString(const std::string& applicationLocation) const { + if (applicationLocation.size() <= 0) + { + std::cout << "No application location was given" << std::endl; + return ""; + } + Botan::AutoSeeded_RNG rng; - std::vector file_contents; + std::vector fileContents; std::stringstream input(getLicenseString()); //prepare the license string to work with the botan cryptobox while(input.good()) { - Botan::byte filebuf[4096] = { 0 }; - input.read((char*)filebuf, sizeof(filebuf)); - size_t got = input.gcount(); + Botan::byte filebuffer[4096] = { 0 }; + input.read((char*)filebuffer, sizeof(filebuffer)); + size_t got = input.gcount(); // what does got stand for? - file_contents.insert(file_contents.end(), filebuf, filebuf+got); + fileContents.insert(fileContents.end(), filebuffer, filebuffer + got); + } + + if(fileContents.size() <= 0) + { + std::cout << "Failed to read licence string" << std::endl; + return ""; } - std::string ret = Botan::CryptoBox::encrypt(&file_contents[0], file_contents.size(),getEncodeKey(applicationLocation),rng); + std::string result = Botan::CryptoBox::encrypt(&fileContents[0], fileContents.size(), getEncodeKey(applicationLocation), rng); //remove Botan Cryptobox begin and end //should not be in the application settings - ret = ret.substr(40,ret.length()-78); + const int leMagicNumberA = 40; + const int leMagicNumberB = 78; - return ret; + if (result.length() < leMagicNumberA + leMagicNumberB) + { + std::cout << "Invalid result" << std::endl; + return ""; + } + + result = result.substr(leMagicNumberA, result.length() - leMagicNumberB); + + return result; } bool License::loadFromEncodedString(const std::string& encodedLicense, const std::string& applicationLocation) { - try { + if (encodedLicense.size() <= 0) + { + std::cout << "No licence string given" << std::endl; + return false; + } + + if (applicationLocation.size() <= 0) + { + std::cout << "No application location given" << std::endl; + return false; + } + + try + { //add cryptobox begin and end to loaded string std::string crypbobxInput = "-----BEGIN BOTAN CRYPTOBOX MESSAGE-----\n"; crypbobxInput += encodedLicense; crypbobxInput += "-----END BOTAN CRYPTOBOX MESSAGE-----"; //decrypt license - loadFromString(Botan::CryptoBox::decrypt(crypbobxInput,getEncodeKey(applicationLocation))); + loadFromString(Botan::CryptoBox::decrypt(crypbobxInput, getEncodeKey(applicationLocation))); // does the result of this operation matter at all? Is it supposed to throw if the input is wrong? wtf? } catch(...) { //loaded string from application settings is invalid return false; } + return true; } std::string License::getEncodeKey(const std::string applicationLocation) const { + if (applicationLocation.size() <= 0) + { + std::cout << "No application location given" << std::endl; + return ""; + } + Botan::PBKDF *pbkdf = Botan::get_pbkdf("PBKDF2(SHA-256)"); Botan::secure_vector salt = Botan::base64_decode("34zA54n60v4CxjY5n20k3J40c976n690", 32); Botan::AutoSeeded_RNG rng; diff --git a/src/lib_license/License.h b/src/lib_license/License.h index 4e1b3ba3..1012fe95 100644 --- a/src/lib_license/License.h +++ b/src/lib_license/License.h @@ -67,8 +67,8 @@ private: std::string m_version; std::string m_publicKeyFilename; std::shared_ptr m_publicKey; - std::vector lines; - std::shared_ptr m_rng; + std::vector m_lines; + std::shared_ptr m_rng; // range? const std::string KEY_FILEENDING = ".pem"; const std::string BEGIN_LICENSE = "-----BEGIN LICENSE-----";