summaryrefslogtreecommitdiffstats
path: root/tqca-tls.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tqca-tls.cpp')
-rw-r--r--tqca-tls.cpp1510
1 files changed, 1510 insertions, 0 deletions
diff --git a/tqca-tls.cpp b/tqca-tls.cpp
new file mode 100644
index 0000000..df9eff1
--- /dev/null
+++ b/tqca-tls.cpp
@@ -0,0 +1,1510 @@
+/*
+ * 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 <tqregexp.h>
+
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#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<TQCA_CertProperty> nameToProperties(struct X509_name_st *name)
+{
+ TQValueList<TQCA_CertProperty> 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<TQCA_CertProperty> subject() const
+ {
+ return cp_subject;
+ }
+
+ TQValueList<TQCA_CertProperty> 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<TQCA_CertProperty>::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<TQCA_CertProperty> 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<TQCA_CertContext> &store, const TQCA_CertContext &_cert, const TQCA_RSAKeyContext &_key)
+ {
+ reset();
+ serv = false;
+ method = const_cast<SSL_METHOD*>(SSLv23_client_method());
+
+ if(!setup(store, _cert, _key))
+ return false;
+
+ mode = Connect;
+ return true;
+ }
+
+ bool startServer(const TQPtrList<TQCA_CertContext> &store, const TQCA_CertContext &_cert, const TQCA_RSAKeyContext &_key)
+ {
+ reset();
+ serv = true;
+ method = const_cast<SSL_METHOD*>(SSLv23_server_method());
+
+ if(!setup(store, _cert, _key))
+ return false;
+
+ mode = Accept;
+ return true;
+ }
+
+ bool setup(const TQPtrList<TQCA_CertContext> &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<TQCA_CertContext> 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<CertContext*>(_cc.clone());
+ key = static_cast<RSAKeyContext*>(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);
+}