diff --git a/src/qca_core.cpp b/src/qca_core.cpp index dfb93b74..ba20db90 100644 --- a/src/qca_core.cpp +++ b/src/qca_core.cpp @@ -1,2234 +1,2234 @@ /* * Copyright (C) 2003-2008 Justin Karneges * Copyright (C) 2004,2005 Brad Hards * Copyright (C) 2014-2016 Ivan Romanov * * 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 * */ #include "qca_core.h" #include "qca_plugin.h" #include "qca_textfilter.h" #include "qca_cert.h" #include "qca_keystore.h" #include "qcaprovider.h" // for qAddPostRoutine #include #include #include #include #include #include #ifdef Q_OS_UNIX # include #endif int qcaVersion() { return QCA_VERSION; } const char *qcaVersionStr() { return QCA_VERSION_STR; } int qcaMajorVersion() { return QCA_MAJOR_VERSION; } int qcaMinorVersion() { return QCA_MINOR_VERSION; } int qcaPatchVersion() { return QCA_PATCH_VERSION; } namespace QCA { // from qca_tools bool botan_init(int prealloc, bool mmap); void botan_deinit(); // from qca_default Provider *create_default_provider(); //---------------------------------------------------------------------------- // Global //---------------------------------------------------------------------------- class Global { public: int refs; bool secmem; bool loaded; bool first_scan; QString app_name; QMutex name_mutex; ProviderManager *manager; QMutex scan_mutex; Random *rng; QMutex rng_mutex; Logger *logger; QVariantMap properties; QMutex prop_mutex; QMap config; QMutex config_mutex; QMutex logger_mutex; Global() { refs = 0; secmem = false; loaded = false; first_scan = false; rng = 0; logger = 0; manager = new ProviderManager; } ~Global() { KeyStoreManager::shutdown(); delete rng; rng = 0; delete manager; manager = 0; delete logger; logger = 0; } void ensure_loaded() { // probably we shouldn't overload scan mutex, or else rename it QMutexLocker locker(&scan_mutex); if(!loaded) { loaded = true; manager->setDefault(create_default_provider()); // manager owns it } } bool ensure_first_scan() { scan_mutex.lock(); if(!first_scan) { first_scan = true; manager->scan(); scan_mutex.unlock(); return true; } scan_mutex.unlock(); return false; } void scan() { scan_mutex.lock(); first_scan = true; manager->scan(); scan_mutex.unlock(); } void ksm_scan() { KeyStoreManager::scan(); } Logger *get_logger() { QMutexLocker locker(&logger_mutex); if(!logger) { logger = new Logger; // needed so deinit may delete the logger regardless // of what thread the logger was created from logger->moveToThread(0); } return logger; } void unloadAllPlugins() { KeyStoreManager::shutdown(); // if the global_rng was owned by a plugin, then delete it rng_mutex.lock(); if(rng && (rng->provider() != manager->find("default"))) { delete rng; rng = 0; } rng_mutex.unlock(); manager->unloadAll(); } }; Q_GLOBAL_STATIC(QMutex, global_mutex) static Global *global = 0; static bool features_have(const QStringList &have, const QStringList &want) { foreach(const QString &i, want) { if(!have.contains(i)) return false; } return true; } void init(MemoryMode mode, int prealloc) { QMutexLocker locker(global_mutex()); if(global) { ++(global->refs); return; } bool allow_mmap_fallback = false; bool drop_root = false; if(mode == Practical) { allow_mmap_fallback = true; drop_root = true; } else if(mode == Locking) drop_root = true; bool secmem = botan_init(prealloc, allow_mmap_fallback); if(drop_root) { -#ifdef Q_OS_UNIX +#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID) setuid(getuid()); #endif } global = new Global; global->secmem = secmem; ++(global->refs); // for maximum setuid safety, qca should be initialized before qapp: // // int main(int argc, char **argv) // { // QCA::Initializer init; // QCoreApplication app(argc, argv); // return 0; // } // // however, the above code has the unfortunate side-effect of causing // qapp to deinit before qca, which can cause problems with any // plugins that have active objects (notably KeyStore). we'll use a // post routine to force qca to deinit first. qAddPostRoutine(deinit); } void init() { init(Practical, 64); } void deinit() { QMutexLocker locker(global_mutex()); if(!global) return; --(global->refs); if(global->refs == 0) { // In order to maintain symmetry with the init() function, remove the // post routine from QCoreApplication. This is needed in case when the // QCA library is unloaded before QCoreApplication instance completes: // QCoreApplication d-tor would try to execute the deinit() function, // which would no longer be there. // Note that this function is documented only in Qt 5.3 and later, but // it has been present since ancient times with the same semantics. qRemovePostRoutine(deinit); delete global; global = 0; botan_deinit(); } } static bool global_check() { Q_ASSERT(global); if(!global) return false; return true; } static bool global_check_load() { Q_ASSERT(global); if(!global) return false; global->ensure_loaded(); return true; } QMutex *global_random_mutex() { return &global->rng_mutex; } Random *global_random() { if(!global->rng) global->rng = new Random; return global->rng; } bool haveSecureMemory() { if(!global_check()) return false; return global->secmem; } bool haveSecureRandom() { if(!global_check_load()) return false; QMutexLocker locker(global_random_mutex()); if(global_random()->provider()->name() != "default") return true; return false; } bool isSupported(const QStringList &features, const QString &provider) { if(!global_check_load()) return false; // single if(!provider.isEmpty()) { Provider *p = global->manager->find(provider); if(!p) { // ok, try scanning for new stuff global->scan(); p = global->manager->find(provider); } if(p && features_have(p->features(), features)) return true; } // all else { if(features_have(global->manager->allFeatures(), features)) return true; global->manager->appendDiagnosticText(QString("Scanning to find features: %1\n").arg(features.join(" "))); // ok, try scanning for new stuff global->scan(); if(features_have(global->manager->allFeatures(), features)) return true; } return false; } bool isSupported(const char *features, const QString &provider) { return isSupported(QString(features).split(',', QString::SkipEmptyParts), provider); } QStringList supportedFeatures() { if(!global_check_load()) return QStringList(); // query all features global->scan(); return global->manager->allFeatures(); } QStringList defaultFeatures() { if(!global_check_load()) return QStringList(); return global->manager->find("default")->features(); } ProviderList providers() { if(!global_check_load()) return ProviderList(); global->ensure_first_scan(); return global->manager->providers(); } bool insertProvider(Provider *p, int priority) { if(!global_check_load()) return false; global->ensure_first_scan(); return global->manager->add(p, priority); } bool unloadProvider(const QString &name) { if(!global_check_load()) return false; global->ensure_first_scan(); return global->manager->unload(name); } void setProviderPriority(const QString &name, int priority) { if(!global_check_load()) return; global->ensure_first_scan(); global->manager->changePriority(name, priority); } int providerPriority(const QString &name) { if(!global_check_load()) return -1; global->ensure_first_scan(); return global->manager->getPriority(name); } Provider *findProvider(const QString &name) { if(!global_check_load()) return 0; global->ensure_first_scan(); return global->manager->find(name); } Provider *defaultProvider() { if(!global_check_load()) return 0; return global->manager->find("default"); } QStringList pluginPaths() { QStringList paths; #ifndef DEVELOPER_MODE const QString qcaPluginPath = qgetenv("QCA_PLUGIN_PATH"); if (!qcaPluginPath.isEmpty()) { #ifdef Q_OS_WIN QLatin1Char pathSep(';'); #else QLatin1Char pathSep(':'); #endif foreach (const QString &path, qcaPluginPath.split(pathSep)) { QString canonicalPath = QDir(path).canonicalPath(); if (!canonicalPath.isEmpty()) paths << canonicalPath; } } paths += QCoreApplication::libraryPaths(); #endif // In developer mode load plugins only from buildtree. // In regular mode QCA_PLUGIN_PATH is path where plugins was installed paths << QDir(QCA_PLUGIN_PATH).canonicalPath(); #ifndef DEVELOPER_MODE paths.removeDuplicates(); #endif // No empty strings paths.removeAll(QString()); return paths; } void scanForPlugins() { if(!global_check_load()) return; global->scan(); global->ksm_scan(); } void unloadAllPlugins() { if(!global_check_load()) return; global->unloadAllPlugins(); } QString pluginDiagnosticText() { if(!global_check_load()) return QString(); return global->manager->diagnosticText(); } void clearPluginDiagnosticText() { if(!global_check_load()) return; global->manager->clearDiagnosticText(); } void appendPluginDiagnosticText(const QString &text) { if(!global_check_load()) return; global->manager->appendDiagnosticText(text); } void setProperty(const QString &name, const QVariant &value) { if(!global_check_load()) return; QMutexLocker locker(&global->prop_mutex); global->properties[name] = value; } QVariant getProperty(const QString &name) { if(!global_check_load()) return QVariant(); QMutexLocker locker(&global->prop_mutex); return global->properties.value(name); } static bool configIsValid(const QVariantMap &config) { if(!config.contains("formtype")) return false; QMapIterator it(config); while(it.hasNext()) { it.next(); const QVariant &v = it.value(); if(v.type() != QVariant::String && v.type() != QVariant::Int && v.type() != QVariant::Bool) return false; } return true; } static QVariantMap readConfig(const QString &name) { QSettings settings("Affinix", "QCA2"); settings.beginGroup("ProviderConfig"); QStringList providerNames = settings.value("providerNames").toStringList(); if(!providerNames.contains(name)) return QVariantMap(); settings.beginGroup(name); QStringList keys = settings.childKeys(); QVariantMap map; foreach(const QString &key, keys) map[key] = settings.value(key); settings.endGroup(); if(!configIsValid(map)) return QVariantMap(); return map; } static bool writeConfig(const QString &name, const QVariantMap &config, bool systemWide = false) { QSettings settings(QSettings::NativeFormat, systemWide ? QSettings::SystemScope : QSettings::UserScope, "Affinix", "QCA2"); settings.beginGroup("ProviderConfig"); // version settings.setValue("version", 2); // add the entry if needed QStringList providerNames = settings.value("providerNames").toStringList(); if(!providerNames.contains(name)) providerNames += name; settings.setValue("providerNames", providerNames); settings.beginGroup(name); QMapIterator it(config); while(it.hasNext()) { it.next(); settings.setValue(it.key(), it.value()); } settings.endGroup(); if(settings.status() == QSettings::NoError) return true; return false; } void setProviderConfig(const QString &name, const QVariantMap &config) { if(!global_check_load()) return; if(!configIsValid(config)) return; global->config_mutex.lock(); global->config[name] = config; global->config_mutex.unlock(); Provider *p = findProvider(name); if(p) p->configChanged(config); } QVariantMap getProviderConfig(const QString &name) { if(!global_check_load()) return QVariantMap(); QVariantMap conf; global->config_mutex.lock(); // try loading from persistent storage conf = readConfig(name); // if not, load the one from memory if(conf.isEmpty()) conf = global->config.value(name); global->config_mutex.unlock(); // if provider doesn't exist or doesn't have a valid config form, // use the config we loaded Provider *p = findProvider(name); if(!p) return conf; QVariantMap pconf = p->defaultConfig(); if(!configIsValid(pconf)) return conf; // if the config loaded was empty, use the provider's config if(conf.isEmpty()) return pconf; // if the config formtype doesn't match the provider's formtype, // then use the provider's if(pconf["formtype"] != conf["formtype"]) return pconf; // otherwise, use the config loaded return conf; } void saveProviderConfig(const QString &name) { if(!global_check_load()) return; QMutexLocker locker(&global->config_mutex); QVariantMap conf = global->config.value(name); if(conf.isEmpty()) return; writeConfig(name, conf); } QVariantMap getProviderConfig_internal(Provider *p) { QVariantMap conf; QString name = p->name(); global->config_mutex.lock(); // try loading from persistent storage conf = readConfig(name); // if not, load the one from memory if(conf.isEmpty()) conf = global->config.value(name); global->config_mutex.unlock(); // if provider doesn't exist or doesn't have a valid config form, // use the config we loaded QVariantMap pconf = p->defaultConfig(); if(!configIsValid(pconf)) return conf; // if the config loaded was empty, use the provider's config if(conf.isEmpty()) return pconf; // if the config formtype doesn't match the provider's formtype, // then use the provider's if(pconf["formtype"] != conf["formtype"]) return pconf; // otherwise, use the config loaded return conf; } QString globalRandomProvider() { QMutexLocker locker(global_random_mutex()); return global_random()->provider()->name(); } void setGlobalRandomProvider(const QString &provider) { QMutexLocker locker(global_random_mutex()); delete global->rng; global->rng = new Random(provider); } Logger *logger() { return global->get_logger(); } bool haveSystemStore() { // ensure the system store is loaded KeyStoreManager::start("default"); KeyStoreManager ksm; ksm.waitForBusyFinished(); QStringList list = ksm.keyStores(); for(int n = 0; n < list.count(); ++n) { KeyStore ks(list[n], &ksm); if(ks.type() == KeyStore::System && ks.holdsTrustedCertificates()) return true; } return false; } CertificateCollection systemStore() { // ensure the system store is loaded KeyStoreManager::start("default"); KeyStoreManager ksm; ksm.waitForBusyFinished(); CertificateCollection col; QStringList list = ksm.keyStores(); for(int n = 0; n < list.count(); ++n) { KeyStore ks(list[n], &ksm); // system store if(ks.type() == KeyStore::System && ks.holdsTrustedCertificates()) { // extract contents QList entries = ks.entryList(); for(int i = 0; i < entries.count(); ++i) { if(entries[i].type() == KeyStoreEntry::TypeCertificate) col.addCertificate(entries[i].certificate()); else if(entries[i].type() == KeyStoreEntry::TypeCRL) col.addCRL(entries[i].crl()); } break; } } return col; } QString appName() { if(!global_check()) return QString(); QMutexLocker locker(&global->name_mutex); return global->app_name; } void setAppName(const QString &s) { if(!global_check()) return; QMutexLocker locker(&global->name_mutex); global->app_name = s; } QString arrayToHex(const QByteArray &a) { return Hex().arrayToString(a); } QByteArray hexToArray(const QString &str) { return Hex().stringToArray(str).toByteArray(); } QString arrayToBase64(const QByteArray &a) { return Base64().arrayToString(a); } QByteArray base64ToArray(const QString &base64String) { return Base64().stringToArray(base64String).toByteArray(); } static Provider *getProviderForType(const QString &type, const QString &provider) { Provider *p = 0; bool scanned = global->ensure_first_scan(); if(!provider.isEmpty()) { // try using specific provider p = global->manager->findFor(provider, type); if(!p && !scanned) { // maybe this provider is new, so scan and try again global->scan(); scanned = true; p = global->manager->findFor(provider, type); } } if(!p) { // try using some other provider p = global->manager->findFor(QString(), type); // note: we used to rescan if no provider was found or if // the only found provider was 'default'. now we only // rescan if no provider was found. this optimizes lookups // for features that are in the default provider (such as // 'sha1') when no other plugin is available. the drawback // is that if a plugin is installed later during runtime, // then it won't be picked up without restarting the // application or manually calling QCA::scanForPlugins. //if((!p || p->name() == "default") && !scanned) if(!p && !scanned) { // maybe there are new providers, so scan and try again // before giving up or using default global->scan(); scanned = true; p = global->manager->findFor(QString(), type); } } return p; } static inline Provider::Context *doCreateContext(Provider *p, const QString &type) { return p->createContext(type); } Provider::Context *getContext(const QString &type, const QString &provider) { if(!global_check_load()) return 0; Provider *p; { p = getProviderForType(type, provider); if(!p) return 0; } return doCreateContext(p, type); } Provider::Context *getContext(const QString &type, Provider *_p) { if(!global_check_load()) return 0; Provider *p; { p = global->manager->find(_p); if(!p) return 0; } return doCreateContext(p, type); } //---------------------------------------------------------------------------- // Initializer //---------------------------------------------------------------------------- Initializer::Initializer(MemoryMode m, int prealloc) { init(m, prealloc); } Initializer::~Initializer() { deinit(); } //---------------------------------------------------------------------------- // Provider //---------------------------------------------------------------------------- Provider::~Provider() { } void Provider::init() { } void Provider::deinit() { } int Provider::version() const { return 0; } QString Provider::credit() const { return QString(); } QVariantMap Provider::defaultConfig() const { return QVariantMap(); } void Provider::configChanged(const QVariantMap &) { } Provider::Context::Context(Provider *parent, const QString &type) :QObject() { _provider = parent; _type = type; } Provider::Context::Context(const Context &from) :QObject() { _provider = from._provider; _type = from._type; } Provider::Context::~Context() { } Provider *Provider::Context::provider() const { return _provider; } QString Provider::Context::type() const { return _type; } bool Provider::Context::sameProvider(const Context *c) const { return (c->provider() == _provider); } //---------------------------------------------------------------------------- // BasicContext //---------------------------------------------------------------------------- BasicContext::BasicContext(Provider *parent, const QString &type) :Context(parent, type) { moveToThread(0); // no thread association } BasicContext::BasicContext(const BasicContext &from) :Context(from) { moveToThread(0); // no thread association } BasicContext::~BasicContext() { } //---------------------------------------------------------------------------- // InfoContext //---------------------------------------------------------------------------- QStringList InfoContext::supportedHashTypes() const { return QStringList(); } QStringList InfoContext::supportedCipherTypes() const { return QStringList(); } QStringList InfoContext::supportedMACTypes() const { return QStringList(); } //---------------------------------------------------------------------------- // PKeyBase //---------------------------------------------------------------------------- PKeyBase::PKeyBase(Provider *p, const QString &type) :BasicContext(p, type) { } int PKeyBase::maximumEncryptSize(EncryptionAlgorithm) const { return 0; } SecureArray PKeyBase::encrypt(const SecureArray &, EncryptionAlgorithm) { return SecureArray(); } bool PKeyBase::decrypt(const SecureArray &, SecureArray *, EncryptionAlgorithm) { return false; } void PKeyBase::startSign(SignatureAlgorithm, SignatureFormat) { } void PKeyBase::startVerify(SignatureAlgorithm, SignatureFormat) { } void PKeyBase::update(const MemoryRegion &) { } QByteArray PKeyBase::endSign() { return QByteArray(); } bool PKeyBase::endVerify(const QByteArray &) { return false; } SymmetricKey PKeyBase::deriveKey(const PKeyBase &) { return SymmetricKey(); } //---------------------------------------------------------------------------- // PKeyContext //---------------------------------------------------------------------------- QByteArray PKeyContext::publicToDER() const { return QByteArray(); } QString PKeyContext::publicToPEM() const { return QString(); } ConvertResult PKeyContext::publicFromDER(const QByteArray &) { return ErrorDecode; } ConvertResult PKeyContext::publicFromPEM(const QString &) { return ErrorDecode; } SecureArray PKeyContext::privateToDER(const SecureArray &, PBEAlgorithm) const { return SecureArray(); } QString PKeyContext::privateToPEM(const SecureArray &, PBEAlgorithm) const { return QString(); } ConvertResult PKeyContext::privateFromDER(const SecureArray &, const SecureArray &) { return ErrorDecode; } ConvertResult PKeyContext::privateFromPEM(const QString &, const SecureArray &) { return ErrorDecode; } //---------------------------------------------------------------------------- // KeyStoreEntryContext //---------------------------------------------------------------------------- bool KeyStoreEntryContext::isAvailable() const { return true; } KeyBundle KeyStoreEntryContext::keyBundle() const { return KeyBundle(); } Certificate KeyStoreEntryContext::certificate() const { return Certificate(); } CRL KeyStoreEntryContext::crl() const { return CRL(); } PGPKey KeyStoreEntryContext::pgpSecretKey() const { return PGPKey(); } PGPKey KeyStoreEntryContext::pgpPublicKey() const { return PGPKey(); } bool KeyStoreEntryContext::ensureAccess() { return true; } //---------------------------------------------------------------------------- // KeyStoreListContext //---------------------------------------------------------------------------- void KeyStoreListContext::start() { QMetaObject::invokeMethod(this, "busyEnd", Qt::QueuedConnection); } void KeyStoreListContext::setUpdatesEnabled(bool) { } bool KeyStoreListContext::isReadOnly(int) const { return true; } KeyStoreEntryContext *KeyStoreListContext::entry(int id, const QString &entryId) { KeyStoreEntryContext *out = 0; QList list = entryList(id); for(int n = 0; n < list.count(); ++n) { if(list[n]->id() == entryId) { out = list.takeAt(n); break; } } qDeleteAll(list); return out; } KeyStoreEntryContext *KeyStoreListContext::entryPassive(const QString &serialized) { Q_UNUSED(serialized); return 0; } QString KeyStoreListContext::writeEntry(int, const KeyBundle &) { return QString(); } QString KeyStoreListContext::writeEntry(int, const Certificate &) { return QString(); } QString KeyStoreListContext::writeEntry(int, const CRL &) { return QString(); } QString KeyStoreListContext::writeEntry(int, const PGPKey &) { return QString(); } bool KeyStoreListContext::removeEntry(int, const QString &) { return false; } //---------------------------------------------------------------------------- // TLSContext //---------------------------------------------------------------------------- void TLSContext::setMTU(int) { } //---------------------------------------------------------------------------- // MessageContext //---------------------------------------------------------------------------- QString MessageContext::diagnosticText() const { return QString(); } //---------------------------------------------------------------------------- // SMSContext //---------------------------------------------------------------------------- void SMSContext::setTrustedCertificates(const CertificateCollection &) { } void SMSContext::setUntrustedCertificates(const CertificateCollection &) { } void SMSContext::setPrivateKeys(const QList &) { } //---------------------------------------------------------------------------- // BufferedComputation //---------------------------------------------------------------------------- BufferedComputation::~BufferedComputation() { } MemoryRegion BufferedComputation::process(const MemoryRegion &a) { clear(); update(a); return final(); } //---------------------------------------------------------------------------- // Filter //---------------------------------------------------------------------------- Filter::~Filter() { } MemoryRegion Filter::process(const MemoryRegion &a) { clear(); MemoryRegion buf = update(a); if(!ok()) return MemoryRegion(); MemoryRegion fin = final(); if(!ok()) return MemoryRegion(); if(buf.isSecure() || fin.isSecure()) return (SecureArray(buf) + SecureArray(fin)); else return (buf.toByteArray() + fin.toByteArray()); } //---------------------------------------------------------------------------- // Algorithm //---------------------------------------------------------------------------- class Algorithm::Private : public QSharedData { public: Provider::Context *c; Private(Provider::Context *context) { c = context; //printf("** [%p] Algorithm Created\n", c); } Private(const Private &from) : QSharedData(from) { c = from.c->clone(); //printf("** [%p] Algorithm Copied (to [%p])\n", from.c, c); } ~Private() { //printf("** [%p] Algorithm Destroyed\n", c); delete c; } }; Algorithm::Algorithm() { } Algorithm::Algorithm(const QString &type, const QString &provider) { change(type, provider); } Algorithm::Algorithm(const Algorithm &from) { *this = from; } Algorithm::~Algorithm() { } Algorithm & Algorithm::operator=(const Algorithm &from) { d = from.d; return *this; } QString Algorithm::type() const { if(d) return d->c->type(); else return QString(); } Provider *Algorithm::provider() const { if(d) return d->c->provider(); else return 0; } Provider::Context *Algorithm::context() { if(d) return d->c; else return 0; } const Provider::Context *Algorithm::context() const { if(d) return d->c; else return 0; } void Algorithm::change(Provider::Context *c) { if(c) d = new Private(c); else d = 0; } void Algorithm::change(const QString &type, const QString &provider) { if(!type.isEmpty()) change(getContext(type, provider)); else change(0); } Provider::Context *Algorithm::takeContext() { if(d) { Provider::Context *c = d->c; // should cause a detach d->c = 0; d = 0; return c; } else return 0; } //---------------------------------------------------------------------------- // SymmetricKey //---------------------------------------------------------------------------- SymmetricKey::SymmetricKey() { } SymmetricKey::SymmetricKey(int size) { set(Random::randomArray(size)); } SymmetricKey::SymmetricKey(const SecureArray &a) { set(a); } SymmetricKey::SymmetricKey(const QByteArray &a) { set(SecureArray(a)); } /* from libgcrypt-1.2.0 */ static unsigned char desWeakKeyTable[64][8] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /*w*/ { 0x00, 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e }, { 0x00, 0x00, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0 }, { 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe }, { 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e }, /*sw*/ { 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e, 0x00 }, { 0x00, 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0, 0xfe }, { 0x00, 0x1e, 0xfe, 0xe0, 0x00, 0x0e, 0xfe, 0xf0 }, { 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0 }, /*sw*/ { 0x00, 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e, 0xfe }, { 0x00, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00 }, { 0x00, 0xe0, 0xfe, 0x1e, 0x00, 0xf0, 0xfe, 0x0e }, { 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe }, /*sw*/ { 0x00, 0xfe, 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0 }, { 0x00, 0xfe, 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e }, { 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00 }, { 0x1e, 0x00, 0x00, 0x1e, 0x0e, 0x00, 0x00, 0x0e }, { 0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e, 0x00 }, /*sw*/ { 0x1e, 0x00, 0xe0, 0xfe, 0x0e, 0x00, 0xf0, 0xfe }, { 0x1e, 0x00, 0xfe, 0xe0, 0x0e, 0x00, 0xfe, 0xf0 }, { 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e, 0x00, 0x00 }, { 0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0e, 0x0e }, /*w*/ { 0x1e, 0x1e, 0xe0, 0xe0, 0x0e, 0x0e, 0xf0, 0xf0 }, { 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e, 0xfe, 0xfe }, { 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0, 0x00, 0xfe }, { 0x1e, 0xe0, 0x1e, 0xe0, 0x0e, 0xf0, 0x0e, 0xf0 }, /*sw*/ { 0x1e, 0xe0, 0xe0, 0x1e, 0x0e, 0xf0, 0xf0, 0x0e }, { 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0, 0xfe, 0x00 }, { 0x1e, 0xfe, 0x00, 0xe0, 0x0e, 0xfe, 0x00, 0xf0 }, { 0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe }, /*sw*/ { 0x1e, 0xfe, 0xe0, 0x00, 0x0e, 0xfe, 0xf0, 0x00 }, { 0x1e, 0xfe, 0xfe, 0x1e, 0x0e, 0xfe, 0xfe, 0x0e }, { 0xe0, 0x00, 0x00, 0xe0, 0xf0, 0x00, 0x00, 0xf0 }, { 0xe0, 0x00, 0x1e, 0xfe, 0xf0, 0x00, 0x0e, 0xfe }, { 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0, 0x00 }, /*sw*/ { 0xe0, 0x00, 0xfe, 0x1e, 0xf0, 0x00, 0xfe, 0x0e }, { 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e, 0x00, 0xfe }, { 0xe0, 0x1e, 0x1e, 0xe0, 0xf0, 0x0e, 0x0e, 0xf0 }, { 0xe0, 0x1e, 0xe0, 0x1e, 0xf0, 0x0e, 0xf0, 0x0e }, /*sw*/ { 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e, 0xfe, 0x00 }, { 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00 }, { 0xe0, 0xe0, 0x1e, 0x1e, 0xf0, 0xf0, 0x0e, 0x0e }, { 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0 }, /*w*/ { 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0, 0xfe, 0xfe }, { 0xe0, 0xfe, 0x00, 0x1e, 0xf0, 0xfe, 0x00, 0x0e }, { 0xe0, 0xfe, 0x1e, 0x00, 0xf0, 0xfe, 0x0e, 0x00 }, { 0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0, 0xfe }, /*sw*/ { 0xe0, 0xfe, 0xfe, 0xe0, 0xf0, 0xfe, 0xfe, 0xf0 }, { 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe }, { 0xfe, 0x00, 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0 }, { 0xfe, 0x00, 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e }, { 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00 }, /*sw*/ { 0xfe, 0x1e, 0x00, 0xe0, 0xfe, 0x0e, 0x00, 0xf0 }, { 0xfe, 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e, 0xfe }, { 0xfe, 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0, 0x00 }, { 0xfe, 0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e }, /*sw*/ { 0xfe, 0xe0, 0x00, 0x1e, 0xfe, 0xf0, 0x00, 0x0e }, { 0xfe, 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e, 0x00 }, { 0xfe, 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0, 0xfe }, { 0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0 }, /*sw*/ { 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00 }, { 0xfe, 0xfe, 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e }, { 0xfe, 0xfe, 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0 }, { 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe } /*w*/ }; bool SymmetricKey::isWeakDESKey() { if(size() != 8) return false; // dubious SecureArray workingCopy(8); // clear parity bits for(uint i = 0; i < 8; i++) workingCopy[i] = (data()[i]) & 0xfe; for(int n = 0; n < 64; n++) { if(memcmp(workingCopy.data(), desWeakKeyTable[n], 8) == 0) return true; } return false; } //---------------------------------------------------------------------------- // InitializationVector //---------------------------------------------------------------------------- InitializationVector::InitializationVector() { } InitializationVector::InitializationVector(int size) { set(Random::randomArray(size)); } InitializationVector::InitializationVector(const SecureArray &a) { set(a); } InitializationVector::InitializationVector(const QByteArray &a) { set(SecureArray(a)); } //---------------------------------------------------------------------------- // AuthTag //---------------------------------------------------------------------------- AuthTag::AuthTag() { } AuthTag::AuthTag(int size) { resize(size); } AuthTag::AuthTag(const SecureArray &a) { set(a); } AuthTag::AuthTag(const QByteArray &a) { set(SecureArray(a)); } //---------------------------------------------------------------------------- // Event //---------------------------------------------------------------------------- class Event::Private : public QSharedData { public: Type type; Source source; PasswordStyle style; KeyStoreInfo ksi; KeyStoreEntry kse; QString fname; void *ptr; }; Event::Event() { } Event::Event(const Event &from) :d(from.d) { } Event::~Event() { } Event & Event::operator=(const Event &from) { d = from.d; return *this; } bool Event::isNull() const { return (d ? false : true); } Event::Type Event::type() const { return d->type; } Event::Source Event::source() const { return d->source; } Event::PasswordStyle Event::passwordStyle() const { return d->style; } KeyStoreInfo Event::keyStoreInfo() const { return d->ksi; } KeyStoreEntry Event::keyStoreEntry() const { return d->kse; } QString Event::fileName() const { return d->fname; } void *Event::ptr() const { return d->ptr; } void Event::setPasswordKeyStore(PasswordStyle pstyle, const KeyStoreInfo &keyStoreInfo, const KeyStoreEntry &keyStoreEntry, void *ptr) { if(!d) d = new Private; d->type = Password; d->source = KeyStore; d->style = pstyle; d->ksi = keyStoreInfo; d->kse = keyStoreEntry; d->fname = QString(); d->ptr = ptr; } void Event::setPasswordData(PasswordStyle pstyle, const QString &fileName, void *ptr) { if(!d) d = new Private; d->type = Password; d->source = Data; d->style = pstyle; d->ksi = KeyStoreInfo(); d->kse = KeyStoreEntry(); d->fname = fileName; d->ptr = ptr; } void Event::setToken(const KeyStoreInfo &keyStoreInfo, const KeyStoreEntry &keyStoreEntry, void *ptr) { if(!d) d = new Private; d->type = Token; d->source = KeyStore; d->style = StylePassword; d->ksi = keyStoreInfo; d->kse = keyStoreEntry; d->fname = QString(); d->ptr = ptr; } //---------------------------------------------------------------------------- // EventGlobal //---------------------------------------------------------------------------- class HandlerBase : public QObject { Q_OBJECT public: HandlerBase(QObject *parent = 0) : QObject(parent) { } protected slots: virtual void ask(int id, const QCA::Event &e) = 0; }; class AskerBase : public QObject { Q_OBJECT public: AskerBase(QObject *parent = 0) : QObject(parent) { } virtual void set_accepted(const SecureArray &password) = 0; virtual void set_rejected() = 0; }; static void handler_add(HandlerBase *h, int pos = -1); static void handler_remove(HandlerBase *h); static void handler_accept(HandlerBase *h, int id, const SecureArray &password); static void handler_reject(HandlerBase *h, int id); static bool asker_ask(AskerBase *a, const Event &e); static void asker_cancel(AskerBase *a); Q_GLOBAL_STATIC(QMutex, g_event_mutex) class EventGlobal; static EventGlobal *g_event = 0; class EventGlobal { public: class HandlerItem { public: HandlerBase *h; QList ids; }; class AskerItem { public: AskerBase *a; int id; Event event; int handler_pos; }; QList handlers; QList askers; int next_id; EventGlobal() { qRegisterMetaType("QCA::Event"); qRegisterMetaType("QCA::SecureArray"); next_id = 0; } int findHandlerItem(HandlerBase *h) { for(int n = 0; n < handlers.count(); ++n) { if(handlers[n].h == h) return n; } return -1; } int findAskerItem(AskerBase *a) { for(int n = 0; n < askers.count(); ++n) { if(askers[n].a == a) return n; } return -1; } int findAskerItemById(int id) { for(int n = 0; n < askers.count(); ++n) { if(askers[n].id == id) return n; } return -1; } void ask(int asker_at) { AskerItem &i = askers[asker_at]; g_event->handlers[i.handler_pos].ids += i.id; QMetaObject::invokeMethod(handlers[i.handler_pos].h, "ask", Qt::QueuedConnection, Q_ARG(int, i.id), Q_ARG(QCA::Event, i.event)); } void reject(int asker_at) { AskerItem &i = askers[asker_at]; // look for the next usable handler int pos = -1; for(int n = i.handler_pos + 1; n < g_event->handlers.count(); ++n) { // handler and asker can't be in the same thread //Q_ASSERT(g_event->handlers[n].h->thread() != i.a->thread()); //if(g_event->handlers[n].h->thread() != i.a->thread()) //{ pos = n; break; //} } // if there is one, try it if(pos != -1) { i.handler_pos = pos; ask(asker_at); } // if not, send official reject else { AskerBase *asker = i.a; askers.removeAt(asker_at); asker->set_rejected(); } } }; void handler_add(HandlerBase *h, int pos) { QMutexLocker locker(g_event_mutex()); if(!g_event) g_event = new EventGlobal; EventGlobal::HandlerItem i; i.h = h; if(pos != -1) { g_event->handlers.insert(pos, i); // adjust handler positions for(int n = 0; n < g_event->askers.count(); ++n) { if(g_event->askers[n].handler_pos >= pos) g_event->askers[n].handler_pos++; } } else g_event->handlers += i; } void handler_remove(HandlerBase *h) { QMutexLocker locker(g_event_mutex()); Q_ASSERT(g_event); if(!g_event) return; int at = g_event->findHandlerItem(h); Q_ASSERT(at != -1); if(at == -1) return; QList ids = g_event->handlers[at].ids; g_event->handlers.removeAt(at); // adjust handler positions within askers for(int n = 0; n < g_event->askers.count(); ++n) { if(g_event->askers[n].handler_pos >= at) g_event->askers[n].handler_pos--; } // reject all askers foreach(int id, ids) { int asker_at = g_event->findAskerItemById(id); Q_ASSERT(asker_at != -1); g_event->reject(asker_at); } if(g_event->handlers.isEmpty()) { delete g_event; g_event = 0; } } void handler_accept(HandlerBase *h, int id, const SecureArray &password) { QMutexLocker locker(g_event_mutex()); Q_ASSERT(g_event); if(!g_event) return; int at = g_event->findHandlerItem(h); Q_ASSERT(at != -1); if(at == -1) return; int asker_at = g_event->findAskerItemById(id); Q_ASSERT(asker_at != -1); if(asker_at == -1) return; g_event->handlers[at].ids.removeAll(g_event->askers[asker_at].id); AskerBase *asker = g_event->askers[asker_at].a; asker->set_accepted(password); } void handler_reject(HandlerBase *h, int id) { QMutexLocker locker(g_event_mutex()); Q_ASSERT(g_event); if(!g_event) return; int at = g_event->findHandlerItem(h); Q_ASSERT(at != -1); if(at == -1) return; int asker_at = g_event->findAskerItemById(id); Q_ASSERT(asker_at != -1); if(asker_at == -1) return; g_event->handlers[at].ids.removeAll(g_event->askers[asker_at].id); g_event->reject(asker_at); } bool asker_ask(AskerBase *a, const Event &e) { QMutexLocker locker(g_event_mutex()); if(!g_event) return false; int pos = -1; for(int n = 0; n < g_event->handlers.count(); ++n) { // handler and asker can't be in the same thread //Q_ASSERT(g_event->handlers[n].h->thread() != a->thread()); //if(g_event->handlers[n].h->thread() != a->thread()) //{ pos = n; break; //} } if(pos == -1) return false; EventGlobal::AskerItem i; i.a = a; i.id = g_event->next_id++; i.event = e; i.handler_pos = pos; g_event->askers += i; int asker_at = g_event->askers.count() - 1; g_event->ask(asker_at); return true; } void asker_cancel(AskerBase *a) { QMutexLocker locker(g_event_mutex()); if(!g_event) return; int at = g_event->findAskerItem(a); if(at == -1) return; for(int n = 0; n < g_event->handlers.count(); ++n) g_event->handlers[n].ids.removeAll(g_event->askers[at].id); g_event->askers.removeAt(at); } //---------------------------------------------------------------------------- // EventHandler //---------------------------------------------------------------------------- class EventHandler::Private : public HandlerBase { Q_OBJECT public: EventHandler *q; bool started; QList activeIds; Private(EventHandler *_q) : HandlerBase(_q), q(_q) { started = false; } public slots: virtual void ask(int id, const QCA::Event &e) { activeIds += id; emit q->eventReady(id, e); } }; EventHandler::EventHandler(QObject *parent) :QObject(parent) { d = new Private(this); } EventHandler::~EventHandler() { if(d->started) { foreach(int id, d->activeIds) handler_reject(d, id); handler_remove(d); } delete d; } void EventHandler::start() { d->started = true; handler_add(d); } void EventHandler::submitPassword(int id, const SecureArray &password) { if(!d->activeIds.contains(id)) return; d->activeIds.removeAll(id); handler_accept(d, id, password); } void EventHandler::tokenOkay(int id) { if(!d->activeIds.contains(id)) return; d->activeIds.removeAll(id); handler_accept(d, id, SecureArray()); } void EventHandler::reject(int id) { if(!d->activeIds.contains(id)) return; d->activeIds.removeAll(id); handler_reject(d, id); } //---------------------------------------------------------------------------- // PasswordAsker //---------------------------------------------------------------------------- class AskerPrivate : public AskerBase { Q_OBJECT public: enum Type { Password, Token }; Type type; PasswordAsker *passwordAsker; TokenAsker *tokenAsker; QMutex m; QWaitCondition w; bool accepted; SecureArray password; bool waiting; bool done; AskerPrivate(PasswordAsker *parent) : AskerBase(parent) { passwordAsker = parent; tokenAsker = 0; type = Password; accepted = false; waiting = false; done = true; } AskerPrivate(TokenAsker *parent) : AskerBase(parent) { passwordAsker = 0; tokenAsker = parent; type = Token; accepted = false; waiting = false; done = true; } void ask(const Event &e) { accepted = false; waiting = false; done = false; password.clear(); if(!asker_ask(this, e)) { done = true; QMetaObject::invokeMethod(this, "emitResponseReady", Qt::QueuedConnection); } } void cancel() { if(!done) asker_cancel(this); } virtual void set_accepted(const SecureArray &_password) { QMutexLocker locker(&m); accepted = true; password = _password; done = true; if(waiting) w.wakeOne(); else QMetaObject::invokeMethod(this, "emitResponseReady", Qt::QueuedConnection); } virtual void set_rejected() { QMutexLocker locker(&m); done = true; if(waiting) w.wakeOne(); else QMetaObject::invokeMethod(this, "emitResponseReady", Qt::QueuedConnection); } void waitForResponse() { QMutexLocker locker(&m); if(done) return; waiting = true; w.wait(&m); waiting = false; } public slots: virtual void emitResponseReady() = 0; }; class PasswordAsker::Private : public AskerPrivate { public: Private(PasswordAsker *_q) : AskerPrivate(_q) { } virtual void emitResponseReady() { emit passwordAsker->responseReady(); } }; PasswordAsker::PasswordAsker(QObject *parent) :QObject(parent) { d = new Private(this); } PasswordAsker::~PasswordAsker() { delete d; } void PasswordAsker::ask(Event::PasswordStyle pstyle, const KeyStoreInfo &keyStoreInfo, const KeyStoreEntry &keyStoreEntry, void *ptr) { Event e; e.setPasswordKeyStore(pstyle, keyStoreInfo, keyStoreEntry, ptr); d->ask(e); } void PasswordAsker::ask(Event::PasswordStyle pstyle, const QString &fileName, void *ptr) { Event e; e.setPasswordData(pstyle, fileName, ptr); d->ask(e); } void PasswordAsker::cancel() { d->cancel(); } void PasswordAsker::waitForResponse() { d->waitForResponse(); } bool PasswordAsker::accepted() const { return d->accepted; } SecureArray PasswordAsker::password() const { return d->password; } //---------------------------------------------------------------------------- // TokenAsker //---------------------------------------------------------------------------- class TokenAsker::Private : public AskerPrivate { public: Private(TokenAsker *_q) : AskerPrivate(_q) { } virtual void emitResponseReady() { emit tokenAsker->responseReady(); } }; TokenAsker::TokenAsker(QObject *parent) :QObject(parent) { d = new Private(this); } TokenAsker::~TokenAsker() { delete d; } void TokenAsker::ask(const KeyStoreInfo &keyStoreInfo, const KeyStoreEntry &keyStoreEntry, void *ptr) { Event e; e.setToken(keyStoreInfo, keyStoreEntry, ptr); d->ask(e); } void TokenAsker::cancel() { d->cancel(); } void TokenAsker::waitForResponse() { d->waitForResponse(); } bool TokenAsker::accepted() const { return d->accepted; } } #include "qca_core.moc"