File indexing completed on 2024-05-05 04:49:51

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Jakob Kummerow <jakob.kummerow@gmail.com>                         *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "signer.h"
0018 
0019 #include "../../shared/ScriptUpdaterStatic.h"
0020 
0021 #include <QFile>
0022 #include <QTimer>
0023 #include <iostream>
0024 
0025 #include <pwd.h>
0026 
0027 signer::signer()
0028 {
0029 }
0030 
0031 signer::~signer()
0032 {}
0033 
0034 bool signer::setParams(int argc, char** argv) 
0035 {
0036     if (argc > 1) {
0037 
0038         // first code block: generate key pair and save to local files
0039         if ( strcmp( argv[1], "keygen" ) == 0 ) {
0040             QTimer::singleShot( 0, this, SLOT(keygen()) );
0041             return 1;
0042 
0043         // second code block: sign a given file
0044         } else if ( strcmp( argv[1], "sign" ) == 0 ) {
0045             m_privkeyFilename = argv[2];
0046             QTimer::singleShot( 0, this, SLOT(signFile()) );
0047 
0048             return 1;
0049         // third code block: check a given file
0050         } else if ( strcmp( argv[1], "check" ) == 0 ) {
0051             m_pubkeyFilename = argv[2];
0052             QTimer::singleShot( 0, this, SLOT(checkSignature()) );
0053             return 1;
0054         }
0055     } 
0056     std::cout << "Usage: \n"
0057     "\"" << argv[0] << " keygen\" to create a new key pair and store it in the \n"
0058     "\tcurrent directory\n"
0059     
0060     "\"" << argv[0] << " sign <privateKey>\" to create a signature for both \n"
0061     "\t'" << archiveFilename.toLatin1().data() << "' and '" << versionFilename.toLatin1().data() << "' and store it as '" << signatureFilename.toLatin1().data() << "', using \n"
0062     "\tthe private key file <privateKey>.\n"
0063     
0064     "\"" << argv[0] << " check <publicKey>\" to check if the signature found in \n"
0065     "\tthe file '" << signatureFilename.toLatin1().data() << "' matches both '" << archiveFilename.toLatin1().data() << "' and '" << versionFilename.toLatin1().data() << "', \n"
0066     "\tusing the public key file <publicKey>\n";
0067     return false;
0068 }
0069 
0070 void 
0071 signer::keygen()
0072 {
0073     QCA::Initializer init;
0074     // only start this "loop" so that we can jump to the end of it using "continue"
0075     for ( int temp = 0; temp < 1; temp++ ) {
0076         if( !QCA::isSupported( "pkey" ) || !QCA::PKey::supportedIOTypes().contains( QCA::PKey::RSA ) )
0077         {
0078             std::cout << "RSA not supported on this system!\n";
0079             continue;
0080         }
0081         QCA::PrivateKey seckey = QCA::KeyGenerator().createRSA( 2048 );
0082         if( seckey.isNull() ) {
0083             std::cout << "Failed to make private RSA key!\n";
0084             continue;
0085         }
0086         QCA::PublicKey pubkey = seckey.toPublicKey();
0087         QCA::SecureArray passPhrase( getpass( "Please enter a passphrase for the new private key: " ) );
0088         QCA::SecureArray passPhraseConfirm( getpass( "Please confirm the passphrase: " ) );
0089         if ( passPhrase != passPhraseConfirm ) 
0090         {
0091             std::cout << "Passphrases do not match, aborting.\n";
0092             continue;
0093         }
0094         seckey.toPEMFile( "privkey.pem", passPhrase );
0095         pubkey.toPEMFile( "pubkey.pem" );
0096         std::cout << "Keys generated and saved to file; have fun!\n";
0097     }
0098     this->thread()->quit();
0099 }
0100 
0101 void 
0102 signer::signFile()
0103 {
0104     std::cout << "signing file " << archiveFilename.toLatin1().data() << "...\n";
0105     QCA::Initializer init;
0106     // only start this "loop" so that we can jump to the end of it using "continue"
0107     for ( int temp = 0; temp < 1; temp++ ) {
0108         QCA::SecureArray passPhrase( getpass("Please enter the passphrase for the private key: ") );
0109         QCA::ConvertResult conversionResult;
0110         QCA::PrivateKey seckey = QCA::PrivateKey::fromPEMFile( m_privkeyFilename, passPhrase, &conversionResult );
0111         if (! ( QCA::ConvertGood == conversionResult ) ) {
0112             std::cout << "Failed to read private key!\n";
0113             continue;
0114         }
0115         QFile file( archiveFilename );
0116         if ( !file.open( QIODevice::ReadOnly ) ) {
0117             std::cout << "Failed to open archive file for reading!\n";
0118             continue;
0119         }
0120         QCA::Hash hash( "sha1" );
0121         hash.update( &file );
0122         file.close();
0123         QFile versionFile( versionFilename );
0124         if ( !versionFile.open( QIODevice::ReadOnly ) ) {
0125             std::cout << "failed to open version file for reading!\n";
0126             continue;
0127         }
0128         QCA::Hash versionHash( "sha1" );
0129         versionHash.update( &versionFile );
0130         versionFile.close();
0131         seckey.startSign( QCA::EMSA3_SHA1 );
0132         seckey.update( hash.final() );
0133         seckey.update( versionHash.final() );
0134         QByteArray signature = seckey.signature();
0135         QFile sigFile( signatureFilename );
0136         if ( !sigFile.open(QIODevice::WriteOnly) ) {
0137             std::cout << "Failed to open signature file for writing!\n";
0138             continue;
0139         }
0140         sigFile.write( signature.toBase64() );
0141         sigFile.close();
0142         std::cout << "Signature written to " << sigFile.fileName().toLatin1().data() << "\n";
0143     }
0144     this->thread()->quit();
0145 }
0146 
0147 void 
0148 signer::checkSignature()
0149 {
0150     std::cout << "checking file " << archiveFilename.toLatin1().data() << "...\n";
0151     QCA::Initializer init;
0152     // only start this "loop" so that we can jump to the end of it using "continue"
0153     for ( int temp = 0; temp < 1; temp++ ) {
0154         QFile pubkeyfile( m_pubkeyFilename );
0155         if ( !pubkeyfile.open( QIODevice::ReadOnly ) ) 
0156         {
0157             std::cout << "Failed to open public key file!\n";
0158             continue;
0159         }
0160         QString pubkeystring = pubkeyfile.readAll();
0161         pubkeyfile.close();
0162         QCA::ConvertResult conversionResult;
0163         QCA::PublicKey pubkey = QCA::PublicKey::fromPEM( pubkeystring, &conversionResult );
0164         if ( !( QCA::ConvertGood == conversionResult ) )
0165         {
0166             std::cout << "Failed to read public key!\n";
0167             continue;
0168         }
0169         QFile file( archiveFilename );
0170         if ( !file.open( QIODevice::ReadOnly ) ) {
0171             std::cout << "Failed to open archive file for reading!\n";
0172             continue;
0173         }
0174         QCA::Hash hash( "sha1" );
0175         hash.update( &file );
0176         file.close();
0177         QFile versionFile( versionFilename );
0178         if ( !versionFile.open( QIODevice::ReadOnly ) ) {
0179             std::cout << "Failed to open version file for reading!\n";
0180             continue;
0181         }
0182         QCA::Hash versionHash( "sha1" );
0183         versionHash.update( &versionFile );
0184         versionFile.close();
0185         QFile sigFile( signatureFilename );
0186         if ( !sigFile.open( QIODevice::ReadOnly ) )
0187         {
0188             std::cout << "Failed to open signature file for reading!\n";
0189             continue;
0190         }
0191         QByteArray signature = QByteArray::fromBase64( sigFile.readAll() );
0192         pubkey.startVerify( QCA::EMSA3_SHA1 );
0193         pubkey.update( hash.final() );
0194         pubkey.update( versionHash.final() );
0195         if ( !pubkey.validSignature( signature ) ) 
0196         {
0197             std::cout << "Invalid signature!\n";
0198             continue;
0199         }
0200         std::cout << "Signature verified :-)\n";
0201     }
0202     this->thread()->quit();
0203 }
0204