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.
1492 lines
27 KiB
1492 lines
27 KiB
/*
|
|
* tqca.cpp - TQt Cryptographic Architecture
|
|
* Copyright (C) 2003 Justin Karneges
|
|
* Copyright (C) 2010-2020 TDE Team
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "tqca.h"
|
|
#include "tqcaprovider.h"
|
|
|
|
#include <tqptrlist.h>
|
|
#include <tqdir.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqlibrary.h>
|
|
#include <tqtimer.h>
|
|
#include <tqhostaddress.h>
|
|
#include <tqapplication.h>
|
|
#include <tqguardedptr.h>
|
|
#include <cstdlib>
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
#define PLUGIN_EXT "dll"
|
|
#elif defined(Q_OS_MAC)
|
|
#define PLUGIN_EXT "dylib"
|
|
#else
|
|
#define PLUGIN_EXT "so"
|
|
#endif
|
|
|
|
using namespace TQCA;
|
|
|
|
class ProviderItem
|
|
{
|
|
public:
|
|
TQCAProvider *p;
|
|
TQString fname;
|
|
|
|
static ProviderItem *load(const TQString &fname)
|
|
{
|
|
TQLibrary *lib = new TQLibrary(fname);
|
|
if(!lib->load()) {
|
|
delete lib;
|
|
return 0;
|
|
}
|
|
void *s = lib->resolve("createProvider");
|
|
if(!s) {
|
|
delete lib;
|
|
return 0;
|
|
}
|
|
TQCAProvider *(*createProvider)() = (TQCAProvider *(*)())s;
|
|
TQCAProvider *p = createProvider();
|
|
if(!p) {
|
|
delete lib;
|
|
return 0;
|
|
}
|
|
ProviderItem *i = new ProviderItem(lib, p);
|
|
i->fname = fname;
|
|
return i;
|
|
}
|
|
|
|
static ProviderItem *fromClass(TQCAProvider *p)
|
|
{
|
|
ProviderItem *i = new ProviderItem(0, p);
|
|
return i;
|
|
}
|
|
|
|
~ProviderItem()
|
|
{
|
|
delete p;
|
|
delete lib;
|
|
}
|
|
|
|
void ensureInit()
|
|
{
|
|
if(init_done)
|
|
return;
|
|
init_done = true;
|
|
p->init();
|
|
}
|
|
|
|
private:
|
|
TQLibrary *lib;
|
|
bool init_done;
|
|
|
|
ProviderItem(TQLibrary *_lib, TQCAProvider *_p)
|
|
{
|
|
lib = _lib;
|
|
p = _p;
|
|
init_done = false;
|
|
}
|
|
};
|
|
|
|
static TQPtrList<ProviderItem> providerList;
|
|
static bool qca_init = false;
|
|
|
|
static bool plugin_have(const TQString &fname)
|
|
{
|
|
TQPtrListIterator<ProviderItem> it(providerList);
|
|
for(ProviderItem *i; (i = it.current()); ++it) {
|
|
if(i->fname == fname)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void plugin_scan()
|
|
{
|
|
TQStringList dirs = TQApplication::libraryPaths();
|
|
for(TQStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
|
|
TQDir libpath(*it);
|
|
TQDir dir(libpath.filePath("crypto"));
|
|
if(!dir.exists())
|
|
continue;
|
|
|
|
TQStringList list = dir.entryList();
|
|
for(TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
|
TQFileInfo fi(dir.filePath(*it));
|
|
if(fi.isDir())
|
|
continue;
|
|
if(fi.extension() != PLUGIN_EXT)
|
|
continue;
|
|
TQString fname = fi.filePath();
|
|
|
|
// don't load the same plugin again!
|
|
if(plugin_have(fname))
|
|
continue;
|
|
//printf("f=[%s]\n", fname.latin1());
|
|
|
|
ProviderItem *i = ProviderItem::load(fname);
|
|
if(!i)
|
|
continue;
|
|
if(i->p->qcaVersion() != TQCA_PLUGIN_VERSION) {
|
|
delete i;
|
|
continue;
|
|
}
|
|
|
|
providerList.append(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void plugin_addClass(TQCAProvider *p)
|
|
{
|
|
ProviderItem *i = ProviderItem::fromClass(p);
|
|
providerList.prepend(i);
|
|
}
|
|
|
|
static void plugin_unloadall()
|
|
{
|
|
providerList.clear();
|
|
}
|
|
|
|
static int plugin_caps()
|
|
{
|
|
int caps = 0;
|
|
TQPtrListIterator<ProviderItem> it(providerList);
|
|
for(ProviderItem *i; (i = it.current()); ++it)
|
|
caps |= i->p->capabilities();
|
|
return caps;
|
|
}
|
|
|
|
TQString TQCA::arrayToHex(const TQByteArray &a)
|
|
{
|
|
TQString out;
|
|
for(int n = 0; n < (int)a.size(); ++n) {
|
|
TQString str;
|
|
str.sprintf("%02x", (uchar)a[n]);
|
|
out.append(str);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
TQByteArray TQCA::hexToArray(const TQString &str)
|
|
{
|
|
TQByteArray out(str.length() / 2);
|
|
int at = 0;
|
|
for(int n = 0; n + 1 < (int)str.length(); n += 2) {
|
|
uchar a = str[n];
|
|
uchar b = str[n+1];
|
|
uchar c = ((a & 0x0f) << 4) + (b & 0x0f);
|
|
out[at++] = c;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
void TQCA::init()
|
|
{
|
|
if(qca_init)
|
|
return;
|
|
qca_init = true;
|
|
providerList.setAutoDelete(true);
|
|
}
|
|
|
|
bool TQCA::isSupported(int capabilities)
|
|
{
|
|
init();
|
|
|
|
int caps = plugin_caps();
|
|
if(caps & capabilities)
|
|
return true;
|
|
|
|
// ok, try scanning for new stuff
|
|
plugin_scan();
|
|
caps = plugin_caps();
|
|
if(caps & capabilities)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void TQCA::insertProvider(TQCAProvider *p)
|
|
{
|
|
plugin_addClass(p);
|
|
}
|
|
|
|
void TQCA::unloadAllPlugins()
|
|
{
|
|
plugin_unloadall();
|
|
}
|
|
|
|
static void *getContext(int cap)
|
|
{
|
|
init();
|
|
|
|
// this call will also trip a scan for new plugins if needed
|
|
if(!TQCA::isSupported(cap))
|
|
return 0;
|
|
|
|
TQPtrListIterator<ProviderItem> it(providerList);
|
|
for(ProviderItem *i; (i = it.current()); ++it) {
|
|
if(i->p->capabilities() & cap) {
|
|
i->ensureInit();
|
|
return i->p->context(cap);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Hash
|
|
//----------------------------------------------------------------------------
|
|
class Hash::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
c = 0;
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
delete c;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
c->reset();
|
|
}
|
|
|
|
TQCA_HashContext *c;
|
|
};
|
|
|
|
Hash::Hash(TQCA_HashContext *c)
|
|
{
|
|
d = new Private;
|
|
d->c = c;
|
|
}
|
|
|
|
Hash::Hash(const Hash &from)
|
|
{
|
|
d = new Private;
|
|
*this = from;
|
|
}
|
|
|
|
Hash & Hash::operator=(const Hash &from)
|
|
{
|
|
delete d->c;
|
|
d->c = from.d->c->clone();
|
|
return *this;
|
|
}
|
|
|
|
Hash::~Hash()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void Hash::clear()
|
|
{
|
|
d->reset();
|
|
}
|
|
|
|
void Hash::update(const TQByteArray &a)
|
|
{
|
|
d->c->update(a.data(), a.size());
|
|
}
|
|
|
|
TQByteArray Hash::final()
|
|
{
|
|
TQByteArray buf;
|
|
d->c->final(&buf);
|
|
return buf;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Cipher
|
|
//----------------------------------------------------------------------------
|
|
class Cipher::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
c = 0;
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
delete c;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
dir = Encrypt;
|
|
key.resize(0);
|
|
iv.resize(0);
|
|
err = false;
|
|
}
|
|
|
|
TQCA_CipherContext *c;
|
|
int dir;
|
|
int mode;
|
|
TQByteArray key, iv;
|
|
bool err;
|
|
};
|
|
|
|
Cipher::Cipher(TQCA_CipherContext *c, int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
|
|
{
|
|
d = new Private;
|
|
d->c = c;
|
|
reset(dir, mode, key, iv, pad);
|
|
}
|
|
|
|
Cipher::Cipher(const Cipher &from)
|
|
{
|
|
d = new Private;
|
|
*this = from;
|
|
}
|
|
|
|
Cipher & Cipher::operator=(const Cipher &from)
|
|
{
|
|
delete d->c;
|
|
d->c = from.d->c->clone();
|
|
d->dir = from.d->dir;
|
|
d->mode = from.d->mode;
|
|
d->key = from.d->key.copy();
|
|
d->iv = from.d->iv.copy();
|
|
d->err = from.d->err;
|
|
return *this;
|
|
}
|
|
|
|
Cipher::~Cipher()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
TQByteArray Cipher::dyn_generateKey(int size) const
|
|
{
|
|
TQByteArray buf;
|
|
if(size != -1)
|
|
buf.resize(size);
|
|
else
|
|
buf.resize(d->c->keySize());
|
|
if(!d->c->generateKey(buf.data(), size))
|
|
return TQByteArray();
|
|
return buf;
|
|
}
|
|
|
|
TQByteArray Cipher::dyn_generateIV() const
|
|
{
|
|
TQByteArray buf(d->c->blockSize());
|
|
if(!d->c->generateIV(buf.data()))
|
|
return TQByteArray();
|
|
return buf;
|
|
}
|
|
|
|
void Cipher::reset(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
|
|
{
|
|
d->reset();
|
|
|
|
d->dir = dir;
|
|
d->mode = mode;
|
|
d->key = key.copy();
|
|
d->iv = iv.copy();
|
|
if(!d->c->setup(d->dir, d->mode, d->key.isEmpty() ? 0: d->key.data(), d->key.size(), d->iv.isEmpty() ? 0 : d->iv.data(), pad)) {
|
|
d->err = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool Cipher::update(const TQByteArray &a)
|
|
{
|
|
if(d->err)
|
|
return false;
|
|
|
|
if(!a.isEmpty()) {
|
|
if(!d->c->update(a.data(), a.size())) {
|
|
d->err = true;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TQByteArray Cipher::final(bool *ok)
|
|
{
|
|
if(ok)
|
|
*ok = false;
|
|
if(d->err)
|
|
return TQByteArray();
|
|
|
|
TQByteArray out;
|
|
if(!d->c->final(&out)) {
|
|
d->err = true;
|
|
return TQByteArray();
|
|
}
|
|
if(ok)
|
|
*ok = true;
|
|
return out;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// SHA1
|
|
//----------------------------------------------------------------------------
|
|
SHA1::SHA1()
|
|
:Hash((TQCA_HashContext *)getContext(CAP_SHA1))
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// SHA256
|
|
//----------------------------------------------------------------------------
|
|
SHA256::SHA256()
|
|
:Hash((TQCA_HashContext *)getContext(CAP_SHA256))
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// MD5
|
|
//----------------------------------------------------------------------------
|
|
MD5::MD5()
|
|
:Hash((TQCA_HashContext *)getContext(CAP_MD5))
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// BlowFish
|
|
//----------------------------------------------------------------------------
|
|
BlowFish::BlowFish(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
|
|
:Cipher((TQCA_CipherContext *)getContext(CAP_BlowFish), dir, mode, key, iv, pad)
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// TripleDES
|
|
//----------------------------------------------------------------------------
|
|
TripleDES::TripleDES(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
|
|
:Cipher((TQCA_CipherContext *)getContext(CAP_TripleDES), dir, mode, key, iv, pad)
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// AES128
|
|
//----------------------------------------------------------------------------
|
|
AES128::AES128(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
|
|
:Cipher((TQCA_CipherContext *)getContext(CAP_AES128), dir, mode, key, iv, pad)
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// AES256
|
|
//----------------------------------------------------------------------------
|
|
AES256::AES256(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
|
|
:Cipher((TQCA_CipherContext *)getContext(CAP_AES256), dir, mode, key, iv, pad)
|
|
{
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// RSAKey
|
|
//----------------------------------------------------------------------------
|
|
class RSAKey::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
c = 0;
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
delete c;
|
|
}
|
|
|
|
TQCA_RSAKeyContext *c;
|
|
};
|
|
|
|
RSAKey::RSAKey()
|
|
{
|
|
d = new Private;
|
|
d->c = (TQCA_RSAKeyContext *)getContext(CAP_RSA);
|
|
}
|
|
|
|
RSAKey::RSAKey(const RSAKey &from)
|
|
{
|
|
d = new Private;
|
|
*this = from;
|
|
}
|
|
|
|
RSAKey & RSAKey::operator=(const RSAKey &from)
|
|
{
|
|
delete d->c;
|
|
d->c = from.d->c->clone();
|
|
return *this;
|
|
}
|
|
|
|
RSAKey::~RSAKey()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
bool RSAKey::isNull() const
|
|
{
|
|
return d->c->isNull();
|
|
}
|
|
|
|
bool RSAKey::havePublic() const
|
|
{
|
|
return d->c->havePublic();
|
|
}
|
|
|
|
bool RSAKey::havePrivate() const
|
|
{
|
|
return d->c->havePrivate();
|
|
}
|
|
|
|
TQByteArray RSAKey::toDER(bool publicOnly) const
|
|
{
|
|
TQByteArray out;
|
|
if(!d->c->toDER(&out, publicOnly))
|
|
return TQByteArray();
|
|
return out;
|
|
}
|
|
|
|
bool RSAKey::fromDER(const TQByteArray &a)
|
|
{
|
|
return d->c->createFromDER(a.data(), a.size());
|
|
}
|
|
|
|
TQString RSAKey::toPEM(bool publicOnly) const
|
|
{
|
|
TQByteArray out;
|
|
if(!d->c->toPEM(&out, publicOnly))
|
|
return TQByteArray();
|
|
|
|
TQCString cs;
|
|
cs.resize(out.size()+1);
|
|
memcpy(cs.data(), out.data(), out.size());
|
|
return TQString::fromLatin1(cs);
|
|
}
|
|
|
|
bool RSAKey::fromPEM(const TQString &str)
|
|
{
|
|
TQCString cs = str.latin1();
|
|
TQByteArray a(cs.length());
|
|
memcpy(a.data(), cs.data(), a.size());
|
|
return d->c->createFromPEM(a.data(), a.size());
|
|
}
|
|
|
|
bool RSAKey::fromNative(void *p)
|
|
{
|
|
return d->c->createFromNative(p);
|
|
}
|
|
|
|
bool RSAKey::encrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
|
|
{
|
|
TQByteArray out;
|
|
if(!d->c->encrypt(a, &out, oaep))
|
|
return false;
|
|
*b = out;
|
|
return true;
|
|
}
|
|
|
|
bool RSAKey::decrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
|
|
{
|
|
TQByteArray out;
|
|
if(!d->c->decrypt(a, &out, oaep))
|
|
return false;
|
|
*b = out;
|
|
return true;
|
|
}
|
|
|
|
bool RSAKey::generate(unsigned int bits)
|
|
{
|
|
return d->c->generate(bits);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// RSA
|
|
//----------------------------------------------------------------------------
|
|
RSA::RSA()
|
|
{
|
|
}
|
|
|
|
RSA::~RSA()
|
|
{
|
|
}
|
|
|
|
RSAKey RSA::key() const
|
|
{
|
|
return v_key;
|
|
}
|
|
|
|
void RSA::setKey(const RSAKey &k)
|
|
{
|
|
v_key = k;
|
|
}
|
|
|
|
bool RSA::encrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
|
|
{
|
|
if(v_key.isNull())
|
|
return false;
|
|
return v_key.encrypt(a, b, oaep);
|
|
}
|
|
|
|
bool RSA::decrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
|
|
{
|
|
if(v_key.isNull())
|
|
return false;
|
|
return v_key.decrypt(a, b, oaep);
|
|
}
|
|
|
|
RSAKey RSA::generateKey(unsigned int bits)
|
|
{
|
|
RSAKey k;
|
|
k.generate(bits);
|
|
return k;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Cert
|
|
//----------------------------------------------------------------------------
|
|
class Cert::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
c = 0;
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
delete c;
|
|
}
|
|
|
|
TQCA_CertContext *c;
|
|
};
|
|
|
|
Cert::Cert()
|
|
{
|
|
d = new Private;
|
|
// crash because this is returning 0
|
|
d->c = (TQCA_CertContext *)getContext(CAP_X509);
|
|
}
|
|
|
|
Cert::Cert(const Cert &from)
|
|
{
|
|
d = new Private;
|
|
*this = from;
|
|
}
|
|
|
|
Cert & Cert::operator=(const Cert &from)
|
|
{
|
|
delete d->c;
|
|
if ( from.d->c )
|
|
d->c = from.d->c->clone();
|
|
else
|
|
d->c = 0;
|
|
return *this;
|
|
}
|
|
|
|
Cert::~Cert()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void Cert::fromContext(TQCA_CertContext *ctx)
|
|
{
|
|
delete d->c;
|
|
d->c = ctx;
|
|
}
|
|
|
|
bool Cert::isNull() const
|
|
{
|
|
return d->c->isNull();
|
|
}
|
|
|
|
TQString Cert::commonName() const
|
|
{
|
|
CertProperties props = subject();
|
|
return props["CN"];
|
|
}
|
|
|
|
TQString Cert::serialNumber() const
|
|
{
|
|
return d->c->serialNumber();
|
|
}
|
|
|
|
TQString Cert::subjectString() const
|
|
{
|
|
return d->c->subjectString();
|
|
}
|
|
|
|
TQString Cert::issuerString() const
|
|
{
|
|
return d->c->issuerString();
|
|
}
|
|
|
|
CertProperties Cert::subject() const
|
|
{
|
|
TQValueList<TQCA_CertProperty> list = d->c->subject();
|
|
CertProperties props;
|
|
for(TQValueList<TQCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
|
|
props[(*it).var] = (*it).val;
|
|
return props;
|
|
}
|
|
|
|
CertProperties Cert::issuer() const
|
|
{
|
|
TQValueList<TQCA_CertProperty> list = d->c->issuer();
|
|
CertProperties props;
|
|
for(TQValueList<TQCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
|
|
props[(*it).var] = (*it).val;
|
|
return props;
|
|
}
|
|
|
|
TQDateTime Cert::notBefore() const
|
|
{
|
|
return d->c->notBefore();
|
|
}
|
|
|
|
TQDateTime Cert::notAfter() const
|
|
{
|
|
return d->c->notAfter();
|
|
}
|
|
|
|
TQByteArray Cert::toDER() const
|
|
{
|
|
TQByteArray out;
|
|
if(!d->c->toDER(&out))
|
|
return TQByteArray();
|
|
return out;
|
|
}
|
|
|
|
bool Cert::fromDER(const TQByteArray &a)
|
|
{
|
|
return d->c->createFromDER(a.data(), a.size());
|
|
}
|
|
|
|
TQString Cert::toPEM() const
|
|
{
|
|
TQByteArray out;
|
|
if(!d->c->toPEM(&out))
|
|
return TQByteArray();
|
|
|
|
TQCString cs;
|
|
cs.resize(out.size()+1);
|
|
memcpy(cs.data(), out.data(), out.size());
|
|
return TQString::fromLatin1(cs);
|
|
}
|
|
|
|
bool Cert::fromPEM(const TQString &str)
|
|
{
|
|
TQCString cs = str.latin1();
|
|
TQByteArray a(cs.length());
|
|
memcpy(a.data(), cs.data(), a.size());
|
|
return d->c->createFromPEM(a.data(), a.size());
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// TLS
|
|
//----------------------------------------------------------------------------
|
|
class TLS::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
c = (TQCA_TLSContext *)getContext(CAP_TLS);
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
delete c;
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
handshaken = false;
|
|
closing = false;
|
|
in.resize(0);
|
|
out.resize(0);
|
|
from_net.resize(0);
|
|
to_net.resize(0);
|
|
host = "";
|
|
hostMismatch = false;
|
|
// this causes the crash, because the Cert ctor is setting a null context
|
|
cert = Cert();
|
|
bytesEncoded = 0;
|
|
tryMore = false;
|
|
}
|
|
|
|
void appendArray(TQByteArray *a, const TQByteArray &b)
|
|
{
|
|
int oldsize = a->size();
|
|
a->resize(oldsize + b.size());
|
|
memcpy(a->data() + oldsize, b.data(), b.size());
|
|
}
|
|
|
|
Cert cert;
|
|
TQCA_TLSContext *c;
|
|
TQByteArray in, out, to_net, from_net;
|
|
int bytesEncoded;
|
|
bool tryMore;
|
|
bool handshaken;
|
|
TQString host;
|
|
bool hostMismatch;
|
|
bool closing;
|
|
|
|
Cert ourCert;
|
|
RSAKey ourKey;
|
|
TQPtrList<TQCA_CertContext> store;
|
|
};
|
|
|
|
TLS::TLS(TQObject *parent)
|
|
:TQObject(parent)
|
|
{
|
|
d = new Private;
|
|
}
|
|
|
|
TLS::~TLS()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void TLS::setCertificate(const Cert &cert, const RSAKey &key)
|
|
{
|
|
d->ourCert = cert;
|
|
d->ourKey = key;
|
|
}
|
|
|
|
void TLS::setCertificateStore(const TQPtrList<Cert> &store)
|
|
{
|
|
// convert the cert list into a context list
|
|
d->store.clear();
|
|
TQPtrListIterator<Cert> it(store);
|
|
for(Cert *cert; (cert = it.current()); ++it)
|
|
d->store.append(cert->d->c);
|
|
}
|
|
|
|
void TLS::reset()
|
|
{
|
|
d->reset();
|
|
}
|
|
|
|
bool TLS::startClient(const TQString &host)
|
|
{
|
|
d->reset();
|
|
d->host = host;
|
|
|
|
if(!d->c->startClient(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
|
|
return false;
|
|
TQTimer::singleShot(0, this, TQT_SLOT(update()));
|
|
return true;
|
|
}
|
|
|
|
bool TLS::startServer()
|
|
{
|
|
d->reset();
|
|
|
|
if(!d->c->startServer(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
|
|
return false;
|
|
TQTimer::singleShot(0, this, TQT_SLOT(update()));
|
|
return true;
|
|
}
|
|
|
|
void TLS::close()
|
|
{
|
|
if(!d->handshaken || d->closing)
|
|
return;
|
|
|
|
d->closing = true;
|
|
TQTimer::singleShot(0, this, TQT_SLOT(update()));
|
|
}
|
|
|
|
bool TLS::isHandshaken() const
|
|
{
|
|
return d->handshaken;
|
|
}
|
|
|
|
void TLS::write(const TQByteArray &a)
|
|
{
|
|
d->appendArray(&d->out, a);
|
|
update();
|
|
}
|
|
|
|
TQByteArray TLS::read()
|
|
{
|
|
TQByteArray a = d->in.copy();
|
|
d->in.resize(0);
|
|
return a;
|
|
}
|
|
|
|
void TLS::writeIncoming(const TQByteArray &a)
|
|
{
|
|
d->appendArray(&d->from_net, a);
|
|
update();
|
|
}
|
|
|
|
TQByteArray TLS::readOutgoing()
|
|
{
|
|
TQByteArray a = d->to_net.copy();
|
|
d->to_net.resize(0);
|
|
return a;
|
|
}
|
|
|
|
TQByteArray TLS::readUnprocessed()
|
|
{
|
|
TQByteArray a = d->from_net.copy();
|
|
d->from_net.resize(0);
|
|
return a;
|
|
}
|
|
|
|
const Cert & TLS::peerCertificate() const
|
|
{
|
|
return d->cert;
|
|
}
|
|
|
|
int TLS::certificateValidityResult() const
|
|
{
|
|
if(d->hostMismatch)
|
|
return TQCA::TLS::HostMismatch;
|
|
else
|
|
return d->c->validityResult();
|
|
}
|
|
|
|
void TLS::update()
|
|
{
|
|
bool force_read = false;
|
|
bool eof = false;
|
|
bool done = false;
|
|
TQGuardedPtr<TLS> self = this;
|
|
|
|
if(d->closing) {
|
|
TQByteArray a;
|
|
int r = d->c->shutdown(d->from_net, &a);
|
|
d->from_net.resize(0);
|
|
if(r == TQCA_TLSContext::Error) {
|
|
reset();
|
|
error(ErrHandshake);
|
|
return;
|
|
}
|
|
if(r == TQCA_TLSContext::Success) {
|
|
d->from_net = d->c->unprocessed().copy();
|
|
done = true;
|
|
}
|
|
d->appendArray(&d->to_net, a);
|
|
}
|
|
else {
|
|
if(!d->handshaken) {
|
|
TQByteArray a;
|
|
int r = d->c->handshake(d->from_net, &a);
|
|
d->from_net.resize(0);
|
|
if(r == TQCA_TLSContext::Error) {
|
|
reset();
|
|
error(ErrHandshake);
|
|
return;
|
|
}
|
|
d->appendArray(&d->to_net, a);
|
|
if(r == TQCA_TLSContext::Success) {
|
|
TQCA_CertContext *cc = d->c->peerCertificate();
|
|
if(cc && !d->host.isEmpty() && d->c->validityResult() == TQCA::TLS::Valid) {
|
|
if(!cc->matchesAddress(d->host))
|
|
d->hostMismatch = true;
|
|
}
|
|
d->cert.fromContext(cc);
|
|
d->handshaken = true;
|
|
handshaken();
|
|
if(!self)
|
|
return;
|
|
|
|
// there is a teeny tiny possibility that incoming data awaits. let us get it.
|
|
force_read = true;
|
|
}
|
|
}
|
|
|
|
if(d->handshaken) {
|
|
if(!d->out.isEmpty() || d->tryMore) {
|
|
d->tryMore = false;
|
|
TQByteArray a;
|
|
int enc;
|
|
bool more = false;
|
|
bool ok = d->c->encode(d->out, &a, &enc);
|
|
eof = d->c->eof();
|
|
if(ok && enc < (int)d->out.size())
|
|
more = true;
|
|
d->out.resize(0);
|
|
if(!eof) {
|
|
if(!ok) {
|
|
reset();
|
|
error(ErrCrypt);
|
|
return;
|
|
}
|
|
d->bytesEncoded += enc;
|
|
if(more)
|
|
d->tryMore = true;
|
|
d->appendArray(&d->to_net, a);
|
|
}
|
|
}
|
|
if(!d->from_net.isEmpty() || force_read) {
|
|
TQByteArray a, b;
|
|
bool ok = d->c->decode(d->from_net, &a, &b);
|
|
eof = d->c->eof();
|
|
d->from_net.resize(0);
|
|
if(!ok) {
|
|
reset();
|
|
error(ErrCrypt);
|
|
return;
|
|
}
|
|
d->appendArray(&d->in, a);
|
|
d->appendArray(&d->to_net, b);
|
|
}
|
|
|
|
if(!d->in.isEmpty()) {
|
|
readyRead();
|
|
if(!self)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!d->to_net.isEmpty()) {
|
|
int bytes = d->bytesEncoded;
|
|
d->bytesEncoded = 0;
|
|
readyReadOutgoing(bytes);
|
|
if(!self)
|
|
return;
|
|
}
|
|
|
|
if(eof) {
|
|
close();
|
|
if(!self)
|
|
return;
|
|
return;
|
|
}
|
|
|
|
if(d->closing && done) {
|
|
reset();
|
|
closed();
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// SASL
|
|
//----------------------------------------------------------------------------
|
|
TQString saslappname = "qca";
|
|
class SASL::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
c = (TQCA_SASLContext *)getContext(CAP_SASL);
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
delete c;
|
|
}
|
|
|
|
void setSecurityProps()
|
|
{
|
|
c->setSecurityProps(noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual, ssfmin, ssfmax, ext_authid, ext_ssf);
|
|
}
|
|
|
|
// security opts
|
|
bool noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual;
|
|
int ssfmin, ssfmax;
|
|
TQString ext_authid;
|
|
int ext_ssf;
|
|
|
|
bool tried;
|
|
TQCA_SASLContext *c;
|
|
TQHostAddress localAddr, remoteAddr;
|
|
int localPort, remotePort;
|
|
TQByteArray stepData;
|
|
bool allowCSF;
|
|
bool first, server;
|
|
|
|
TQByteArray inbuf, outbuf;
|
|
};
|
|
|
|
SASL::SASL(TQObject *parent)
|
|
:TQObject(parent)
|
|
{
|
|
d = new Private;
|
|
reset();
|
|
}
|
|
|
|
SASL::~SASL()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void SASL::setAppName(const TQString &name)
|
|
{
|
|
saslappname = name;
|
|
}
|
|
|
|
void SASL::reset()
|
|
{
|
|
d->localPort = -1;
|
|
d->remotePort = -1;
|
|
|
|
d->noPlain = false;
|
|
d->noActive = false;
|
|
d->noDict = false;
|
|
d->noAnon = false;
|
|
d->reqForward = false;
|
|
d->reqCreds = false;
|
|
d->reqMutual = false;
|
|
d->ssfmin = 0;
|
|
d->ssfmax = 0;
|
|
d->ext_authid = "";
|
|
d->ext_ssf = 0;
|
|
|
|
d->inbuf.resize(0);
|
|
d->outbuf.resize(0);
|
|
|
|
d->c->reset();
|
|
}
|
|
|
|
int SASL::errorCondition() const
|
|
{
|
|
return d->c->errorCond();
|
|
}
|
|
|
|
void SASL::setAllowPlain(bool b)
|
|
{
|
|
d->noPlain = !b;
|
|
}
|
|
|
|
void SASL::setAllowAnonymous(bool b)
|
|
{
|
|
d->noAnon = !b;
|
|
}
|
|
|
|
void SASL::setAllowActiveVulnerable(bool b)
|
|
{
|
|
d->noActive = !b;
|
|
}
|
|
|
|
void SASL::setAllowDictionaryVulnerable(bool b)
|
|
{
|
|
d->noDict = !b;
|
|
}
|
|
|
|
void SASL::setRequireForwardSecrecy(bool b)
|
|
{
|
|
d->reqForward = b;
|
|
}
|
|
|
|
void SASL::setRequirePassCredentials(bool b)
|
|
{
|
|
d->reqCreds = b;
|
|
}
|
|
|
|
void SASL::setRequireMutualAuth(bool b)
|
|
{
|
|
d->reqMutual = b;
|
|
}
|
|
|
|
void SASL::setMinimumSSF(int x)
|
|
{
|
|
d->ssfmin = x;
|
|
}
|
|
|
|
void SASL::setMaximumSSF(int x)
|
|
{
|
|
d->ssfmax = x;
|
|
}
|
|
|
|
void SASL::setExternalAuthID(const TQString &authid)
|
|
{
|
|
d->ext_authid = authid;
|
|
}
|
|
|
|
void SASL::setExternalSSF(int x)
|
|
{
|
|
d->ext_ssf = x;
|
|
}
|
|
|
|
void SASL::setLocalAddr(const TQHostAddress &addr, TQ_UINT16 port)
|
|
{
|
|
d->localAddr = addr;
|
|
d->localPort = port;
|
|
}
|
|
|
|
void SASL::setRemoteAddr(const TQHostAddress &addr, TQ_UINT16 port)
|
|
{
|
|
d->remoteAddr = addr;
|
|
d->remotePort = port;
|
|
}
|
|
|
|
bool SASL::startClient(const TQString &service, const TQString &host, const TQStringList &mechlist, bool allowClientSendFirst)
|
|
{
|
|
TQCA_SASLHostPort la, ra;
|
|
if(d->localPort != -1) {
|
|
la.addr = d->localAddr;
|
|
la.port = d->localPort;
|
|
}
|
|
if(d->remotePort != -1) {
|
|
ra.addr = d->remoteAddr;
|
|
ra.port = d->remotePort;
|
|
}
|
|
|
|
d->allowCSF = allowClientSendFirst;
|
|
d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
|
|
d->setSecurityProps();
|
|
|
|
if(!d->c->clientStart(mechlist))
|
|
return false;
|
|
d->first = true;
|
|
d->server = false;
|
|
d->tried = false;
|
|
TQTimer::singleShot(0, this, TQT_SLOT(tryAgain()));
|
|
return true;
|
|
}
|
|
|
|
bool SASL::startServer(const TQString &service, const TQString &host, const TQString &realm, TQStringList *mechlist)
|
|
{
|
|
TQCA_SASLHostPort la, ra;
|
|
if(d->localPort != -1) {
|
|
la.addr = d->localAddr;
|
|
la.port = d->localPort;
|
|
}
|
|
if(d->remotePort != -1) {
|
|
ra.addr = d->remoteAddr;
|
|
ra.port = d->remotePort;
|
|
}
|
|
|
|
d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
|
|
d->setSecurityProps();
|
|
|
|
if(!d->c->serverStart(realm, mechlist, saslappname))
|
|
return false;
|
|
d->first = true;
|
|
d->server = true;
|
|
d->tried = false;
|
|
return true;
|
|
}
|
|
|
|
void SASL::putServerFirstStep(const TQString &mech)
|
|
{
|
|
int r = d->c->serverFirstStep(mech, 0);
|
|
handleServerFirstStep(r);
|
|
}
|
|
|
|
void SASL::putServerFirstStep(const TQString &mech, const TQByteArray &clientInit)
|
|
{
|
|
int r = d->c->serverFirstStep(mech, &clientInit);
|
|
handleServerFirstStep(r);
|
|
}
|
|
|
|
void SASL::handleServerFirstStep(int r)
|
|
{
|
|
if(r == TQCA_SASLContext::Success)
|
|
authenticated();
|
|
else if(r == TQCA_SASLContext::Continue)
|
|
nextStep(d->c->result());
|
|
else if(r == TQCA_SASLContext::AuthCheck)
|
|
tryAgain();
|
|
else
|
|
error(ErrAuth);
|
|
}
|
|
|
|
void SASL::putStep(const TQByteArray &stepData)
|
|
{
|
|
d->stepData = stepData.copy();
|
|
tryAgain();
|
|
}
|
|
|
|
void SASL::setUsername(const TQString &user)
|
|
{
|
|
d->c->setClientParams(&user, 0, 0, 0);
|
|
}
|
|
|
|
void SASL::setAuthzid(const TQString &authzid)
|
|
{
|
|
d->c->setClientParams(0, &authzid, 0, 0);
|
|
}
|
|
|
|
void SASL::setPassword(const TQString &pass)
|
|
{
|
|
d->c->setClientParams(0, 0, &pass, 0);
|
|
}
|
|
|
|
void SASL::setRealm(const TQString &realm)
|
|
{
|
|
d->c->setClientParams(0, 0, 0, &realm);
|
|
}
|
|
|
|
void SASL::continueAfterParams()
|
|
{
|
|
tryAgain();
|
|
}
|
|
|
|
void SASL::continueAfterAuthCheck()
|
|
{
|
|
tryAgain();
|
|
}
|
|
|
|
void SASL::tryAgain()
|
|
{
|
|
int r;
|
|
|
|
if(d->server) {
|
|
if(!d->tried) {
|
|
r = d->c->nextStep(d->stepData);
|
|
d->tried = true;
|
|
}
|
|
else {
|
|
r = d->c->tryAgain();
|
|
}
|
|
|
|
if(r == TQCA_SASLContext::Error) {
|
|
error(ErrAuth);
|
|
return;
|
|
}
|
|
else if(r == TQCA_SASLContext::Continue) {
|
|
d->tried = false;
|
|
nextStep(d->c->result());
|
|
return;
|
|
}
|
|
else if(r == TQCA_SASLContext::AuthCheck) {
|
|
authCheck(d->c->username(), d->c->authzid());
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if(d->first) {
|
|
if(!d->tried) {
|
|
r = d->c->clientFirstStep(d->allowCSF);
|
|
d->tried = true;
|
|
}
|
|
else
|
|
r = d->c->tryAgain();
|
|
|
|
if(r == TQCA_SASLContext::Error) {
|
|
error(ErrAuth);
|
|
return;
|
|
}
|
|
else if(r == TQCA_SASLContext::NeedParams) {
|
|
//d->tried = false;
|
|
TQCA_SASLNeedParams np = d->c->clientParamsNeeded();
|
|
needParams(np.user, np.authzid, np.pass, np.realm);
|
|
return;
|
|
}
|
|
|
|
TQString mech = d->c->mech();
|
|
const TQByteArray *clientInit = d->c->clientInit();
|
|
|
|
d->first = false;
|
|
d->tried = false;
|
|
clientFirstStep(mech, clientInit);
|
|
}
|
|
else {
|
|
if(!d->tried) {
|
|
r = d->c->nextStep(d->stepData);
|
|
d->tried = true;
|
|
}
|
|
else
|
|
r = d->c->tryAgain();
|
|
|
|
if(r == TQCA_SASLContext::Error) {
|
|
error(ErrAuth);
|
|
return;
|
|
}
|
|
else if(r == TQCA_SASLContext::NeedParams) {
|
|
//d->tried = false;
|
|
TQCA_SASLNeedParams np = d->c->clientParamsNeeded();
|
|
needParams(np.user, np.authzid, np.pass, np.realm);
|
|
return;
|
|
}
|
|
d->tried = false;
|
|
//else if(r == TQCA_SASLContext::Continue) {
|
|
nextStep(d->c->result());
|
|
// return;
|
|
//}
|
|
}
|
|
}
|
|
|
|
if(r == TQCA_SASLContext::Success)
|
|
authenticated();
|
|
else if(r == TQCA_SASLContext::Error)
|
|
error(ErrAuth);
|
|
}
|
|
|
|
int SASL::ssf() const
|
|
{
|
|
return d->c->security();
|
|
}
|
|
|
|
void SASL::write(const TQByteArray &a)
|
|
{
|
|
TQByteArray b;
|
|
if(!d->c->encode(a, &b)) {
|
|
error(ErrCrypt);
|
|
return;
|
|
}
|
|
int oldsize = d->outbuf.size();
|
|
d->outbuf.resize(oldsize + b.size());
|
|
memcpy(d->outbuf.data() + oldsize, b.data(), b.size());
|
|
readyReadOutgoing(a.size());
|
|
}
|
|
|
|
TQByteArray SASL::read()
|
|
{
|
|
TQByteArray a = d->inbuf.copy();
|
|
d->inbuf.resize(0);
|
|
return a;
|
|
}
|
|
|
|
void SASL::writeIncoming(const TQByteArray &a)
|
|
{
|
|
TQByteArray b;
|
|
if(!d->c->decode(a, &b)) {
|
|
error(ErrCrypt);
|
|
return;
|
|
}
|
|
int oldsize = d->inbuf.size();
|
|
d->inbuf.resize(oldsize + b.size());
|
|
memcpy(d->inbuf.data() + oldsize, b.data(), b.size());
|
|
readyRead();
|
|
}
|
|
|
|
TQByteArray SASL::readOutgoing()
|
|
{
|
|
TQByteArray a = d->outbuf.copy();
|
|
d->outbuf.resize(0);
|
|
return a;
|
|
}
|
|
|
|
#include "tqca.moc"
|