Commit d466e45f authored by Jan Möbius's avatar Jan Möbius
Browse files

Reworkedlicense system

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@10805 383ad7c9-94d9-4d36-a494-682f7c89f535
parent be5b4b8f
......@@ -40,6 +40,26 @@
* *
\*===========================================================================*/
/*
License File format:
0: Signature over all other entries
1: Expiry date
2: Plugin filename
3: coreHash
4: pluginHash
5: cpuHash
6: windowsProductId (Windows only otherwise filled with "-" before hashing)
7..?: mac Address hashes
*/
#include <OpenFlipper/LicenseManager/LicenseManager.hh>
#include <OpenFlipper/common/GlobalOptions.hh>
#include <QFile>
......@@ -136,35 +156,34 @@ bool LicenseManager::authenticate() {
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
foreach ( QNetworkInterface netInterface, interfaces ) {
if ( !( netInterface.flags() & QNetworkInterface::IsLoopBack ) ) {
std::cerr << "Got MAC: " << netInterface.humanReadableName().toStdString() << " " << netInterface.hardwareAddress().toStdString() << std::endl;
std::cerr << "Flags: " << netInterface.flags() << std::endl;
if ( netInterface.hardwareAddress().count(":") == 5 ) {
macHashes.push_back(netInterface.hardwareAddress().toAscii().toUpper());
} else {
std::cerr << "Skipped non ethernet mac" << std::endl;
}
// Ignore loopback interfaces
if ( ( netInterface.flags() & QNetworkInterface::IsLoopBack ) ) {
std::cerr << "Loopback" << std::endl;
continue;
}
// Ignore non ethernet macs
if ( netInterface.hardwareAddress().count(":") != 5 ) {
continue;
}
// Cleanup mac adress
QString currentMac = netInterface.hardwareAddress().toAscii().toUpper();
currentMac = currentMac.remove(":");
mac = mac + netInterface.hardwareAddress().remove(":");
macHashes.push_back(currentMac);
}
std::cerr << "Got " << macHashes.size() << " macs" << std::endl;
// cleanup the list from duplicates (virtual interfaces on windows connected to an existing device ... )
macHashes.removeDuplicates();
std::cerr << "Got " << macHashes.size() << " macs after cleanup" << std::endl;
for (uint i = 0 ; i < macHashes.size(); ++i ) {
std::cerr << "Got mac : " << macHashes[i].toStdString() << std::endl;
}
QString macHash = QCryptographicHash::hash ( mac.toAscii() , QCryptographicHash::Sha1 ).toHex();
// generate hashes
for (int i = 0 ; i < macHashes.size(); ++i )
macHashes[i] = QCryptographicHash::hash ( macHashes[i].toAscii() , QCryptographicHash::Sha1 ).toHex();
// ===============================================================================================
// Compute hash of processor information
......@@ -217,6 +236,22 @@ bool LicenseManager::authenticate() {
QString cpuHash = QCryptographicHash::hash ( processor.toAscii() , QCryptographicHash::Sha1 ).toHex();
// ===============================================================================================
// Get windows product id
// ===============================================================================================
QString productId = "-";
#ifdef WIN32
QSettings registryProduct("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion", QSettings::NativeFormat);
productId = registryProduct.value( "ProductId", "Unknown" ).toString();
std::cerr<< "Got product id : " << productHash.toStdString() << std::endl;
#endif
QString productHash = QCryptographicHash::hash ( productId.toAscii() , QCryptographicHash::Sha1 ).toHex();
// ===============================================================================================
// Check License or generate request
......@@ -237,51 +272,64 @@ bool LicenseManager::authenticate() {
QString licenseContents = file.readAll();
QStringList elements = licenseContents.split('\n',QString::SkipEmptyParts);
// simplify license file entries
for ( int i = 0 ; i < elements.size(); ++i )
elements[i] = elements[i].simplified();
if ( elements.size() != 7 ) {
QString sizeMismatchMessage = "The license file for plugin \"" + name() + "\" is invalid!\n";
sizeMismatchMessage += "License File contains " + QString::number(elements.size()) + " lines but it should contain exactly 7!\n";
sizeMismatchMessage += "Read data: \n";
sizeMismatchMessage += "======================================== \n";
for ( int i = 0 ; i < elements.size(); ++i )
sizeMismatchMessage += elements[i] + "\n";
sizeMismatchMessage += "======================================== \n";
QMessageBox::critical(0,tr("License file size invalid"),sizeMismatchMessage );
} else {
// Check signature of license file
QString license = saltPre + elements[0] + elements[1] + elements[2] + elements[3] + elements[4] + elements[5] + saltPost;
QString licenseHash = QCryptographicHash::hash ( license.toAscii() , QCryptographicHash::Sha1 ).toHex();
QDate currentDate = QDate::currentDate();
QDate expiryDate = QDate::fromString(elements[5],Qt::ISODate);
if ( licenseHash != elements[6] ) {
authstring_ += tr("License Error: The license file signature for plugin \"") + name() + tr("\" is invalid!\n\n");
} else if ( elements[0] != pluginFileName() ) {
authstring_ += tr("License Error: The license file contains plugin name\"") + elements[0] + tr("\" but this is plugin \"") + name() + "\"!\n\n";
} else if ( elements[1] != coreHash ) {
authstring_ += tr("License Error: The license file for plugin \"") + name() + tr("\" is invalid for the currently running OpenFlipper Core!\n\n");
} else if ( elements[2] != pluginHash ) {
authstring_ += tr("License Error: The plugin \"") + name() + tr("\" is a different version than specified in license file!\n\n");
} else if ( elements[3] != macHash ) {
authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed Network?)!\n\n";
} else if ( elements[4] != cpuHash ) {
authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed CPU?)!\n\n";
} else if ( currentDate > expiryDate ) {
authstring_ += tr("License Error: The license for plugin \"") + name() + tr("\" has expired on ") + elements[1] + "!\n\n";
// Check the signature of the file (excluding first element as this is the signature itself)
QString license = saltPre;
for ( int i = 1 ; i < elements.size(); ++i )
license += elements[i];
license += saltPost;
QString licenseHash = QCryptographicHash::hash ( license.toAscii() , QCryptographicHash::Sha1 ).toHex();
bool signatureOk = licenseHash == elements[0];
// Check expiry date
QDate currentDate = QDate::currentDate();
QDate expiryDate = QDate::fromString(elements[1],Qt::ISODate);
bool expired = (currentDate > expiryDate);
// Get number of available mac adresses
QStringList licensedMacs;
for ( int i = 7 ; i < elements.size(); ++i ) {
std::cerr << "MacHash: " << elements[i].toStdString() << std::endl;
licensedMacs.push_back(elements[i]);
}
bool macFound = false;
for ( int i = 0; i < macHashes.size(); ++i ) {
if ( licensedMacs.contains(macHashes[i]) ) {
macFound = true;
std::cerr << "Found mac" << std::endl;
} else {
authenticated_ = true;
std::cerr << "not matching Mac!" << std::endl;
}
// Clean it on success
if ( authenticated_ )
authstring_ = "";
}
if ( !signatureOk ) {
authstring_ += tr("License Error: The license file signature for Plugin \"") + name() + tr("\" is invalid!\n\n");
} else if ( expired ) {
authstring_ += tr("License Error: The license for plugin \"") + name() + tr("\" has expired on ") + elements[1] + "!\n\n";
} else if ( elements[2] != pluginFileName() ) {
authstring_ += tr("License Error: The license file contains plugin name\"") + elements[2] + tr("\" but this is plugin \"") + name() + "\"!\n\n";
} else if ( elements[3] != coreHash ) {
authstring_ += tr("License Error: The license file for plugin \"") + name() + tr("\" is invalid for the currently running OpenFlipper Core!\n\n");
} else if ( elements[4] != pluginHash ) {
authstring_ += tr("License Error: The plugin \"") + name() + tr("\" is a different version than specified in license file!\n\n");
} else if ( elements[5] != cpuHash ) {
authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed CPU?)!\n\n";
} else if ( elements[6] != productHash ) {
authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed OS?)!\n\n";
} else if ( !macFound ) {
authstring_ += "License Error: The plugin \"" + name() + "\" is not allowed to run on the current system (Changed Network?)!\n\n";
} else {
authenticated_ = true;
}
// Clean it on success
if ( authenticated_ )
authstring_ = "";
}
if ( authenticated_ ) {
......@@ -294,10 +342,13 @@ bool LicenseManager::authenticate() {
authstring_ += pluginFileName() +"\n";
authstring_ += coreHash +"\n";
authstring_ += pluginHash +"\n";
authstring_ += macHash +"\n";
authstring_ += cpuHash +"\n";
authstring_ += productHash +"\n";
for ( int i = 0 ; i < macHashes.size(); ++i )
authstring_ += macHashes[i] +"\n";
QString keyRequest = saltPre + pluginFileName() + coreHash + pluginHash + macHash + cpuHash + saltPost;
QString keyRequest = saltPre + pluginFileName() + coreHash + pluginHash + cpuHash + productHash + macHashes.join("") + saltPost;
QString requestSig = QCryptographicHash::hash ( keyRequest.toAscii() , QCryptographicHash::Sha1 ).toHex();
authstring_ += requestSig + "\n";
......
......@@ -6,16 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>312</height>
<width>812</width>
<height>727</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
......@@ -33,42 +33,118 @@
</item>
</layout>
</item>
<item row="1" column="0" rowspan="2" colspan="2">
<widget class="QPushButton" name="splitButton">
<property name="text">
<string>Split</string>
</property>
</widget>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QPushButton" name="splitButton">
<property name="text">
<string>Split</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="Line" name="line_2">
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="validButton">
<property name="text">
<string>Valid</string>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
</widget>
<item row="0" column="1">
<widget class="QLineEdit" name="fileNameBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Core Hash</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="coreHashBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Plugin Hash</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="pluginHashBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Cpu Hash</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="cpuHashBox"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Filename</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="productIDBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>ProductID</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Signature</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="signatureBox"/>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Mac Hashes</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="macHashBox"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
......@@ -139,7 +215,7 @@
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_2">
......@@ -167,14 +243,14 @@
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<item>
<widget class="QPushButton" name="generateButton">
<property name="text">
<string>Generate</string>
......
......@@ -48,14 +48,21 @@
#include "salt.hh"
KeyGenWidget::KeyGenWidget(QMainWindow *parent)
: QMainWindow(parent)
: QMainWindow(parent),
valid_(false)
{
setupUi(this);
connect(generateButton,SIGNAL(clicked()),this,SLOT(slotGenerateButton()));
connect(splitButton,SIGNAL(clicked()),this,SLOT(slotSplit()));
connect(validButton,SIGNAL(clicked()),this,SLOT(slotValid()));
connect(requestData,SIGNAL(textChanged()),this,SLOT(slotAnalyze()));
// connect spinboxes forexpiry date
connect(days ,SIGNAL(valueChanged(int)),this,SLOT(slotDate()));
connect(months,SIGNAL(valueChanged(int)),this,SLOT(slotDate()));
connect(years ,SIGNAL(valueChanged(int)),this,SLOT(slotDate()));
// Automatically set expire date to current date
// For security reasons no default span is set here!
......@@ -63,7 +70,7 @@ KeyGenWidget::KeyGenWidget(QMainWindow *parent)
}
void KeyGenWidget::slotValid() {
void KeyGenWidget::slotDate() {
QDate today = QDate::currentDate();
today = today.addDays(days->value());
today = today.addMonths(months->value());
......@@ -72,6 +79,107 @@ void KeyGenWidget::slotValid() {
expires->setDate(today);
}
void KeyGenWidget::slotAnalyze() {
// Convert to text and split to elements
QString inputData = requestData->toPlainText();
QStringList data = inputData.split('\n',QString::SkipEmptyParts);
// This is never avalid request!
if ( data.size() < 6 ) {
QPalette p = requestData->palette();
p.setColor( QPalette::Base, QColor(255,0,0) );
requestData->setPalette(p);
valid_ = false;
return;
} else {
QPalette p = requestData->palette();
p.setColor( QPalette::Base, QColor(255,255,255) );
requestData->setPalette(p);
}
// Get strings
QString name = data[0].simplified();
QString coreHash = data[1].simplified();
QString pluginHash = data[2].simplified();
QString cpuHash = data[3].simplified();
QString productHash = data[4].simplified();
QStringList macHashes;
for ( int i = 5 ; i < data.size() - 1; ++i)
macHashes.push_back(data[i].simplified());
QString requestSig = data[data.size() - 1].simplified();
fileNameBox->setText(name);
coreHashBox->setText(coreHash);
pluginHashBox->setText(pluginHash);
cpuHashBox->setText(cpuHash);
productIDBox->setText(productHash);
macHashBox->setText(macHashes.join("\n"));
signatureBox->setText(requestSig);
// Get the salts
QString saltPre;
ADD_SALT_PRE(saltPre);
QString saltPost;
ADD_SALT_POST(saltPost);
QString keyRequest = saltPre + name + coreHash + pluginHash + cpuHash + productHash + macHashes.join("") + saltPost;
QString requestSigCheck = QCryptographicHash::hash ( keyRequest.toAscii() , QCryptographicHash::Sha1 ).toHex();
if ( requestSig != requestSigCheck ) {
QPalette p = signatureBox->palette();
p.setColor( QPalette::Base, QColor(255,0,0) );
signatureBox->setPalette(p);
valid_ = false;
return;
} else {
QPalette p = signatureBox->palette();
p.setColor( QPalette::Base, QColor(0,255,0) );
signatureBox->setPalette(p);
}
QString expiryDate = expires->date().toString(Qt::ISODate);
license_ = "";
// Add basic hashes
license_ += expiryDate + "\n";
license_ += name + "\n";
license_ += coreHash + "\n";
license_ += pluginHash + "\n";
license_ += cpuHash + "\n";
license_ += productHash + "\n";
license_ += macHashes.join("\n") + "\n";
QString licenseTmp = saltPre + expiryDate + name + coreHash + pluginHash + cpuHash + productHash + macHashes.join("") + saltPost;
QString licenseHash = QCryptographicHash::hash ( licenseTmp.toAscii() , QCryptographicHash::Sha1 ).toHex();
std::cerr << "license : " << licenseTmp.toStdString() << std::endl;
std::cerr << "hash : " << licenseHash.toStdString() << std::endl;
// Prepend signature
license_ = licenseHash + "\n" + license_;
std::cerr << "Full license : " << license_.toStdString() << std::endl;
valid_ = true;
licenseFileName_ = name;
}
void KeyGenWidget::slotSplit() {
// Get request data
......@@ -86,81 +194,29 @@ void KeyGenWidget::slotSplit() {
}
KeyGenWidget::~KeyGenWidget() {
}
void KeyGenWidget::slotGenerateButton() {
QString inputData = requestData->toPlainText();
QStringList data = inputData.split('\n',QString::SkipEmptyParts);
if ( ! valid_ ) {
std::cerr << "Invalid! " << std::endl;
return;
}
std::cerr << "Writing License file to output : " << licenseFileName_.toStdString() << std::endl;
QFile outFile(licenseFileName_ + ".lic");
if ( data.size() != 6) {
QMessageBox::critical(this,tr("Wrong request data"),tr("The request has to contain 6 lines of data"));
if (!outFile.open(QIODevice::WriteOnly|QIODevice::Text)) {
QMessageBox::critical(this,tr("Unable to open file"),tr("Unable to Open output File"));
return;
} else {
}
// Clean strings
QString name = data[0].simplified();
QString coreHash = data[1].simplified();
QString pluginHash = data[2].simplified();
QString macHash = data[3].simplified();
QString cpuHash = data[4].simplified();
QString requestSig = data[5].simplified();
QString expiryDate = expires->date().toString(Qt::ISODate);
std::cerr << "Generating key for Plugin : " << name.toStdString() << std::endl;
std::cerr << "Core Hash : " << coreHash.toStdString() << std::endl;
std::cerr << "Plugin Hash : " << pluginHash.toStdString() << std::endl;
std::cerr << "macHash is : " << macHash.toStdString() << std::endl;
std::cerr << "cpuHash is : " << cpuHash.toStdString() << std::endl;
std::cerr << "expiryDate is : " << expiryDate.toStdString() << std::endl;
std::cerr << "requestSignature is : " << requestSig.toStdString() << std::endl;