comparison 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
comparison
equal deleted inserted replaced
760:438d7c88104f 761:49168bcb02e2
4 * This file is Free Software under the GNU GPL (v>=2) 4 * This file is Free Software under the GNU GPL (v>=2)
5 * and comes with ABSOLUTELY NO WARRANTY! 5 * and comes with ABSOLUTELY NO WARRANTY!
6 * See LICENSE.txt for details. 6 * See LICENSE.txt for details.
7 */ 7 */
8 #include "createinstallerdialog.h" 8 #include "createinstallerdialog.h"
9 #include "sslhelp.h"
10
9 #include <QDebug> 11 #include <QDebug>
10 #include <QTextEdit> 12 #include <QTextEdit>
11 #include <QDir> 13 #include <QDir>
12 #include <QPushButton> 14 #include <QPushButton>
13 #include <QGroupBox> 15 #include <QGroupBox>
14 #include <QHBoxLayout> 16 #include <QHBoxLayout>
15 #include <QVBoxLayout> 17 #include <QVBoxLayout>
16 #include <QLabel> 18 #include <QLabel>
17 #include <QFileDialog> 19 #include <QFileDialog>
20 #include <QSaveFile>
18 #include <QSettings> 21 #include <QSettings>
19 #include <QStyle> 22 #include <QStyle>
20 #include <QApplication> 23 #include <QApplication>
21 #include <QMessageBox> 24 #include <QMessageBox>
22 #include <QTemporaryDir> 25 #include <QTemporaryDir>
26
27 #include <polarssl/pk.h>
23 28
24 /* Static information used in codesigning */ 29 /* Static information used in codesigning */
25 #ifndef SIGN_HASH 30 #ifndef SIGN_HASH
26 #define SIGN_HASH "sha256" 31 #define SIGN_HASH "sha256"
27 #endif 32 #endif
29 #define SIGN_URL "https://wald.intevation.org/projects/trustbridge/" 34 #define SIGN_URL "https://wald.intevation.org/projects/trustbridge/"
30 #endif 35 #endif
31 #ifndef SIGN_PUBLISHER 36 #ifndef SIGN_PUBLISHER
32 #define SIGN_PUBLISHER "TrustBridge Test with ümlaut" 37 #define SIGN_PUBLISHER "TrustBridge Test with ümlaut"
33 #endif 38 #endif
34
35 39
36 CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) : 40 CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) :
37 QDialog(parent), 41 QDialog(parent),
38 mProgress(this), 42 mProgress(this),
39 mInstallerPath(), 43 mInstallerPath(),
226 if (options.status() != QSettings::NoError || keys.size() < 1) { 230 if (options.status() != QSettings::NoError || keys.size() < 1) {
227 showErrorMessage(tr("Folder %1 does not appear to contain a meta.ini") 231 showErrorMessage(tr("Folder %1 does not appear to contain a meta.ini")
228 .arg(binDir.path())); 232 .arg(binDir.path()));
229 return; 233 return;
230 } 234 }
231 235 /* Sign the linux installer */
232 QTemporaryDir *signedFilesDir = codesignBinaries(binDir.path() + "/windows"); 236 QDir linuxDir(binDir.path() + "/linux");
233 237 if (!linuxDir.exists()) {
234 if (!signedFilesDir) { 238 showErrorMessage(tr("Failed to find the directory for linux binaries: %s")
239 .arg(linuxDir.path()));
240 return;
241 }
242 QStringList nameFilter;
243 nameFilter << "*.sh";
244 QStringList candidates = linuxDir.entryList(nameFilter, QDir::Files | QDir::Readable);
245 if (candidates.isEmpty()) {
246 showErrorMessage(tr("Failed to find a readable *.sh file in: %s")
247 .arg(linuxDir.path()));
248 return;
249 }
250 if (candidates.size() > 1) {
251 showErrorMessage(tr("Unexpected additional .sh files in: %s")
252 .arg(linuxDir.path()));
253 return;
254 }
255 mProgress.setLabelText(tr("Signing Linux package..."));
256 mProgress.cancel();
257
258 QString outFileName = options.value("setupname", "TrustBridge-default.exe"
259 ).toString().replace(".exe", ".sh").arg(QString());
260
261 if (!appendTextSignatureToFile(linuxDir.path() + "/" + candidates.first(),
262 outDir.path() + "/" + outFileName)) {
263 qDebug() << "Failed to sign linux package.";
264 mProgress.close();
265 return;
266 }
267
268 /* The Windows installer */
269
270 mCurrentWorkingDir = codesignBinaries(binDir.path() + "/windows");
271
272 if (!mCurrentWorkingDir) {
235 /* Error messages should have been shown by the codesign function */ 273 /* Error messages should have been shown by the codesign function */
274 mProgress.close();
236 return; 275 return;
237 } 276 }
238 277
239 mProgress.setLabelText(tr("Creating NSIS package...")); 278 mProgress.setLabelText(tr("Creating NSIS package..."));
240 279
242 QStringList arguments; 281 QStringList arguments;
243 mNSISProc.setProgram("makensis"); 282 mNSISProc.setProgram("makensis");
244 mNSISProc.setProcessChannelMode(QProcess::MergedChannels); 283 mNSISProc.setProcessChannelMode(QProcess::MergedChannels);
245 mNSISProc.setWorkingDirectory(outDir.path()); 284 mNSISProc.setWorkingDirectory(outDir.path());
246 #ifdef Q_OS_WIN 285 #ifdef Q_OS_WIN
247 arguments << QString::fromLatin1("/Dfiles_dir=") + signedFilesDir->path().replace("/", "\\"); 286 arguments << QString::fromLatin1("/Dfiles_dir=") + mCurrentWorkingDir->path().replace("/", "\\");
248 arguments << "/Dpath_sep=\\"; 287 arguments << "/Dpath_sep=\\";
249 foreach (const QString &key, keys) { 288 foreach (const QString &key, keys) {
250 QString value = options.value(key, QString()).toString(); 289 QString value = options.value(key, QString()).toString();
251 if (key == "setupname") { 290 if (key == "setupname") {
252 value = value.arg(outDir.path().replace("/", "\\") + "\\"); 291 value = value.arg(outDir.path().replace("/", "\\") + "\\");
253 mInstallerPath = value; 292 mInstallerPath = value;
254 } 293 }
255 arguments << QString::fromLatin1("/D%1=%2").arg(key, value); 294 arguments << QString::fromLatin1("/D%1=%2").arg(key, value);
256 } 295 }
257 #else 296 #else
258 arguments << QString::fromLatin1("-Dfiles_dir=") + signedFilesDir->path(); 297 arguments << QString::fromLatin1("-Dfiles_dir=") + mCurrentWorkingDir->path();
259 arguments << "-Dpath_sep=/"; 298 arguments << "-Dpath_sep=/";
260 foreach (const QString &key, keys) { 299 foreach (const QString &key, keys) {
261 QString value = options.value(key, QString()).toString(); 300 QString value = options.value(key, QString()).toString();
262 if (key == "setupname") { 301 if (key == "setupname") {
263 value = value.arg(outDir.path() + "/"); 302 value = value.arg(outDir.path() + "/");
358 } 397 }
359 mProgress.cancel(); 398 mProgress.cancel();
360 return target; 399 return target;
361 } 400 }
362 401
402 bool CreateInstallerDialog::appendTextSignatureToFile(const QString& input,
403 const QString& output) {
404 QFile inFile(input);
405 pk_context pk;
406
407 pk_init(&pk);
408 int ret = pk_parse_keyfile(&pk, mCertFile->text().toLocal8Bit().constData(), "");
409
410 if (ret != 0) {
411 showErrorMessage(tr("Failed to load certificate: %1")
412 .arg(getPolarSSLErrorMsg(ret)));
413 pk_free(&pk);
414 return false;
415 }
416
417 /* Check that it is a 3072 bit RSA key as specified */
418 if (!pk.pk_info || pk_get_size(&pk) != 3072 ||
419 pk.pk_info->type != POLARSSL_PK_RSA) {
420 qDebug() << pk.pk_info->type << "type";
421 qDebug() << POLARSSL_PK_RSA << "rsa";
422 qDebug() << "size " << pk_get_size(&pk);
423 showErrorMessage(tr("Only 3072 bit RSA keys are supported by the current format."));
424 pk_free(&pk);
425 return false;
426 }
427
428 if (!inFile.open(QIODevice::ReadOnly)) {
429 showErrorMessage(tr("Failed to open input file: %1").arg(inFile.fileName()));
430 pk_free(&pk);
431 return false;
432 }
433
434 const QByteArray inputContent = inFile.readAll(); // Memory is cheap :)
435 inFile.close();
436
437 if (inputContent.isEmpty()) {
438 showErrorMessage(tr("Failed to read input file: %1").arg(inFile.fileName()));
439 pk_free(&pk);
440 return false;
441 }
442
443 const QByteArray signature = rsaSignSHA256Hash(sha256sum(inputContent), &pk);
444
445 pk_free(&pk);
446 if (signature.size() != 3072 / 8) {
447 qDebug() << "Signature creation returned signature of invalid size.";
448 return false;
449 }
450
451 QSaveFile outFile(output);
452 outFile.open(QIODevice::WriteOnly);
453 outFile.write(inputContent);
454 outFile.write("\r\nS:");
455 outFile.write(signature.toBase64());
456
457 return outFile.commit();
458 }
459
363 FinishedDialog::FinishedDialog(QDialog *parent, 460 FinishedDialog::FinishedDialog(QDialog *parent,
364 QString msg, QString details, bool isErr): 461 QString msg, QString details, bool isErr):
365 QDialog(parent) 462 QDialog(parent)
366 { 463 {
367 QVBoxLayout *topLayout = new QVBoxLayout; 464 QVBoxLayout *topLayout = new QVBoxLayout;

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