Mercurial > trustbridge
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; |