aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/str/libkvistr.cpp
diff options
context:
space:
mode:
authorGravatar Fabio Bas2010-08-07 14:57:06 +0000
committerGravatar Fabio Bas2010-08-07 14:57:06 +0000
commit33d25fdc724ce3e3bee43cdf2d1f976ba050e3d7 (patch)
tree95a451f61cdd35e3c523217156df201db098cb1a /src/modules/str/libkvistr.cpp
parentquick fix (diff)
downloadKVIrc-33d25fdc724ce3e3bee43cdf2d1f976ba050e3d7.tar.gz
KVIrc-33d25fdc724ce3e3bee43cdf2d1f976ba050e3d7.tar.bz2
KVIrc-33d25fdc724ce3e3bee43cdf2d1f976ba050e3d7.zip
implemented $str.evpSign() and $str.evpVerify() (ticket #866)
git-svn-id: https://svn.kvirc.de/svn/trunk/kvirc@4802 17fca916-40b9-46aa-a4ea-0a15b648b75c
Diffstat (limited to 'src/modules/str/libkvistr.cpp')
-rw-r--r--src/modules/str/libkvistr.cpp285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/modules/str/libkvistr.cpp b/src/modules/str/libkvistr.cpp
index a190c1caa..2f62c243d 100644
--- a/src/modules/str/libkvistr.cpp
+++ b/src/modules/str/libkvistr.cpp
@@ -33,13 +33,16 @@
#include "kvi_settings.h"
#include "kvi_malloc.h"
#include "kvi_kvs_arraycast.h"
+#include "kvi_options.h"
#include <QRegExp>
#include <QClipboard>
#if defined( COMPILE_SSL_SUPPORT ) && !defined( COMPILE_NO_EMBEDDED_CODE )
// The current implementation
+ #include <openssl/ssl.h>
#include <openssl/evp.h>
+ #include <openssl/pem.h>
#elif defined(COMPILE_NO_EMBEDDED_CODE)
// The preferred new implementation (until QCryptographicHash supports all
// hashes we want).
@@ -2173,6 +2176,286 @@ static bool str_kvs_fnc_printf(KviKvsModuleFunctionCall * c)
}
+/*
+ @doc: str.evpSign
+ @type:
+ function
+ @title:
+ $str.evpSign
+ @short:
+ Returns a signature for a message created using your certificate's private key
+ @syntax:
+ <string> $str.evpSign(<message:string>[,<certificate:string>[,<password:string>]])
+ @description:
+ This function returns a base64-encoded signature string created signing the
+ provided <message> using the private key of the specified <certificate>.[br]
+ If the <certificate> parameter is omitted, the private key specified in the
+ kvirc options will be used.[br]
+ If any error occurs, this function will return an empty string.
+ @examples:
+ [example]
+ # Emulate a call with no parameters
+ %message="test message";
+ %pcert=$file.read($option(stringSSLPrivateKeyPath));
+ %sign=$str.evpSign(%message,%pcert,$option(stringSSLPrivateKeyPass));
+ [/example]
+ [example]
+ # Sign and verify the signature using the certificates from options
+ %message="test message";
+ %sign=$str.evpSign(%message);
+ if($str.evpVerify(%message, %sign))
+ {
+ echo "signature is valid";
+ } else {
+ echo "signature is not valid";
+ }
+ [/example]
+ @seealso:
+ [fnc]$str.evpVerify[/fnc]
+*/
+
+static bool str_kvs_fnc_evpSign(KviKvsModuleFunctionCall * c)
+{
+ QByteArray szMessage;
+ QByteArray szCert;
+ QByteArray szPass;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("message",KVS_PT_NONEMPTYCSTRING,0,szMessage)
+ KVSM_PARAMETER("certificate",KVS_PT_NONEMPTYCSTRING,KVS_PF_OPTIONAL,szCert)
+ KVSM_PARAMETER("password",KVS_PT_NONEMPTYCSTRING,KVS_PF_OPTIONAL,szPass)
+ KVSM_PARAMETERS_END(c)
+
+#if defined(COMPILE_SSL_SUPPORT) && !defined(COMPILE_NO_EMBEDDED_CODE)
+
+ EVP_MD_CTX md_ctx;
+ EVP_PKEY * pKey = 0;
+ unsigned int len = 0;
+ unsigned char *sig = 0;
+
+ //FIXME do we need a mutex here?
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ if(szCert.isEmpty())
+ {
+ //use default cert
+ if(!KVI_OPTION_BOOL(KviOption_boolUseSSLPrivateKey))
+ {
+ c->warning(__tr2qs("No certificate specified and no private key certificate defined in KVIrc options."));
+ c->returnValue()->setString("");
+ return true;
+ }
+
+ FILE * f = fopen(KVI_OPTION_STRING(KviOption_stringSSLPrivateKeyPath).toUtf8().data(),"r");
+ if(!f)
+ {
+ c->warning(__tr2qs("File I/O error while trying to use the private key file %s"),KVI_OPTION_STRING(KviOption_stringSSLPrivateKeyPath).toUtf8().data());
+ c->returnValue()->setString("");
+ return true;
+ }
+
+ szPass = KVI_OPTION_STRING(KviOption_stringSSLPrivateKeyPass).toUtf8();
+ PEM_read_PrivateKey(f, &pKey, NULL, szPass.data());
+
+ fclose(f);
+
+ if(!pKey)
+ {
+ c->warning(__tr2qs("Can not read private key while trying to use the default private key certificate %s"),KVI_OPTION_STRING(KviOption_stringSSLPrivateKeyPath).toUtf8().data());
+ c->returnValue()->setString("");
+ return true;
+ }
+ } else {
+ // get from parameter (with optional password)
+ BIO *in;
+ in = BIO_new_mem_buf((unsigned char*)szCert.data(), szCert.size());
+ PEM_read_bio_PrivateKey(in, &pKey, NULL, szPass.data());
+ BIO_free(in);
+
+ if(!pKey)
+ {
+ c->warning(__tr2qs("Can not read private key while trying to use the provided certificate (wrong password?)"));
+ c->returnValue()->setString("");
+ return true;
+ }
+ }
+
+ len = EVP_PKEY_size(pKey);
+ sig = (unsigned char*)kvi_malloc(len*sizeof(char));
+
+ EVP_SignInit(&md_ctx, EVP_sha1());
+ EVP_SignUpdate(&md_ctx, (unsigned char *)szMessage.data(), szMessage.length());
+ if (EVP_SignFinal (&md_ctx, sig, &len, pKey))
+ {
+ QByteArray szSign((const char *)sig, len);
+ OPENSSL_free(sig);
+ EVP_PKEY_free(pKey);
+ c->returnValue()->setString(szSign.toBase64().data());
+ return true;
+ }
+ c->warning(__tr2qs("An error occured while signing the message."));
+ c->returnValue()->setString("");
+ return true;
+
+#else
+ c->warning(__tr2qs("KVIrc is compiled without OpenSSL support."));
+ c->returnValue()->setString("");
+ return true;
+#endif
+}
+
+/*
+ @doc: str.evpVerify
+ @type:
+ function
+ @title:
+ $str.evpVerify
+ @short:
+ Verifies the signature for a message against a public key
+ @syntax:
+ <bool> $str.evpVerify(<message:string>,<signature:string>[,<certificate:string>[,<password:string>]])
+ @description:
+ This function verifies the signature for a message against a publick key contained in a certificate.[br]
+ The signature has to be base64-encoded, as the one returned by [fnc]$str.evpSign[/fnc].[br]
+ If the <certificate> parameter is omitted, the public key certificate specified in the
+ kvirc options will be used.[br]
+ If any error occurs, this function will return false.
+ @examples:
+ [example]
+ # Emulate a call with no certificate parameters
+ %message="test message";
+ %signature=$str.evpSign(%message);
+ %cert=$file.read($option(stringSSLCertificatePath));
+ $str.evpVerify(%message,%signature,%cert,$option(stringSSLCertificatePass));
+ [/example]
+ [example]
+ # Sign and verify the signature using the certificates from options
+ %message="test message";
+ %sign=$str.evpSign(%message);
+ if($str.evpVerify(%message, %sign))
+ {
+ echo "signature is valid";
+ } else {
+ echo "signature is not valid";
+ }
+ [/example]
+ @seealso:
+ [fnc]$str.evpSign[/fnc]
+*/
+
+static bool str_kvs_fnc_evpVerify(KviKvsModuleFunctionCall * c)
+{
+ QByteArray szMessage;
+ QByteArray szCert;
+ QByteArray szSign;
+ QByteArray szSignB64;
+ QByteArray szPass;
+
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("message",KVS_PT_NONEMPTYCSTRING,0,szMessage)
+ KVSM_PARAMETER("signature",KVS_PT_NONEMPTYCSTRING,0,szSignB64)
+ KVSM_PARAMETER("certificate",KVS_PT_NONEMPTYCSTRING,KVS_PF_OPTIONAL,szCert)
+ KVSM_PARAMETER("password",KVS_PT_NONEMPTYCSTRING,KVS_PF_OPTIONAL,szPass)
+ KVSM_PARAMETERS_END(c)
+
+#if defined(COMPILE_SSL_SUPPORT) && !defined(COMPILE_NO_EMBEDDED_CODE)
+
+ //FIXME do we need a mutex here?
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ szSign = QByteArray::fromBase64(szSignB64);
+ const char * message = szMessage.data();
+
+ EVP_MD_CTX md_ctx;
+ EVP_PKEY *pKey = 0;
+ X509 *cert = 0;
+ int err = -1;
+
+ if(szCert.isEmpty())
+ {
+ //use default cert
+ if(!KVI_OPTION_BOOL(KviOption_boolUseSSLCertificate))
+ {
+ c->warning(__tr2qs("No certificate specified and no public key certificate defined in KVIrc options."));
+ c->returnValue()->setString("");
+ return true;
+ }
+
+ FILE * f = fopen(KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data(),"r");
+ if(!f)
+ {
+ c->warning(__tr2qs("File I/O error while trying to use the public key file %s"),KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data());
+ c->returnValue()->setString("");
+ return true;
+ }
+
+ szPass = KVI_OPTION_STRING(KviOption_stringSSLCertificatePass).toUtf8();
+ PEM_read_X509(f, &cert, NULL, szPass.data());
+
+ fclose(f);
+
+ if(cert)
+ {
+ pKey = (EVP_PKEY *) X509_get_pubkey(cert);
+ X509_free(cert);
+ }
+
+ if(!pKey)
+ {
+ c->warning(__tr2qs("Can not read public key while trying to use the default public key certificate %s"),KVI_OPTION_STRING(KviOption_stringSSLCertificatePath).toUtf8().data());
+ c->returnValue()->setString("");
+ return true;
+ }
+ } else {
+ // get from parameter (with optional password)
+ BIO *in = BIO_new_mem_buf((unsigned char*)szCert.data(), szCert.size());
+ PEM_read_bio_X509(in, &cert, 0, szPass.data());
+
+ if(cert)
+ {
+ pKey = (EVP_PKEY *) X509_get_pubkey(cert);
+ X509_free(cert);
+ } else {
+ pKey = PEM_read_bio_PUBKEY(in, NULL, 0, szPass.data());
+ }
+
+ BIO_free(in);
+
+ if(!pKey)
+ {
+ c->warning(__tr2qs("Can not read public key from the provided certificate."));
+ c->returnValue()->setBoolean(false);
+ return true;
+ }
+ }
+
+ EVP_VerifyInit(&md_ctx, EVP_sha1());
+ EVP_VerifyUpdate(&md_ctx, message, strlen(message));
+ err = EVP_VerifyFinal(&md_ctx, (unsigned char*)szSign.data(), szSign.size(), pKey);
+ EVP_MD_CTX_cleanup(&md_ctx);
+ EVP_PKEY_free(pKey);
+ switch(err)
+ {
+ case 0:
+ c->returnValue()->setBoolean(false);
+ return true;
+ case 1:
+ c->returnValue()->setBoolean(true);
+ return true;
+ default:
+ c->warning(__tr2qs("An error occured during signature verification."));
+ c->returnValue()->setBoolean(false);
+ return true;
+ }
+#else
+ c->warning(__tr2qs("KVIrc is compiled without OpenSSL support."));
+ c->returnValue()->setBoolean(false);
+ return true;
+#endif
+}
+
+
/*********************************************************************/
// Module stuff
@@ -2223,6 +2506,8 @@ static bool str_module_init(KviModule * m)
KVSM_REGISTER_FUNCTION(m,"upcase",str_kvs_fnc_upcase);
KVSM_REGISTER_FUNCTION(m,"urlencode",str_kvs_fnc_urlencode);
KVSM_REGISTER_FUNCTION(m,"word",str_kvs_fnc_word);
+ KVSM_REGISTER_FUNCTION(m,"evpSign",str_kvs_fnc_evpSign);
+ KVSM_REGISTER_FUNCTION(m,"evpVerify",str_kvs_fnc_evpVerify);
KVSM_REGISTER_SIMPLE_COMMAND(m,"toClipboard",str_kvs_cmd_toClipboard);
return true;