/* * qca-tls.cpp - TLS plugin for TQCA * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "tqca-tls.h" #include #include #include #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x00907000 #define OSSL_097 #endif #ifndef OSSL_097 #define NO_AES #endif static TQByteArray lib_randomArray(int size) { if(RAND_status() == 0) { srand(time(NULL)); char buf[128]; for(int n = 0; n < 128; ++n) buf[n] = rand(); RAND_seed(buf, 128); } TQByteArray a(size); RAND_bytes((unsigned char *)a.data(), a.size()); return a; } static bool lib_generateKeyIV(const EVP_CIPHER *_type, const TQByteArray &data, const TQByteArray &salt, TQByteArray *key, TQByteArray *iv, int keysize=-1) { TQByteArray k, i; unsigned char *kp = 0; unsigned char *ip = 0; #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) EVP_CIPHER type = *_type; EVP_CIPHER *loctype = &type; if(keysize != -1) type.key_len = keysize; #else EVP_CIPHER *loctype = EVP_CIPHER_meth_dup(_type); Q_UNUSED(keysize) #endif if(key) { k.resize(EVP_CIPHER_key_length(loctype)); kp = (unsigned char *)k.data(); } if(iv) { i.resize(EVP_CIPHER_iv_length(loctype)); ip = (unsigned char *)i.data(); } int res = EVP_BytesToKey(loctype, EVP_sha1(), (unsigned char *)salt.data(), (unsigned char *)data.data(), data.size(), 1, kp, ip); #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) EVP_CIPHER_meth_free(loctype); #endif if (!res) return false; if(key) *key = k; if(iv) *iv = i; return true; } static void appendArray(TQByteArray *a, const TQByteArray &b) { int oldsize = a->size(); a->resize(oldsize + b.size()); memcpy(a->data() + oldsize, b.data(), b.size()); } static TQByteArray bio2buf(BIO *b) { TQByteArray buf; while(1) { char block[1024]; int ret = BIO_read(b, block, 1024); int oldsize = buf.size(); buf.resize(oldsize + ret); memcpy(buf.data() + oldsize, block, ret); if(ret != 1024) break; } BIO_free(b); return buf; } class SHA1Context : public TQCA_HashContext { public: SHA1Context() { reset(); } TQCA_HashContext *clone() { return new SHA1Context(*this); } void reset() { SHA1_Init(&c); } void update(const char *in, unsigned int len) { SHA1_Update(&c, in, len); } void final(TQByteArray *out) { TQByteArray buf(20); SHA1_Final((unsigned char *)buf.data(), &c); *out = buf; } SHA_CTX c; }; class MD5Context : public TQCA_HashContext { public: MD5Context() { reset(); } TQCA_HashContext *clone() { return new MD5Context(*this); } void reset() { MD5_Init(&c); } void update(const char *in, unsigned int len) { MD5_Update(&c, in, len); } void final(TQByteArray *out) { TQByteArray buf(16); MD5_Final((unsigned char *)buf.data(), &c); *out = buf; } MD5_CTX c; }; class EVPCipherContext : public TQCA_CipherContext { public: EVPCipherContext() { type = 0; } virtual ~EVPCipherContext() { if(type) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) EVP_CIPHER_CTX_cleanup(c); OPENSSL_free(c); #else EVP_CIPHER_CTX_free(c); #endif type = 0; } } TQCA_CipherContext *clone() { EVPCipherContext *cc = cloneSelf(); cc->r = r.copy(); return cc; } virtual EVPCipherContext *cloneSelf() const=0; virtual const EVP_CIPHER *getType(int mode) const=0; int keySize() { return EVP_CIPHER_key_length(getType(TQCA::CBC)); } int blockSize() { return EVP_CIPHER_block_size(getType(TQCA::CBC)); } bool generateKey(char *out, int keysize) { TQByteArray a; if(!lib_generateKeyIV(getType(TQCA::CBC), lib_randomArray(128), lib_randomArray(2), &a, 0, keysize)) return false; memcpy(out, a.data(), a.size()); return true; } bool generateIV(char *out) { TQByteArray a; if(!lib_generateKeyIV(getType(TQCA::CBC), lib_randomArray(128), lib_randomArray(2), 0, &a)) return false; memcpy(out, a.data(), a.size()); return true; } bool setup(int _dir, int mode, const char *key, int keysize, const char *iv, bool _pad) { dir = _dir; pad = _pad; type = getType(mode); r.resize(0); #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) c = (EVP_CIPHER_CTX*)OPENSSL_malloc(sizeof(EVP_CIPHER_CTX)); EVP_CIPHER_CTX_init(c); #else c = EVP_CIPHER_CTX_new(); #endif if(dir == TQCA::Encrypt) { if(!EVP_EncryptInit(c, type, NULL, NULL)) return false; if(keysize != EVP_CIPHER_key_length(type)) EVP_CIPHER_CTX_set_key_length(c, keysize); if(!EVP_EncryptInit(c, NULL, (unsigned char *)key, (unsigned char *)iv)) return false; } else { if(!EVP_DecryptInit(c, type, NULL, NULL)) return false; if(keysize != EVP_CIPHER_key_length(type)) EVP_CIPHER_CTX_set_key_length(c, keysize); if(!EVP_DecryptInit(c, NULL, (unsigned char *)key, (unsigned char *)iv)) return false; } return true; } bool update(const char *in, unsigned int len) { TQByteArray result(len + EVP_CIPHER_block_size(type)); int olen; if(dir == TQCA::Encrypt || !pad) { if(!EVP_EncryptUpdate(c, (unsigned char *)result.data(), &olen, (unsigned char *)in, len)) return false; } else { if(!EVP_DecryptUpdate(c, (unsigned char *)result.data(), &olen, (unsigned char *)in, len)) return false; } result.resize(olen); appendArray(&r, result); return true; } bool final(TQByteArray *out) { if(pad) { TQByteArray result(EVP_CIPHER_block_size(type)); int olen; if(dir == TQCA::Encrypt) { if(!EVP_EncryptFinal_ex(c, (unsigned char *)result.data(), &olen)) return false; } else { if(!EVP_DecryptFinal_ex(c, (unsigned char *)result.data(), &olen)) return false; } result.resize(olen); appendArray(&r, result); } *out = r.copy(); r.resize(0); return true; } EVP_CIPHER_CTX *c; const EVP_CIPHER *type; TQByteArray r; int dir; bool pad; }; class BlowFishContext : public EVPCipherContext { public: EVPCipherContext *cloneSelf() const { return new BlowFishContext(*this); } const EVP_CIPHER *getType(int mode) const { if(mode == TQCA::CBC) return EVP_bf_cbc(); else if(mode == TQCA::CFB) return EVP_bf_cfb(); else return 0; } }; class TripleDESContext : public EVPCipherContext { public: EVPCipherContext *cloneSelf() const { return new TripleDESContext(*this); } const EVP_CIPHER *getType(int mode) const { if(mode == TQCA::CBC) return EVP_des_ede3_cbc(); else if(mode == TQCA::CFB) return EVP_des_ede3_cfb(); else return 0; } }; #ifndef NO_AES class AES128Context : public EVPCipherContext { public: EVPCipherContext *cloneSelf() const { return new AES128Context(*this); } const EVP_CIPHER *getType(int mode) const { if(mode == TQCA::CBC) return EVP_aes_128_cbc(); else if(mode == TQCA::CFB) return EVP_aes_128_cfb(); else return 0; } }; class AES256Context : public EVPCipherContext { public: EVPCipherContext *cloneSelf() const { return new AES256Context(*this); } const EVP_CIPHER *getType(int mode) const { if(mode == TQCA::CBC) return EVP_aes_256_cbc(); else if(mode == TQCA::CFB) return EVP_aes_256_cfb(); else return 0; } }; #endif class RSAKeyContext : public TQCA_RSAKeyContext { public: RSAKeyContext() { pub = 0; sec = 0; } ~RSAKeyContext() { reset(); } void reset() { if(pub) { RSA_free(pub); pub = 0; } if(sec) { RSA_free(sec); sec = 0; } } void separate(RSA *r, RSA **_pub, RSA **_sec) { // public unsigned char *buf, *p; int len = i2d_RSAPublicKey(r, NULL); if(len > 0) { buf = (unsigned char *)malloc(len); p = buf; i2d_RSAPublicKey(r, &p); p = buf; #ifdef OSSL_097 *_pub = d2i_RSAPublicKey(NULL, (const unsigned char **)&p, len); #else *_pub = d2i_RSAPublicKey(NULL, (unsigned char **)&p, len); #endif free(buf); } len = i2d_RSAPrivateKey(r, NULL); if(len > 0) { buf = (unsigned char *)malloc(len); p = buf; i2d_RSAPrivateKey(r, &p); p = buf; #ifdef OSSL_097 *_sec = d2i_RSAPrivateKey(NULL, (const unsigned char **)&p, len); #else *_sec = d2i_RSAPrivateKey(NULL, (unsigned char **)&p, len); #endif free(buf); } } bool isNull() const { if(!pub && !sec) return true; return false; } bool havePublic() const { return pub ? true : false; } bool havePrivate() const { return sec ? true : false; } bool createFromDER(const char *in, unsigned int len) { RSA *r; void *p; // private? p = (void *)in; #ifdef OSSL_097 r = d2i_RSAPrivateKey(NULL, (const unsigned char **)&p, len); #else r = d2i_RSAPrivateKey(NULL, (unsigned char **)&p, len); #endif if(r) { reset(); // private means both, I think, so separate them separate(r, &pub, &sec); return true; } else { // public? p = (void *)in; #ifdef OSSL_097 r = d2i_RSAPublicKey(NULL, (const unsigned char **)&p, len); #else r = d2i_RSAPublicKey(NULL, (unsigned char **)&p, len); #endif if(!r) { // try this other public function, for whatever reason p = (void *)in; r = d2i_RSA_PUBKEY(NULL, (const unsigned char **)&p, len); } if(r) { if(pub) { RSA_free(pub); } pub = r; return true; } } return false; } bool createFromPEM(const char *in, unsigned int len) { BIO *bi; // private? bi = BIO_new(BIO_s_mem()); BIO_write(bi, in, len); RSA *r = PEM_read_bio_RSAPrivateKey(bi, NULL, NULL, NULL); BIO_free(bi); if(r) { reset(); separate(r, &pub, &sec); return true; } else { // public? bi = BIO_new(BIO_s_mem()); BIO_write(bi, in, len); r = PEM_read_bio_RSAPublicKey(bi, NULL, NULL, NULL); BIO_free(bi); if(r) { if(pub) { RSA_free(pub); } pub = r; return true; } } return false; } bool createFromNative(void *in) { reset(); separate((RSA *)in, &pub, &sec); return true; } bool generate(unsigned int bits) { BIGNUM *bign = BN_new(); if (BN_set_word(bign, RSA_F4) != 1) { BN_free(bign); return false; } RSA *r = RSA_new(); if(!r) { BN_free(bign); return false; } RSA_generate_key_ex(r, bits, bign, NULL); separate(r, &pub, &sec); RSA_free(r); BN_free(bign); return true; } TQCA_RSAKeyContext *clone() const { // deep copy RSAKeyContext *c = new RSAKeyContext; if(pub) { c->pub = RSAPublicKey_dup(pub); } if(sec) { c->sec = RSAPrivateKey_dup(sec); } return c; } bool toDER(TQByteArray *out, bool publicOnly) { if(sec && !publicOnly) { int len = i2d_RSAPrivateKey(sec, NULL); TQByteArray buf(len); unsigned char *p; p = (unsigned char *)buf.data(); i2d_RSAPrivateKey(sec, &p); *out = buf; return true; } else if(pub) { int len = i2d_RSAPublicKey(pub, NULL); TQByteArray buf(len); unsigned char *p; p = (unsigned char *)buf.data(); i2d_RSAPublicKey(pub, &p); *out = buf; return true; } else return false; } bool toPEM(TQByteArray *out, bool publicOnly) { if(sec && !publicOnly) { BIO *bo = BIO_new(BIO_s_mem()); PEM_write_bio_RSAPrivateKey(bo, sec, NULL, NULL, 0, NULL, NULL); *out = bio2buf(bo); return true; } else if(pub) { BIO *bo = BIO_new(BIO_s_mem()); PEM_write_bio_RSAPublicKey(bo, pub); *out = bio2buf(bo); return true; } else return false; } bool encrypt(const TQByteArray &in, TQByteArray *out, bool oaep) { if(!pub) return false; int size = RSA_size(pub); int flen = in.size(); if(oaep) { if(flen >= size - 41) flen = size - 41; } else { if(flen >= size - 11) flen = size - 11; } TQByteArray result(size); unsigned char *from = (unsigned char *)in.data(); unsigned char *to = (unsigned char *)result.data(); int ret = RSA_public_encrypt(flen, from, to, pub, oaep ? RSA_PKCS1_OAEP_PADDING : RSA_PKCS1_PADDING); if(ret == -1) return false; result.resize(ret); *out = result; return true; } bool decrypt(const TQByteArray &in, TQByteArray *out, bool oaep) { if(!sec) return false; int size = RSA_size(sec); int flen = in.size(); TQByteArray result(size); unsigned char *from = (unsigned char *)in.data(); unsigned char *to = (unsigned char *)result.data(); int ret = RSA_private_decrypt(flen, from, to, sec, oaep ? RSA_PKCS1_OAEP_PADDING : RSA_PKCS1_PADDING); if(ret == -1) return false; result.resize(ret); *out = result; return true; } RSA *pub, *sec; }; static TQValueList nameToProperties(struct X509_name_st *name) { TQValueList list; for(int n = 0; n < X509_NAME_entry_count(name); ++n) { X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, n); TQCA_CertProperty p; ASN1_OBJECT *ao = X509_NAME_ENTRY_get_object(ne); int nid = OBJ_obj2nid(ao); if(nid == NID_undef) continue; p.var = OBJ_nid2sn(nid); ASN1_STRING *as = X509_NAME_ENTRY_get_data(ne); TQCString c; c.resize(as->length+1); strncpy(c.data(), (char *)as->data, as->length); p.val = TQString::fromLatin1(c); list += p; } return list; } // (taken from tdelibs) -- Justin // // This code is mostly taken from OpenSSL v0.9.5a // by Eric Young TQDateTime ASN1_UTCTIME_TQDateTime(ASN1_UTCTIME *tm, int *isGmt) { TQDateTime qdt; char *v; int gmt=0; int i; int y=0,M=0,d=0,h=0,m=0,s=0; TQDate qdate; TQTime qtime; i = tm->length; v = (char *)tm->data; if (i < 10) goto auq_err; if (v[i-1] == 'Z') gmt=1; for (i=0; i<10; i++) if ((v[i] > '9') || (v[i] < '0')) goto auq_err; y = (v[0]-'0')*10+(v[1]-'0'); if (y < 50) y+=100; M = (v[2]-'0')*10+(v[3]-'0'); if ((M > 12) || (M < 1)) goto auq_err; d = (v[4]-'0')*10+(v[5]-'0'); h = (v[6]-'0')*10+(v[7]-'0'); m = (v[8]-'0')*10+(v[9]-'0'); if ( (v[10] >= '0') && (v[10] <= '9') && (v[11] >= '0') && (v[11] <= '9')) s = (v[10]-'0')*10+(v[11]-'0'); // localize the date and display it. qdate.setYMD(y+1900, M, d); qtime.setHMS(h,m,s); qdt.setDate(qdate); qdt.setTime(qtime); auq_err: if (isGmt) *isGmt = gmt; return qdt; } // (adapted from tdelibs) -- Justin static bool cnMatchesAddress(const TQString &_cn, const TQString &peerHost) { TQString cn = _cn.stripWhiteSpace().lower(); TQRegExp rx; // Check for invalid characters if(TQRegExp("[^a-zA-Z0-9\\.\\*\\-]").search(cn) >= 0) return false; // Domains can legally end with '.'s. We don't need them though. while(cn.endsWith(".")) cn.truncate(cn.length()-1); // Do not let empty CN's get by!! if(cn.isEmpty()) return false; // Check for IPv4 address rx.setPattern("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"); if(rx.exactMatch(peerHost)) return peerHost == cn; // Check for IPv6 address here... rx.setPattern("^\\[.*\\]$"); if(rx.exactMatch(peerHost)) return peerHost == cn; if(cn.contains('*')) { // First make sure that there are at least two valid parts // after the wildcard (*). TQStringList parts = TQStringList::split('.', cn, false); while(parts.count() > 2) parts.remove(parts.begin()); if(parts.count() != 2) { return false; // we don't allow *.root - that's bad } if(parts[0].contains('*') || parts[1].contains('*')) { return false; } // RFC2818 says that *.example.com should match against // foo.example.com but not bar.foo.example.com // (ie. they must have the same number of parts) if(TQRegExp(cn, false, true).exactMatch(peerHost) && TQStringList::split('.', cn, false).count() == TQStringList::split('.', peerHost, false).count()) return true; return false; } // We must have an exact match in this case (insensitive though) // (note we already did .lower()) if(cn == peerHost) return true; return false; } class CertContext : public TQCA_CertContext { public: CertContext() { x = 0; } ~CertContext() { reset(); } TQCA_CertContext *clone() const { CertContext *c = new CertContext(*this); if(x) { c->x = X509_dup(x); } return c; } void reset() { if(x) { X509_free(x); x = 0; serial = ""; v_subject = ""; v_issuer = ""; cp_subject.clear(); cp_issuer.clear(); na = TQDateTime(); nb = TQDateTime(); } } bool isNull() const { return (x ? false: true); } bool createFromDER(const char *in, unsigned int len) { const unsigned char *p = (const unsigned char *)in; X509 *t = d2i_X509(NULL, &p, len); if(!t) return false; fromX509(t); X509_free(t); return true; } bool createFromPEM(const char *in, unsigned int len) { BIO *bi = BIO_new(BIO_s_mem()); BIO_write(bi, in, len); X509 *t = PEM_read_bio_X509(bi, NULL, NULL, NULL); BIO_free(bi); if(!t) return false; fromX509(t); X509_free(t); return true; } bool toDER(TQByteArray *out) { int len = i2d_X509(x, NULL); TQByteArray buf(len); unsigned char *p = (unsigned char *)buf.data(); i2d_X509(x, &p); *out = buf; return true; } bool toPEM(TQByteArray *out) { BIO *bo = BIO_new(BIO_s_mem()); PEM_write_bio_X509(bo, x); *out = bio2buf(bo); return true; } void fromX509(X509 *t) { reset(); x = X509_dup(t); // serial number ASN1_INTEGER *ai = X509_get_serialNumber(x); if(ai) { char *rep = i2s_ASN1_INTEGER(NULL, ai); serial = rep; OPENSSL_free(rep); } // validity dates nb = ASN1_UTCTIME_TQDateTime(X509_get_notBefore(x), NULL); na = ASN1_UTCTIME_TQDateTime(X509_get_notAfter(x), NULL); // extract the subject/issuer strings struct X509_name_st *sn = X509_get_subject_name(x); struct X509_name_st *in = X509_get_issuer_name(x); char buf[1024]; X509_NAME_oneline(sn, buf, 1024); v_subject = buf; X509_NAME_oneline(in, buf, 1024); v_issuer = buf; // extract the subject/issuer contents cp_subject = nameToProperties(sn); cp_issuer = nameToProperties(in); } TQString serialNumber() const { return serial; } TQString subjectString() const { return v_subject; } TQString issuerString() const { return v_issuer; } TQValueList subject() const { return cp_subject; } TQValueList issuer() const { return cp_issuer; } TQDateTime notBefore() const { return nb; } TQDateTime notAfter() const { return na; } bool matchesAddress(const TQString &realHost) const { TQString peerHost = realHost.stripWhiteSpace(); while(peerHost.endsWith(".")) peerHost.truncate(peerHost.length()-1); peerHost = peerHost.lower(); TQString cn; for(TQValueList::ConstIterator it = cp_subject.begin(); it != cp_subject.end(); ++it) { if((*it).var == "CN") { cn = (*it).val; break; } } if(cnMatchesAddress(cn, peerHost)) return true; return false; } X509 *x; TQString serial, v_subject, v_issuer; TQValueList cp_subject, cp_issuer; TQDateTime nb, na; }; static bool ssl_init = false; class TLSContext : public TQCA_TLSContext { public: enum { Good, TryAgain, Bad }; enum { Idle, Connect, Accept, Handshake, Active, Closing }; bool serv; int mode; TQByteArray sendQueue, recvQueue; CertContext *cert; RSAKeyContext *key; SSL *ssl; SSL_METHOD *method; SSL_CTX *context; BIO *rbio, *wbio; CertContext cc; int vr; bool v_eof; TLSContext() { if(!ssl_init) { SSL_library_init(); SSL_load_error_strings(); ssl_init = true; } ssl = 0; context = 0; cert = 0; key = 0; } ~TLSContext() { reset(); } void reset() { if(ssl) { SSL_free(ssl); ssl = 0; } if(context) { SSL_CTX_free(context); context = 0; } if(cert) { delete cert; cert = 0; } if(key) { delete key; key = 0; } sendQueue.resize(0); recvQueue.resize(0); mode = Idle; cc.reset(); vr = TQCA::TLS::Unknown; v_eof = false; } bool eof() const { return v_eof; } bool startClient(const TQPtrList &store, const TQCA_CertContext &_cert, const TQCA_RSAKeyContext &_key) { reset(); serv = false; method = const_cast(SSLv23_client_method()); if(!setup(store, _cert, _key)) return false; mode = Connect; return true; } bool startServer(const TQPtrList &store, const TQCA_CertContext &_cert, const TQCA_RSAKeyContext &_key) { reset(); serv = true; method = const_cast(SSLv23_server_method()); if(!setup(store, _cert, _key)) return false; mode = Accept; return true; } bool setup(const TQPtrList &list, const TQCA_CertContext &_cc, const TQCA_RSAKeyContext &kc) { context = SSL_CTX_new(method); if(!context) { reset(); return false; } // load the cert store if(!list.isEmpty()) { X509_STORE *store = SSL_CTX_get_cert_store(context); TQPtrListIterator it(list); for(CertContext *i; (i = (CertContext *)it.current()); ++it) X509_STORE_add_cert(store, i->x); } ssl = SSL_new(context); if(!ssl) { reset(); return false; } SSL_set_ssl_method(ssl, method); // can this return error? // setup the memory bio rbio = BIO_new(BIO_s_mem()); wbio = BIO_new(BIO_s_mem()); // this passes control of the bios to ssl. we don't need to free them. SSL_set_bio(ssl, rbio, wbio); // setup the cert to send if(!_cc.isNull() && !kc.isNull()) { cert = static_cast(_cc.clone()); key = static_cast(kc.clone()); if(SSL_use_certificate(ssl, cert->x) != 1) { reset(); return false; } if(SSL_use_RSAPrivateKey(ssl, key->sec) != 1) { reset(); return false; } } return true; } int handshake(const TQByteArray &in, TQByteArray *out) { if(!in.isEmpty()) BIO_write(rbio, in.data(), in.size()); if(mode == Connect) { int ret = doConnect(); if(ret == Good) { mode = Handshake; } else if(ret == Bad) { reset(); return Error; } } if(mode == Accept) { int ret = doAccept(); if(ret == Good) { getCert(); mode = Active; } else if(ret == Bad) { reset(); return Error; } } if(mode == Handshake) { int ret = doHandshake(); if(ret == Good) { getCert(); mode = Active; } else if(ret == Bad) { reset(); return Error; } } // process outgoing *out = readOutgoing(); if(mode == Active) return Success; else return Continue; } int shutdown(const TQByteArray &in, TQByteArray *out) { if(!in.isEmpty()) BIO_write(rbio, in.data(), in.size()); int ret = doShutdown(); if(ret == Bad) { reset(); return Error; } *out = readOutgoing(); if(ret == Good) { mode = Idle; return Success; } else { mode = Closing; return Continue; } } void getCert() { // verify the certificate int code = TQCA::TLS::Unknown; X509 *x = SSL_get_peer_certificate(ssl); if(x) { cc.fromX509(x); X509_free(x); int ret = SSL_get_verify_result(ssl); if(ret == X509_V_OK) code = TQCA::TLS::Valid; else code = resultToCV(ret); } else { cc.reset(); code = TQCA::TLS::NoCert; } vr = code; } bool encode(const TQByteArray &plain, TQByteArray *to_net, int *enc) { if(mode != Active) return false; appendArray(&sendQueue, plain); int encoded = 0; if(sendQueue.size() > 0) { int ret = SSL_write(ssl, sendQueue.data(), sendQueue.size()); enum { Good, Continue, Done, Error }; int m; if(ret <= 0) { int x = SSL_get_error(ssl, ret); if(x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) m = Continue; else if(x == SSL_ERROR_ZERO_RETURN) m = Done; else m = Error; } else { m = Good; encoded = ret; int newsize = sendQueue.size() - encoded; char *r = sendQueue.data(); memmove(r, r + encoded, newsize); sendQueue.resize(newsize); } if(m == Done) { sendQueue.resize(0); v_eof = true; return false; } if(m == Error) { sendQueue.resize(0); return false; } } *to_net = readOutgoing(); *enc = encoded; return true; } bool decode(const TQByteArray &from_net, TQByteArray *plain, TQByteArray *to_net) { if(mode != Active) return false; if(!from_net.isEmpty()) BIO_write(rbio, from_net.data(), from_net.size()); TQByteArray a; while(!v_eof) { a.resize(8192); int ret = SSL_read(ssl, a.data(), a.size()); if(ret > 0) { if(ret != (int)a.size()) a.resize(ret); appendArray(&recvQueue, a); } else if(ret <= 0) { int x = SSL_get_error(ssl, ret); if(x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) break; else if(x == SSL_ERROR_ZERO_RETURN) v_eof = true; else return false; } } *plain = recvQueue.copy(); recvQueue.resize(0); // could be outgoing data also *to_net = readOutgoing(); return true; } TQByteArray unprocessed() { TQByteArray a; int size = BIO_pending(rbio); if(size <= 0) return a; a.resize(size); int r = BIO_read(rbio, a.data(), size); if(r <= 0) { a.resize(0); return a; } if(r != size) a.resize(r); return a; } TQByteArray readOutgoing() { TQByteArray a; int size = BIO_pending(wbio); if(size <= 0) return a; a.resize(size); int r = BIO_read(wbio, a.data(), size); if(r <= 0) { a.resize(0); return a; } if(r != size) a.resize(r); return a; } int doConnect() { int ret = SSL_connect(ssl); if(ret < 0) { int x = SSL_get_error(ssl, ret); if(x == SSL_ERROR_WANT_CONNECT || x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) return TryAgain; else return Bad; } else if(ret == 0) return Bad; return Good; } int doAccept() { int ret = SSL_accept(ssl); if(ret < 0) { int x = SSL_get_error(ssl, ret); if(x == SSL_ERROR_WANT_CONNECT || x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) return TryAgain; else return Bad; } else if(ret == 0) return Bad; return Good; } int doHandshake() { int ret = SSL_do_handshake(ssl); if(ret < 0) { int x = SSL_get_error(ssl, ret); if(x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) return TryAgain; else return Bad; } else if(ret == 0) return Bad; return Good; } int doShutdown() { int ret = SSL_shutdown(ssl); if(ret >= 1) return Good; else { if(ret == 0) return TryAgain; int x = SSL_get_error(ssl, ret); if(x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) return TryAgain; return Bad; } } TQCA_CertContext *peerCertificate() const { return cc.clone(); } int validityResult() const { return vr; } int resultToCV(int ret) const { int rc; switch(ret) { case X509_V_ERR_CERT_REJECTED: rc = TQCA::TLS::Rejected; break; case X509_V_ERR_CERT_UNTRUSTED: rc = TQCA::TLS::Untrusted; break; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_CRL_SIGNATURE_FAILURE: case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: rc = TQCA::TLS::SignatureFailed; break; case X509_V_ERR_INVALID_CA: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: rc = TQCA::TLS::InvalidCA; break; case X509_V_ERR_INVALID_PURPOSE: rc = TQCA::TLS::InvalidPurpose; break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: rc = TQCA::TLS::SelfSigned; break; case X509_V_ERR_CERT_REVOKED: rc = TQCA::TLS::Revoked; break; case X509_V_ERR_PATH_LENGTH_EXCEEDED: rc = TQCA::TLS::PathLengthExceeded; break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: rc = TQCA::TLS::Expired; break; case X509_V_ERR_APPLICATION_VERIFICATION: case X509_V_ERR_OUT_OF_MEM: case X509_V_ERR_UNABLE_TO_GET_CRL: case X509_V_ERR_CERT_CHAIN_TOO_LONG: default: rc = TQCA::TLS::Unknown; break; } return rc; } }; class TQCAOpenSSL : public TQCAProvider { public: TQCAOpenSSL() {} ~TQCAOpenSSL() {} void init() { } int qcaVersion() const { return TQCA_PLUGIN_VERSION; } int capabilities() const { int caps = TQCA::CAP_SHA1 | TQCA::CAP_MD5 | TQCA::CAP_BlowFish | TQCA::CAP_TripleDES | #ifndef NO_AES TQCA::CAP_AES128 | TQCA::CAP_AES256 | #endif TQCA::CAP_RSA | TQCA::CAP_X509 | TQCA::CAP_TLS; return caps; } void *context(int cap) { if(cap == TQCA::CAP_SHA1) return new SHA1Context; else if(cap == TQCA::CAP_MD5) return new MD5Context; else if(cap == TQCA::CAP_BlowFish) return new BlowFishContext; else if(cap == TQCA::CAP_TripleDES) return new TripleDESContext; #ifndef NO_AES else if(cap == TQCA::CAP_AES128) return new AES128Context; else if(cap == TQCA::CAP_AES256) return new AES256Context; #endif else if(cap == TQCA::CAP_RSA) return new RSAKeyContext; else if(cap == TQCA::CAP_X509) return new CertContext; else if(cap == TQCA::CAP_TLS) return new TLSContext; return 0; } }; #ifdef TQCA_PLUGIN TQCAProvider *createProvider() #else TQCAProvider *createProviderTLS() #endif { return (new TQCAOpenSSL); }