diff ui/createinstallerdialog.cpp @ 761:49168bcb02e2

(Issue55) Sign a linux installer This uses the same RSA key that is used for Windows codesigning to create an additonal S:<base64encodedsignature> line. Signed is everything up to the last \r\n before the S: line. The hash algorithm is sha256
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 07 Jul 2014 18:52:48 +0200
parents 6c4fff146999
children aec00847d86d
line wrap: on
line diff
--- a/ui/createinstallerdialog.cpp	Mon Jul 07 18:50:06 2014 +0200
+++ b/ui/createinstallerdialog.cpp	Mon Jul 07 18:52:48 2014 +0200
@@ -6,6 +6,8 @@
  * See LICENSE.txt for details.
  */
 #include "createinstallerdialog.h"
+#include "sslhelp.h"
+
 #include <QDebug>
 #include <QTextEdit>
 #include <QDir>
@@ -15,12 +17,15 @@
 #include <QVBoxLayout>
 #include <QLabel>
 #include <QFileDialog>
+#include <QSaveFile>
 #include <QSettings>
 #include <QStyle>
 #include <QApplication>
 #include <QMessageBox>
 #include <QTemporaryDir>
 
+#include <polarssl/pk.h>
+
 /* Static information used in codesigning */
 #ifndef SIGN_HASH
 #define SIGN_HASH "sha256"
@@ -32,7 +37,6 @@
 #define SIGN_PUBLISHER "TrustBridge Test with ümlaut"
 #endif
 
-
 CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) :
     QDialog(parent),
     mProgress(this),
@@ -228,11 +232,46 @@
                 .arg(binDir.path()));
         return;
     }
+    /* Sign the linux installer */
+    QDir linuxDir(binDir.path() + "/linux");
+    if (!linuxDir.exists()) {
+        showErrorMessage(tr("Failed to find the directory for linux binaries: %s")
+                .arg(linuxDir.path()));
+        return;
+    }
+    QStringList nameFilter;
+    nameFilter << "*.sh";
+    QStringList candidates = linuxDir.entryList(nameFilter, QDir::Files | QDir::Readable);
+    if (candidates.isEmpty()) {
+        showErrorMessage(tr("Failed to find a readable *.sh file in: %s")
+                .arg(linuxDir.path()));
+        return;
+    }
+    if (candidates.size() > 1) {
+        showErrorMessage(tr("Unexpected additional .sh files in: %s")
+                .arg(linuxDir.path()));
+        return;
+    }
+    mProgress.setLabelText(tr("Signing Linux package..."));
+    mProgress.cancel();
 
-    QTemporaryDir *signedFilesDir = codesignBinaries(binDir.path() + "/windows");
+    QString outFileName = options.value("setupname", "TrustBridge-default.exe"
+            ).toString().replace(".exe", ".sh").arg(QString());
 
-    if (!signedFilesDir) {
+    if (!appendTextSignatureToFile(linuxDir.path() + "/" + candidates.first(),
+                outDir.path() + "/" + outFileName)) {
+        qDebug() << "Failed to sign linux package.";
+        mProgress.close();
+        return;
+    }
+
+    /* The Windows installer */
+
+    mCurrentWorkingDir = codesignBinaries(binDir.path() + "/windows");
+
+    if (!mCurrentWorkingDir) {
         /* Error messages should have been shown by the codesign function */
+        mProgress.close();
         return;
     }
 
@@ -244,7 +283,7 @@
     mNSISProc.setProcessChannelMode(QProcess::MergedChannels);
     mNSISProc.setWorkingDirectory(outDir.path());
 #ifdef Q_OS_WIN
-    arguments << QString::fromLatin1("/Dfiles_dir=") + signedFilesDir->path().replace("/", "\\");
+    arguments << QString::fromLatin1("/Dfiles_dir=") + mCurrentWorkingDir->path().replace("/", "\\");
     arguments << "/Dpath_sep=\\";
     foreach (const QString &key, keys) {
         QString value = options.value(key, QString()).toString();
@@ -255,7 +294,7 @@
         arguments << QString::fromLatin1("/D%1=%2").arg(key, value);
     }
 #else
-    arguments << QString::fromLatin1("-Dfiles_dir=") + signedFilesDir->path();
+    arguments << QString::fromLatin1("-Dfiles_dir=") + mCurrentWorkingDir->path();
     arguments << "-Dpath_sep=/";
     foreach (const QString &key, keys) {
         QString value = options.value(key, QString()).toString();
@@ -360,6 +399,64 @@
     return target;
 }
 
+bool CreateInstallerDialog::appendTextSignatureToFile(const QString& input,
+                                                 const QString& output) {
+    QFile inFile(input);
+    pk_context pk;
+
+    pk_init(&pk);
+    int ret = pk_parse_keyfile(&pk, mCertFile->text().toLocal8Bit().constData(), "");
+
+    if (ret != 0) {
+        showErrorMessage(tr("Failed to load certificate: %1")
+                .arg(getPolarSSLErrorMsg(ret)));
+        pk_free(&pk);
+        return false;
+    }
+
+    /* Check that it is a 3072 bit RSA key as specified */
+    if (!pk.pk_info || pk_get_size(&pk) != 3072 ||
+            pk.pk_info->type != POLARSSL_PK_RSA) {
+        qDebug() << pk.pk_info->type << "type";
+        qDebug() << POLARSSL_PK_RSA << "rsa";
+        qDebug() << "size " << pk_get_size(&pk);
+        showErrorMessage(tr("Only 3072 bit RSA keys are supported by the current format."));
+        pk_free(&pk);
+        return false;
+    }
+
+    if (!inFile.open(QIODevice::ReadOnly)) {
+        showErrorMessage(tr("Failed to open input file: %1").arg(inFile.fileName()));
+        pk_free(&pk);
+        return false;
+    }
+
+    const QByteArray inputContent = inFile.readAll(); // Memory is cheap :)
+    inFile.close();
+
+    if (inputContent.isEmpty()) {
+        showErrorMessage(tr("Failed to read input file: %1").arg(inFile.fileName()));
+        pk_free(&pk);
+        return false;
+    }
+
+    const QByteArray signature = rsaSignSHA256Hash(sha256sum(inputContent), &pk);
+
+    pk_free(&pk);
+    if (signature.size() != 3072 / 8) {
+        qDebug() << "Signature creation returned signature of invalid size.";
+        return false;
+    }
+
+    QSaveFile outFile(output);
+    outFile.open(QIODevice::WriteOnly);
+    outFile.write(inputContent);
+    outFile.write("\r\nS:");
+    outFile.write(signature.toBase64());
+
+    return outFile.commit();
+}
+
 FinishedDialog::FinishedDialog(QDialog *parent,
         QString msg, QString details, bool isErr):
     QDialog(parent)

http://wald.intevation.org/projects/trustbridge/