ksmimecrypto.cc

00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 2003 Stefan Rompf <sux@loplof.de>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public License
00016  * along with this library; see the file COPYING.LIB.  If not, write to
00017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 
00021 
00022 #include "ksmimecrypto.h"
00023 
00024 #include <qptrlist.h>
00025 #include <qcstring.h>
00026 #include <qstring.h>
00027 #include <kdebug.h>
00028 
00029 #include "kopenssl.h"
00030 #include "ksslcertificate.h"
00031 #include "ksslpkcs12.h"
00032 
00033 // this hack provided by Malte Starostik to avoid glibc/openssl bug
00034 // on some systems
00035 #ifdef KSSL_HAVE_SSL
00036 #define crypt _openssl_crypt
00037 #include <openssl/err.h>
00038 #undef crypt
00039 #endif
00040 
00041 
00042 // forward included macros to KOpenSSLProxy
00043 #define sk_new kossl->sk_new
00044 #define sk_free kossl->sk_free
00045 #define sk_push kossl->sk_push
00046 #define sk_value kossl->sk_value
00047 #define sk_num kossl->sk_num
00048 #define BIO_ctrl kossl->BIO_ctrl
00049 
00050 
00051 #ifdef KSSL_HAVE_SSL
00052 static const char eot = 0;
00053 
00054 class KSMIMECryptoPrivate {
00055     KOpenSSLProxy *kossl;
00056 
00057 public:
00058     KSMIMECryptoPrivate(KOpenSSLProxy *kossl);
00059 
00060 
00061     STACK_OF(X509) *certsToX509(QPtrList<KSSLCertificate> &certs);
00062 
00063     KSMIMECrypto::rc signMessage(BIO *clearText,
00064                  BIO *cipherText,
00065                  KSSLPKCS12 &privKey, QPtrList<KSSLCertificate> &certs,
00066                  bool detached);
00067 
00068     KSMIMECrypto::rc encryptMessage(BIO *clearText,
00069                     BIO *cipherText, KSMIMECrypto::algo algorithm,
00070                     QPtrList<KSSLCertificate> &recip);
00071 
00072     KSMIMECrypto::rc checkSignature(BIO *clearText,
00073                     BIO *signature, bool detached,
00074                     QPtrList<KSSLCertificate> &recip);
00075 
00076     KSMIMECrypto::rc decryptMessage(BIO *cipherText,
00077                     BIO *clearText,
00078                     KSSLPKCS12 &privKey);
00079 
00080     void MemBIOToQByteArray(BIO *src, QByteArray &dest);
00081 
00082     KSMIMECrypto::rc sslErrToRc(void);
00083 };
00084 
00085 
00086 KSMIMECryptoPrivate::KSMIMECryptoPrivate(KOpenSSLProxy *kossl): kossl(kossl) {
00087 }
00088 
00089 
00090 STACK_OF(X509) *KSMIMECryptoPrivate::certsToX509(QPtrList<KSSLCertificate> &certs) {
00091     STACK_OF(X509) *x509 = (STACK_OF(X509) *) sk_new(NULL);
00092     KSSLCertificate *cert = certs.first();
00093     while(cert) {
00094     sk_X509_push(x509, cert->getCert());
00095     cert = certs.next();
00096     }
00097     return x509;
00098 }
00099 
00100 
00101 KSMIMECrypto::rc KSMIMECryptoPrivate::signMessage(BIO *clearText,
00102                           BIO *cipherText,
00103                           KSSLPKCS12 &privKey, QPtrList<KSSLCertificate> &certs,
00104                           bool detached) {
00105 
00106     STACK_OF(X509) *other = NULL;
00107     KSMIMECrypto::rc rc;
00108     int flags = detached?PKCS7_DETACHED:0;
00109 
00110     if (certs.count()) other = certsToX509(certs);
00111 
00112     PKCS7 *p7 = kossl->PKCS7_sign(privKey.getCertificate()->getCert(), privKey.getPrivateKey(),
00113                   other, clearText, flags);
00114 
00115     if (other) sk_X509_free(other);
00116 
00117     if (!p7) return sslErrToRc();
00118 
00119     if (kossl->i2d_PKCS7_bio(cipherText, p7)) {
00120     rc = KSMIMECrypto::KSC_R_OK;
00121     } else {
00122     rc = sslErrToRc();
00123     }
00124 
00125     kossl->PKCS7_free(p7);
00126 
00127     return rc;
00128 }
00129 
00130 KSMIMECrypto::rc KSMIMECryptoPrivate::encryptMessage(BIO *clearText,
00131                              BIO *cipherText, KSMIMECrypto::algo algorithm,
00132                              QPtrList<KSSLCertificate> &recip) {
00133     EVP_CIPHER *cipher = NULL;
00134     KSMIMECrypto::rc rc;
00135     switch(algorithm) {
00136     case KSMIMECrypto::KSC_C_DES3_CBC:
00137         cipher = kossl->EVP_des_ede3_cbc();
00138         break;
00139     case KSMIMECrypto::KSC_C_RC2_CBC_128:
00140         cipher = kossl->EVP_rc2_cbc();
00141         break;
00142     case KSMIMECrypto::KSC_C_RC2_CBC_64:
00143         cipher = kossl->EVP_rc2_64_cbc();
00144         break;
00145     case KSMIMECrypto::KSC_C_DES_CBC:
00146         cipher = kossl->EVP_des_cbc();
00147         break;
00148     case KSMIMECrypto::KSC_C_RC2_CBC_40:
00149         cipher = kossl->EVP_rc2_40_cbc();
00150         break;
00151     }
00152     if (!cipher) return KSMIMECrypto::KSC_R_NOCIPHER;
00153 
00154     STACK_OF(X509) *certs = certsToX509(recip);
00155 
00156     PKCS7 *p7 = kossl->PKCS7_encrypt(certs, clearText, cipher, 0);
00157 
00158     sk_X509_free(certs);
00159 
00160     if (!p7) return sslErrToRc();
00161 
00162     if (kossl->i2d_PKCS7_bio(cipherText, p7)) {
00163     rc = KSMIMECrypto::KSC_R_OK;
00164     } else {
00165     rc = sslErrToRc();
00166     }
00167 
00168     kossl->PKCS7_free(p7);
00169 
00170     return rc;
00171 }
00172 
00173 
00174 KSMIMECrypto::rc KSMIMECryptoPrivate::checkSignature(BIO *clearText,
00175                              BIO *signature, bool detached,
00176                              QPtrList<KSSLCertificate> &recip) {
00177 
00178     PKCS7 *p7 = kossl->d2i_PKCS7_bio(signature, NULL);
00179     KSMIMECrypto::rc rc = KSMIMECrypto::KSC_R_OTHER;
00180 
00181     if (!p7) return sslErrToRc();
00182 
00183     BIO *in;
00184     BIO *out;
00185     if (detached) {
00186     in = clearText;
00187     out = NULL;
00188     } else {
00189     in = NULL;
00190     out = clearText;
00191     }
00192 
00193     X509_STORE *dummystore = kossl->X509_STORE_new();
00194     if (kossl->PKCS7_verify(p7, NULL, dummystore, in, out, PKCS7_NOVERIFY)) {
00195     STACK_OF(X509) *signers = kossl->PKCS7_get0_signers(p7, 0, PKCS7_NOVERIFY);
00196     int num = sk_X509_num(signers);
00197 
00198     for(int n=0; n<num; n++) {
00199         KSSLCertificate *signer = KSSLCertificate::fromX509(sk_X509_value(signers, n));
00200         recip.append(signer);
00201     }
00202 
00203     sk_X509_free(signers);
00204     rc = KSMIMECrypto::KSC_R_OK;
00205     } else {
00206     rc = sslErrToRc();
00207     }
00208 
00209     kossl->X509_STORE_free(dummystore);
00210     kossl->PKCS7_free(p7);
00211 
00212     return rc;
00213 }
00214 
00215 
00216 KSMIMECrypto::rc KSMIMECryptoPrivate::decryptMessage(BIO *cipherText,
00217                              BIO *clearText,
00218                              KSSLPKCS12 &privKey) {
00219 
00220     PKCS7 *p7 = kossl->d2i_PKCS7_bio(cipherText, NULL);
00221     KSMIMECrypto::rc rc;
00222 
00223     if (!p7) return sslErrToRc();
00224 
00225     if (kossl->PKCS7_decrypt(p7, privKey.getPrivateKey(), privKey.getCertificate()->getCert(),
00226                  clearText, 0)) {
00227     rc = KSMIMECrypto::KSC_R_OK;
00228     } else {
00229     rc = sslErrToRc();
00230     }
00231 
00232     kossl->PKCS7_free(p7);
00233 
00234     return rc;
00235 }
00236 
00237 
00238 void KSMIMECryptoPrivate::MemBIOToQByteArray(BIO *src, QByteArray &dest) {
00239     char *buf;
00240     long len = BIO_get_mem_data(src, &buf);
00241     dest.assign(buf, len);
00242     /* Now this goes quite a bit into openssl internals.
00243        We assume that openssl uses malloc() (it does in
00244        default config) and rip out the buffer.
00245     */
00246     reinterpret_cast<BUF_MEM *>(src->ptr)->data = NULL;
00247 }
00248 
00249 
00250 KSMIMECrypto::rc KSMIMECryptoPrivate::sslErrToRc(void) {
00251     unsigned long cerr = kossl->ERR_get_error();
00252 
00253     // To be completed and possibly fixed
00254 
00255     switch(ERR_GET_REASON(cerr)) {
00256     case ERR_R_MALLOC_FAILURE:
00257         return KSMIMECrypto::KSC_R_NOMEM;
00258     }
00259 
00260     switch(ERR_GET_LIB(cerr)) {
00261     case ERR_LIB_PKCS7:
00262         switch(ERR_GET_REASON(cerr)) {
00263         case PKCS7_R_WRONG_CONTENT_TYPE:
00264         case PKCS7_R_NO_CONTENT:
00265         case PKCS7_R_NO_SIGNATURES_ON_DATA:
00266             return KSMIMECrypto::KSC_R_FORMAT;
00267             break;
00268         case PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE:
00269         case PKCS7_R_DECRYPT_ERROR: // Hmm?
00270             return KSMIMECrypto::KSC_R_WRONGKEY;
00271             break;
00272         case PKCS7_R_DIGEST_FAILURE:
00273             return KSMIMECrypto::KSC_R_VERIFY;
00274         default:
00275             break;
00276         }
00277         break;
00278     default:
00279         break;
00280     }
00281 
00282     kdDebug(7029) <<"KSMIMECrypto: uncaught error " <<ERR_GET_LIB(cerr)
00283           <<" " <<ERR_GET_REASON(cerr) <<endl;
00284     return KSMIMECrypto::KSC_R_OTHER;
00285 }
00286 #endif
00287 
00288 
00289 KSMIMECrypto::KSMIMECrypto() {
00290 #ifdef KSSL_HAVE_SSL
00291     kossl = KOpenSSLProxy::self();
00292     priv = new KSMIMECryptoPrivate(kossl);
00293     if (!kossl->hasLibCrypto()) kossl = 0L;
00294 #else
00295     kossl = 0L;
00296 #endif
00297 }
00298 
00299 
00300 KSMIMECrypto::~KSMIMECrypto() {
00301 #ifdef KSSL_HAVE_SSL
00302     delete priv;
00303 #endif
00304 }
00305 
00306 
00307 KSMIMECrypto::rc KSMIMECrypto::signMessage(const QCString &clearText,
00308                        QByteArray &cipherText,
00309                        const KSSLPKCS12 &privKey,
00310                        const QPtrList<KSSLCertificate> &certs,
00311                        bool detached) {
00312 #ifdef KSSL_HAVE_SSL
00313     if (!kossl) return KSC_R_NO_SSL;
00314     BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size());
00315     BIO *out = kossl->BIO_new(kossl->BIO_s_mem());
00316 
00317     rc rc = priv->signMessage(in, out,
00318                   const_cast<KSSLPKCS12 &>(privKey),
00319                   const_cast<QPtrList<KSSLCertificate> &>(certs),
00320                   detached);
00321 
00322     if (!rc) priv->MemBIOToQByteArray(out, cipherText);
00323 
00324     kossl->BIO_free(out);
00325     kossl->BIO_free(in);
00326 
00327     return rc;
00328 #else
00329     return KSC_R_NO_SSL;
00330 #endif
00331 }
00332 
00333 
00334 KSMIMECrypto::rc KSMIMECrypto::checkDetachedSignature(const QCString &clearText,
00335                               const QByteArray &signature,
00336                               QPtrList<KSSLCertificate> &foundCerts) {
00337 #ifdef KSSL_HAVE_SSL
00338     if (!kossl) return KSC_R_NO_SSL;
00339     BIO *txt = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.length());
00340     BIO *sig = kossl->BIO_new_mem_buf((char *)signature.data(), signature.size());
00341 
00342     rc rc = priv->checkSignature(txt, sig, true, foundCerts);
00343 
00344     kossl->BIO_free(sig);
00345     kossl->BIO_free(txt);
00346 
00347     return rc;
00348 #else
00349     return KSC_R_NO_SSL;
00350 #endif
00351 }
00352 
00353 
00354 KSMIMECrypto::rc KSMIMECrypto::checkOpaqueSignature(const QByteArray &signedText,
00355                             QCString &clearText,
00356                             QPtrList<KSSLCertificate> &foundCerts) {
00357 #ifdef KSSL_HAVE_SSL
00358     if (!kossl) return KSC_R_NO_SSL;
00359 
00360     BIO *in = kossl->BIO_new_mem_buf((char *)signedText.data(), signedText.size());
00361     BIO *out = kossl->BIO_new(kossl->BIO_s_mem());
00362 
00363     rc rc = priv->checkSignature(out, in, false, foundCerts);
00364 
00365     kossl->BIO_write(out, &eot, 1);
00366     priv->MemBIOToQByteArray(out, clearText);
00367 
00368     kossl->BIO_free(out);
00369     kossl->BIO_free(in);
00370 
00371     return rc;
00372 #else
00373     return KSC_R_NO_SSL;
00374 #endif
00375 }
00376 
00377 
00378 KSMIMECrypto::rc KSMIMECrypto::encryptMessage(const QCString &clearText,
00379                           QByteArray &cipherText,
00380                           algo algorithm,
00381                           const QPtrList<KSSLCertificate> &recip) {
00382 #ifdef KSSL_HAVE_SSL
00383     if (!kossl) return KSC_R_NO_SSL;
00384 
00385     BIO *in = kossl->BIO_new_mem_buf((char *)clearText.data(), clearText.size());
00386     BIO *out = kossl->BIO_new(kossl->BIO_s_mem());
00387 
00388     rc rc = priv->encryptMessage(in,out,algorithm,
00389                  const_cast< QPtrList<KSSLCertificate> &>(recip));
00390 
00391     if (!rc) priv->MemBIOToQByteArray(out, cipherText);
00392 
00393     kossl->BIO_free(out);
00394     kossl->BIO_free(in);
00395 
00396     return rc;
00397 #else
00398     return KSC_R_NO_SSL;
00399 #endif
00400 }
00401 
00402 
00403 KSMIMECrypto::rc KSMIMECrypto::decryptMessage(const QByteArray &cipherText,
00404                           QCString &clearText,
00405                           const KSSLPKCS12 &privKey) {
00406 #ifdef KSSL_HAVE_SSL
00407     if (!kossl) return KSC_R_NO_SSL;
00408 
00409     BIO *in = kossl->BIO_new_mem_buf((char *)cipherText.data(), cipherText.size());
00410     BIO *out = kossl->BIO_new(kossl->BIO_s_mem());
00411 
00412     rc rc = priv->decryptMessage(in,out,
00413                  const_cast<KSSLPKCS12 &>(privKey));
00414 
00415     kossl->BIO_write(out, &eot, 1);
00416     priv->MemBIOToQByteArray(out, clearText);
00417 
00418     kossl->BIO_free(out);
00419     kossl->BIO_free(in);
00420 
00421     return rc;
00422 #else
00423     return KSC_R_NO_SSL;
00424 #endif
00425 }
KDE Home | KDE Accessibility Home | Description of Access Keys