You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqca-tls/tqca-tls.cpp

1515 lines
30 KiB

/*
* 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>
#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<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);
}