diff options
Diffstat (limited to 'kio/kssl/ksmimecrypto.cc')
-rw-r--r-- | kio/kssl/ksmimecrypto.cc | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/kio/kssl/ksmimecrypto.cc b/kio/kssl/ksmimecrypto.cc new file mode 100644 index 000000000..8b171ddb4 --- /dev/null +++ b/kio/kssl/ksmimecrypto.cc @@ -0,0 +1,424 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2003 Stefan Rompf <sux@loplof.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#include <qptrlist.h> +#include <qcstring.h> +#include <qstring.h> +#include <kdebug.h> + +#include "kopenssl.h" +#include "ksslcertificate.h" +#include "ksslpkcs12.h" +#include "ksmimecrypto.h" + +// this hack provided by Malte Starostik to avoid glibc/openssl bug +// on some systems +#ifdef KSSL_HAVE_SSL +#define crypt _openssl_crypt +#include <openssl/err.h> +#undef crypt +#endif + + +// forward included macros to KOpenSSLProxy +#define sk_new kossl->sk_new +#define sk_free kossl->sk_free +#define sk_push kossl->sk_push +#define sk_value kossl->sk_value +#define sk_num kossl->sk_num +#define BIO_ctrl kossl->BIO_ctrl + + +#ifdef KSSL_HAVE_SSL +static const char eot = 0; + +class KSMIMECryptoPrivate { + KOpenSSLProxy *kossl; + +public: + KSMIMECryptoPrivate(KOpenSSLProxy *kossl); + + + STACK_OF(X509) *certsToX509(QPtrList<KSSLCertificate> &certs); + + KSMIMECrypto::rc signMessage(BIO *clearText, + BIO *cipherText, + KSSLPKCS12 &privKey, QPtrList<KSSLCertificate> &certs, + bool detached); + + KSMIMECrypto::rc encryptMessage(BIO *clearText, + BIO *cipherText, KSMIMECrypto::algo algorithm, + QPtrList<KSSLCertificate> &recip); + + KSMIMECrypto::rc checkSignature(BIO *clearText, + BIO *signature, bool detached, + QPtrList<KSSLCertificate> &recip); + + KSMIMECrypto::rc decryptMessage(BIO *cipherText, + BIO *clearText, + KSSLPKCS12 &privKey); + + void MemBIOToQByteArray(BIO *src, QByteArray &dest); + + KSMIMECrypto::rc sslErrToRc(void); +}; + + +KSMIMECryptoPrivate::KSMIMECryptoPrivate(KOpenSSLProxy *kossl): kossl(kossl) { +} + + +STACK_OF(X509) *KSMIMECryptoPrivate::certsToX509(QPtrList<KSSLCertificate> &certs) { + STACK_OF(X509) *x509 = sk_new(NULL); + KSSLCertificate *cert = certs.first(); + while(cert) { + sk_X509_push(x509, cert->getCert()); + cert = certs.next(); + } + return x509; +} + + +KSMIMECrypto::rc KSMIMECryptoPrivate::signMessage(BIO *clearText, + BIO *cipherText, + KSSLPKCS12 &privKey, QPtrList<KSSLCertificate> &certs, + bool detached) { + + STACK_OF(X509) *other = NULL; + KSMIMECrypto::rc rc; + int flags = detached?PKCS7_DETACHED:0; + + if (certs.count()) other = certsToX509(certs); + + PKCS7 *p7 = kossl->PKCS7_sign(privKey.getCertificate()->getCert(), privKey.getPrivateKey(), + other, clearText, flags); + + if (other) sk_X509_free(other); + + if (!p7) return sslErrToRc(); + + if (kossl->i2d_PKCS7_bio(cipherText, p7)) { + rc = KSMIMECrypto::KSC_R_OK; + } else { + rc = sslErrToRc(); + } + + kossl->PKCS7_free(p7); + + return rc; +} + +KSMIMECrypto::rc KSMIMECryptoPrivate::encryptMessage(BIO *clearText, + BIO *cipherText, KSMIMECrypto::algo algorithm, + QPtrList<KSSLCertificate> &recip) { + EVP_CIPHER *cipher = NULL; + KSMIMECrypto::rc rc; + switch(algorithm) { + case KSMIMECrypto::KSC_C_DES3_CBC: + cipher = kossl->EVP_des_ede3_cbc(); + break; + case KSMIMECrypto::KSC_C_RC2_CBC_128: + cipher = kossl->EVP_rc2_cbc(); + break; + case KSMIMECrypto::KSC_C_RC2_CBC_64: + cipher = kossl->EVP_rc2_64_cbc(); + break; + case KSMIMECrypto::KSC_C_DES_CBC: + cipher = kossl->EVP_des_cbc(); + break; + case KSMIMECrypto::KSC_C_RC2_CBC_40: + cipher = kossl->EVP_rc2_40_cbc(); + break; + } + if (!cipher) return KSMIMECrypto::KSC_R_NOCIPHER; + + STACK_OF(X509) *certs = certsToX509(recip); + + PKCS7 *p7 = kossl->PKCS7_encrypt(certs, clearText, cipher, 0); + + sk_X509_free(certs); + + if (!p7) return sslErrToRc(); + + if (kossl->i2d_PKCS7_bio(cipherText, p7)) { + rc = KSMIMECrypto::KSC_R_OK; + } else { + rc = sslErrToRc(); + } + + kossl->PKCS7_free(p7); + + return rc; +} + + +KSMIMECrypto::rc KSMIMECryptoPrivate::checkSignature(BIO *clearText, + BIO *signature, bool detached, + QPtrList<KSSLCertificate> &recip) { + + PKCS7 *p7 = kossl->d2i_PKCS7_bio(signature, NULL); + KSMIMECrypto::rc rc = KSMIMECrypto::KSC_R_OTHER; + + if (!p7) return sslErrToRc(); + + BIO *in; + BIO *out; + if (detached) { + in = clearText; + out = NULL; + } else { + in = NULL; + out = clearText; + } + + X509_STORE *dummystore = kossl->X509_STORE_new(); + if (kossl->PKCS7_verify(p7, NULL, dummystore, in, out, PKCS7_NOVERIFY)) { + STACK_OF(X509) *signers = kossl->PKCS7_get0_signers(p7, 0, PKCS7_NOVERIFY); + int num = sk_X509_num(signers); + + for(int n=0; n<num; n++) { + KSSLCertificate *signer = KSSLCertificate::fromX509(sk_X509_value(signers, n)); + recip.append(signer); + } + + sk_X509_free(signers); + rc = KSMIMECrypto::KSC_R_OK; + } else { + rc = sslErrToRc(); + } + + kossl->X509_STORE_free(dummystore); + kossl->PKCS7_free(p7); + + return rc; +} + + +KSMIMECrypto::rc KSMIMECryptoPrivate::decryptMessage(BIO *cipherText, + BIO *clearText, + KSSLPKCS12 &privKey) { + + PKCS7 *p7 = kossl->d2i_PKCS7_bio(cipherText, NULL); + KSMIMECrypto::rc rc; + + if (!p7) return sslErrToRc(); + + if (kossl->PKCS7_decrypt(p7, privKey.getPrivateKey(), privKey.getCertificate()->getCert(), + clearText, 0)) { + rc = KSMIMECrypto::KSC_R_OK; + } else { + rc = sslErrToRc(); + } + + kossl->PKCS7_free(p7); + + return rc; +} + + +void KSMIMECryptoPrivate::MemBIOToQByteArray(BIO *src, QByteArray &dest) { + char *buf; + long len = BIO_get_mem_data(src, &buf); + dest.assign(buf, len); + /* Now this goes quite a bit into openssl internals. + We assume that openssl uses malloc() (it does in + default config) and rip out the buffer. + */ + reinterpret_cast<BUF_MEM *>(src->ptr)->data = NULL; +} + + +KSMIMECrypto::rc KSMIMECryptoPrivate::sslErrToRc(void) { + unsigned long cerr = kossl->ERR_get_error(); + + // To be completed and possibly fixed + + switch(ERR_GET_REASON(cerr)) { + case ERR_R_MALLOC_FAILURE: + return KSMIMECrypto::KSC_R_NOMEM; + } + + switch(ERR_GET_LIB(cerr)) { + case ERR_LIB_PKCS7: + switch(ERR_GET_REASON(cerr)) { + case PKCS7_R_WRONG_CONTENT_TYPE: + case PKCS7_R_NO_CONTENT: + case PKCS7_R_NO_SIGNATURES_ON_DATA: + return KSMIMECrypto::KSC_R_FORMAT; + break; + case PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE: + case PKCS7_R_DECRYPT_ERROR: // Hmm? + return KSMIMECrypto::KSC_R_WRONGKEY; + break; + case PKCS7_R_DIGEST_FAILURE: + return KSMIMECrypto::KSC_R_VERIFY; + default: + break; + } + break; + default: + break; + } + + kdDebug(7029) <<"KSMIMECrypto: uncaught error " <<ERR_GET_LIB(cerr) + <<" " <<ERR_GET_REASON(cerr) <<endl; + return KSMIMECrypto::KSC_R_OTHER; +} +#endif + + +KSMIMECrypto::KSMIMECrypto() { +#ifdef KSSL_HAVE_SSL + kossl = KOpenSSLProxy::self(); + priv = new KSMIMECryptoPrivate(kossl); + if (!kossl->hasLibCrypto()) kossl = 0L; +#else + kossl = 0L; +#endif +} + + +KSMIMECrypto::~KSMIMECrypto() { +#ifdef KSSL_HAVE_SSL + delete priv; +#endif +} + + +KSMIMECrypto::rc KSMIMECrypto::signMessage(const QCString &clearText, + QByteArray &cipherText, + const KSSLPKCS12 &privKey, + const QPtrList<KSSLCertificate> &certs, + bool detached) { +#ifdef KSSL_HAVE_SSL + if (!kossl) return KSC_R_NO_SSL; + BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size()); + BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); + + rc rc = priv->signMessage(in, out, + const_cast<KSSLPKCS12 &>(privKey), + const_cast<QPtrList<KSSLCertificate> &>(certs), + detached); + + if (!rc) priv->MemBIOToQByteArray(out, cipherText); + + kossl->BIO_free(out); + kossl->BIO_free(in); + + return rc; +#else + return KSC_R_NO_SSL; +#endif +} + + +KSMIMECrypto::rc KSMIMECrypto::checkDetachedSignature(const QCString &clearText, + const QByteArray &signature, + QPtrList<KSSLCertificate> &foundCerts) { +#ifdef KSSL_HAVE_SSL + if (!kossl) return KSC_R_NO_SSL; + BIO *txt = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.length()); + BIO *sig = kossl->BIO_new_mem_buf((char *)signature.data(), signature.size()); + + rc rc = priv->checkSignature(txt, sig, true, foundCerts); + + kossl->BIO_free(sig); + kossl->BIO_free(txt); + + return rc; +#else + return KSC_R_NO_SSL; +#endif +} + + +KSMIMECrypto::rc KSMIMECrypto::checkOpaqueSignature(const QByteArray &signedText, + QCString &clearText, + QPtrList<KSSLCertificate> &foundCerts) { +#ifdef KSSL_HAVE_SSL + if (!kossl) return KSC_R_NO_SSL; + + BIO *in = kossl->BIO_new_mem_buf((char *)signedText.data(), signedText.size()); + BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); + + rc rc = priv->checkSignature(out, in, false, foundCerts); + + kossl->BIO_write(out, &eot, 1); + priv->MemBIOToQByteArray(out, clearText); + + kossl->BIO_free(out); + kossl->BIO_free(in); + + return rc; +#else + return KSC_R_NO_SSL; +#endif +} + + +KSMIMECrypto::rc KSMIMECrypto::encryptMessage(const QCString &clearText, + QByteArray &cipherText, + algo algorithm, + const QPtrList<KSSLCertificate> &recip) { +#ifdef KSSL_HAVE_SSL + if (!kossl) return KSC_R_NO_SSL; + + BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size()); + BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); + + rc rc = priv->encryptMessage(in,out,algorithm, + const_cast< QPtrList<KSSLCertificate> &>(recip)); + + if (!rc) priv->MemBIOToQByteArray(out, cipherText); + + kossl->BIO_free(out); + kossl->BIO_free(in); + + return rc; +#else + return KSC_R_NO_SSL; +#endif +} + + +KSMIMECrypto::rc KSMIMECrypto::decryptMessage(const QByteArray &cipherText, + QCString &clearText, + const KSSLPKCS12 &privKey) { +#ifdef KSSL_HAVE_SSL + if (!kossl) return KSC_R_NO_SSL; + + BIO *in = kossl->BIO_new_mem_buf((char *)cipherText.data(), cipherText.size()); + BIO *out = kossl->BIO_new(kossl->BIO_s_mem()); + + rc rc = priv->decryptMessage(in,out, + const_cast<KSSLPKCS12 &>(privKey)); + + kossl->BIO_write(out, &eot, 1); + priv->MemBIOToQByteArray(out, clearText); + + kossl->BIO_free(out); + kossl->BIO_free(in); + + return rc; +#else + return KSC_R_NO_SSL; +#endif +} |