/****************************************************************************************
 * Copyright (c) 2009 Jakob Kummerow <jakob.kummerow@gmail.com>                         *
 *                                                                                      *
 * This program is free software; you can redistribute it and/or modify it under        *
 * the terms of the GNU General Public License as published by the Free Software        *
 * Foundation; either version 2 of the License, or (at your option) any later           *
 * version.                                                                             *
 *                                                                                      *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
 *                                                                                      *
 * You should have received a copy of the GNU General Public License along with         *
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
 ****************************************************************************************/

#include "signer.h"

#include "../../shared/ScriptUpdaterStatic.h"

#include <QFile>
#include <QTimer>
#include <iostream>

#include <pwd.h>

signer::signer()
{
}

signer::~signer()
{}

bool signer::setParams(int argc, char** argv) 
{
	if (argc > 1) {

		// first code block: generate key pair and save to local files
		if ( strcmp( argv[1], "keygen" ) == 0 ) {
			QTimer::singleShot( 0, this, SLOT(keygen()) );
			return 1;

		// second code block: sign a given file
		} else if ( strcmp( argv[1], "sign" ) == 0 ) {
			m_privkeyFilename = argv[2];
			QTimer::singleShot( 0, this, SLOT(signFile()) );

			return 1;
		// third code block: check a given file
		} else if ( strcmp( argv[1], "check" ) == 0 ) {
			m_pubkeyFilename = argv[2];
			QTimer::singleShot( 0, this, SLOT(checkSignature()) );
			return 1;
		}
	} 
	std::cout << "Usage: \n"
	"\"" << argv[0] << " keygen\" to create a new key pair and store it in the \n"
	"\tcurrent directory\n"
	
	"\"" << argv[0] << " sign <privateKey>\" to create a signature for both \n"
	"\t'" << archiveFilename.toLatin1().data() << "' and '" << versionFilename.toLatin1().data() << "' and store it as '" << signatureFilename.toLatin1().data() << "', using \n"
	"\tthe private key file <privateKey>.\n"
	
	"\"" << argv[0] << " check <publicKey>\" to check if the signature found in \n"
	"\tthe file '" << signatureFilename.toLatin1().data() << "' matches both '" << archiveFilename.toLatin1().data() << "' and '" << versionFilename.toLatin1().data() << "', \n"
	"\tusing the public key file <publicKey>\n";
	return false;
}

void 
signer::keygen()
{
	QCA::Initializer init;
	// only start this "loop" so that we can jump to the end of it using "continue"
	for ( int temp = 0; temp < 1; temp++ ) {
		if( !QCA::isSupported( "pkey" ) || !QCA::PKey::supportedIOTypes().contains( QCA::PKey::RSA ) )
		{
			std::cout << "RSA not supported on this system!\n";
			continue;
		}
		QCA::PrivateKey seckey = QCA::KeyGenerator().createRSA( 2048 );
        if( seckey.isNull() ) {
            std::cout << "Failed to make private RSA key!\n";
            continue;
        }
		QCA::PublicKey pubkey = seckey.toPublicKey();
		QCA::SecureArray passPhrase( getpass( "Please enter a passphrase for the new private key: " ) );
		QCA::SecureArray passPhraseConfirm( getpass( "Please confirm the passphrase: " ) );
		if ( passPhrase != passPhraseConfirm ) 
		{
			std::cout << "Passphrases do not match, aborting.\n";
			continue;
		}
		seckey.toPEMFile( "privkey.pem", passPhrase );
		pubkey.toPEMFile( "pubkey.pem" );
		std::cout << "Keys generated and saved to file; have fun!\n";
	}
	this->thread()->quit();
}

void 
signer::signFile()
{
	std::cout << "signing file " << archiveFilename.toLatin1().data() << "...\n";
	QCA::Initializer init;
	// only start this "loop" so that we can jump to the end of it using "continue"
	for ( int temp = 0; temp < 1; temp++ ) {
		QCA::SecureArray passPhrase( getpass("Please enter the passphrase for the private key: ") );
		QCA::ConvertResult conversionResult;
        QCA::PrivateKey seckey = QCA::PrivateKey::fromPEMFile( m_privkeyFilename, passPhrase, &conversionResult );
        if (! ( QCA::ConvertGood == conversionResult ) ) {
            std::cout << "Failed to read private key!\n";
			continue;
        }
		QFile file( archiveFilename );
		if ( !file.open( QIODevice::ReadOnly ) ) {
			std::cout << "Failed to open archive file for reading!\n";
			continue;
		}
		QCA::Hash hash( "sha1" );
		hash.update( &file );
		file.close();
		QFile versionFile( versionFilename );
		if ( !versionFile.open( QIODevice::ReadOnly ) ) {
			std::cout << "failed to open version file for reading!\n";
			continue;
		}
		QCA::Hash versionHash( "sha1" );
		versionHash.update( &versionFile );
		versionFile.close();
		seckey.startSign( QCA::EMSA3_SHA1 );
		seckey.update( hash.final() );
		seckey.update( versionHash.final() );
		QByteArray signature = seckey.signature();
		QFile sigFile( signatureFilename );
		if ( !sigFile.open(QIODevice::WriteOnly) ) {
			std::cout << "Failed to open signature file for writing!\n";
			continue;
		}
		sigFile.write( signature.toBase64() );
		sigFile.close();
		std::cout << "Signature written to " << sigFile.fileName().toLatin1().data() << "\n";
	}
	this->thread()->quit();
}

void 
signer::checkSignature()
{
	std::cout << "checking file " << archiveFilename.toLatin1().data() << "...\n";
	QCA::Initializer init;
	// only start this "loop" so that we can jump to the end of it using "continue"
	for ( int temp = 0; temp < 1; temp++ ) {
		QFile pubkeyfile( m_pubkeyFilename );
		if ( !pubkeyfile.open( QIODevice::ReadOnly ) ) 
		{
			std::cout << "Failed to open public key file!\n";
			continue;
		}
		QString pubkeystring = pubkeyfile.readAll();
		pubkeyfile.close();
		QCA::ConvertResult conversionResult;
		QCA::PublicKey pubkey = QCA::PublicKey::fromPEM( pubkeystring, &conversionResult );
		if ( !( QCA::ConvertGood == conversionResult ) )
		{
			std::cout << "Failed to read public key!\n";
			continue;
		}
		QFile file( archiveFilename );
		if ( !file.open( QIODevice::ReadOnly ) ) {
			std::cout << "Failed to open archive file for reading!\n";
			continue;
		}
		QCA::Hash hash( "sha1" );
		hash.update( &file );
		file.close();
		QFile versionFile( versionFilename );
		if ( !versionFile.open( QIODevice::ReadOnly ) ) {
			std::cout << "Failed to open version file for reading!\n";
			continue;
		}
		QCA::Hash versionHash( "sha1" );
		versionHash.update( &versionFile );
		versionFile.close();
		QFile sigFile( signatureFilename );
		if ( !sigFile.open( QIODevice::ReadOnly ) )
		{
			std::cout << "Failed to open signature file for reading!\n";
			continue;
		}
		QByteArray signature = QByteArray::fromBase64( sigFile.readAll() );
		pubkey.startVerify( QCA::EMSA3_SHA1 );
		pubkey.update( hash.final() );
		pubkey.update( versionHash.final() );
		if ( !pubkey.validSignature( signature ) ) 
		{
			std::cout << "Invalid signature!\n";
			continue;
		}
		std::cout << "Signature verified :-)\n";
	}
	this->thread()->quit();
}
		
