diff --git a/examples/saslclient/saslclient.cpp b/examples/saslclient/saslclient.cpp index 0d3ef7d2..1353c225 100644 --- a/examples/saslclient/saslclient.cpp +++ b/examples/saslclient/saslclient.cpp @@ -1,569 +1,569 @@ /* Copyright (C) 2003-2008 Justin Karneges Copyright (C) 2006 Michail Pishchagin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include // QtCrypto has the declarations for all of QCA #include #ifdef QT_STATICPLUGIN #include "import_plugins.h" #endif static QString prompt(const QString &s) { printf("* %s ", qPrintable(s)); fflush(stdout); char line[256]; fgets(line, 255, stdin); QString result = line; if(result[result.length()-1] == '\n') result.truncate(result.length()-1); return result; } static QString socketErrorToString(QAbstractSocket::SocketError x) { QString s; switch(x) { case QAbstractSocket::ConnectionRefusedError: s = "connection refused or timed out"; break; case QAbstractSocket::RemoteHostClosedError: s = "remote host closed the connection"; break; case QAbstractSocket::HostNotFoundError: s = "host not found"; break; case QAbstractSocket::SocketAccessError: s = "access error"; break; case QAbstractSocket::SocketResourceError: s = "too many sockets"; break; case QAbstractSocket::SocketTimeoutError: s = "operation timed out"; break; case QAbstractSocket::DatagramTooLargeError: s = "datagram was larger than system limit"; break; case QAbstractSocket::NetworkError: s = "network error"; break; case QAbstractSocket::AddressInUseError: s = "address is already in use"; break; case QAbstractSocket::SocketAddressNotAvailableError: s = "address does not belong to the host"; break; case QAbstractSocket::UnsupportedSocketOperationError: s = "operation is not supported by the local operating system"; break; default: s = "unknown socket error"; break; } return s; } static QString saslAuthConditionToString(QCA::SASL::AuthCondition x) { QString s; switch(x) { case QCA::SASL::NoMechanism: s = "no appropriate mechanism could be negotiated"; break; case QCA::SASL::BadProtocol: s = "bad SASL protocol"; break; case QCA::SASL::BadServer: s = "server failed mutual authentication"; break; // AuthFail or unknown (including those defined for server only) default: s = "generic authentication failure"; break; }; return s; } class ClientTest : public QObject { Q_OBJECT private: QString host, proto, authzid, realm, user, pass; int port; bool no_authzid, no_realm; int mode; // 0 = receive mechanism list, 1 = sasl negotiation, 2 = app QTcpSocket *sock; QCA::SASL *sasl; QByteArray inbuf; bool sock_done; int waitCycles; public: ClientTest(const QString &_host, int _port, const QString &_proto, const QString &_authzid, const QString &_realm, const QString &_user, const QString &_pass, bool _no_authzid, bool _no_realm) : host(_host), proto(_proto), authzid(_authzid), realm(_realm), user(_user), pass(_pass), port(_port), no_authzid(_no_authzid), no_realm(_no_realm), sock_done(false), waitCycles(0) { sock = new QTcpSocket(this); connect(sock, SIGNAL(connected()), SLOT(sock_connected())); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError))); sasl = new QCA::SASL(this); connect(sasl, SIGNAL(clientStarted(bool, const QByteArray &)), SLOT(sasl_clientFirstStep(bool, const QByteArray &))); connect(sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &))); connect(sasl, SIGNAL(needParams(const QCA::SASL::Params &)), SLOT(sasl_needParams(const QCA::SASL::Params &))); connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); connect(sasl, SIGNAL(readyReadOutgoing()), SLOT(sasl_readyReadOutgoing())); connect(sasl, SIGNAL(error()), SLOT(sasl_error())); } public Q_SLOTS: void start() { mode = 0; // mech list mode int flags = 0; flags |= QCA::SASL::AllowPlain; flags |= QCA::SASL::AllowAnonymous; sasl->setConstraints((QCA::SASL::AuthFlags)flags, 0, 256); if(!user.isEmpty()) sasl->setUsername(user); if(!authzid.isEmpty()) sasl->setAuthzid(authzid); if(!pass.isEmpty()) sasl->setPassword(pass.toUtf8()); if(!realm.isEmpty()) sasl->setRealm(realm); printf("Connecting to %s:%d, for protocol %s\n", qPrintable(host), port, qPrintable(proto)); sock->connectToHost(host, port); } Q_SIGNALS: void quit(); private Q_SLOTS: void sock_connected() { printf("Connected to server. Awaiting mechanism list...\n"); } void sock_error(QAbstractSocket::SocketError x) { if(x == QAbstractSocket::RemoteHostClosedError) { if(mode == 2) // app mode, where disconnect means completion { sock_done = true; tryFinished(); return; } else // any other mode, where disconnect is an error { printf("Error: server closed connection unexpectedly.\n"); emit quit(); return; } } printf("Error: socket: %s\n", qPrintable(socketErrorToString(x))); emit quit(); } void sock_readyRead() { if(mode == 2) // app mode { QByteArray a = sock->readAll(); printf("Read %d bytes\n", a.size()); // there is a possible flaw in the qca 2.0 api, in // that if sasl data is received from the peer // followed by a disconnect from the peer, there is // no clear approach to salvaging the bytes. tls is // not affected because tls has the concept of // closing a session. with sasl, there is no // closing, and since the qca api is asynchronous, // we could potentially wait forever for decoded // data, if the last write was a partial packet. // // for now, we can perform a simple workaround of // waiting at least three event loop cycles for // decoded data before giving up and assuming the // last write was partial. the fact is, all current // qca sasl providers respond within this time // frame, so this fix should work fine for now. in // qca 2.1, we should revise the api to handle this // situation better. // // further note: i guess this only affects application // protocols that have no close message of their // own, and rely on the tcp-level close. examples // are http, and of course this qcatest protocol. if(waitCycles == 0) { waitCycles = 3; QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection); } sasl->writeIncoming(a); } else // mech list or sasl negotiation mode { if(sock->canReadLine()) { QString line = sock->readLine(); line.truncate(line.length() - 1); // chop the newline handleLine(line); } } } void sasl_clientFirstStep(bool clientInit, const QByteArray &clientInitData) { printf("Choosing mech: %s\n", qPrintable(sasl->mechanism())); QString line = sasl->mechanism(); if(clientInit) { line += ' '; line += arrayToString(clientInitData); } sendLine(line); } void sasl_nextStep(const QByteArray &stepData) { QString line = "C"; if(!stepData.isEmpty()) { line += ','; line += arrayToString(stepData); } sendLine(line); } void sasl_needParams(const QCA::SASL::Params ¶ms) { if(params.needUsername()) { user = prompt("Username:"); sasl->setUsername(user); } if(params.canSendAuthzid() && !no_authzid) { authzid = prompt("Authorize As (enter to skip):"); if(!authzid.isEmpty()) sasl->setAuthzid(authzid); } if(params.needPassword()) { QCA::ConsolePrompt prompt; prompt.getHidden("* Password"); prompt.waitForFinished(); QCA::SecureArray pass = prompt.result(); sasl->setPassword(pass); } if(params.canSendRealm() && !no_realm) { QStringList realms = sasl->realmList(); printf("Available realms:\n"); if(realms.isEmpty()) printf(" (none specified)\n"); foreach(const QString &s, realms) printf(" %s\n", qPrintable(s)); realm = prompt("Realm (enter to skip):"); if(!realm.isEmpty()) sasl->setRealm(realm); } sasl->continueAfterParams(); } void sasl_authenticated() { printf("SASL success!\n"); printf("SSF: %d\n", sasl->ssf()); } void sasl_readyRead() { QByteArray a = sasl->read(); inbuf += a; processInbuf(); } void sasl_readyReadOutgoing() { QByteArray a = sasl->readOutgoing(); sock->write(a); } void sasl_error() { int e = sasl->errorCode(); if(e == QCA::SASL::ErrorInit) printf("Error: sasl: initialization failed.\n"); else if(e == QCA::SASL::ErrorHandshake) printf("Error: sasl: %s.\n", qPrintable(saslAuthConditionToString(sasl->authCondition()))); else if(e == QCA::SASL::ErrorCrypt) printf("Error: sasl: broken security layer.\n"); else printf("Error: sasl: unknown error.\n"); emit quit(); } void waitWriteIncoming() { --waitCycles; if(waitCycles > 0) { QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection); return; } tryFinished(); } private: void tryFinished() { if(sock_done && waitCycles == 0) { printf("Finished, server closed connection.\n"); // if we give up on waiting for a response to // writeIncoming, then it might come late. in // theory this shouldn't happen if we wait enough // cycles, but if one were to arrive then it could // occur between the request to quit the app and // the actual quit of the app. to assist with // debugging, then, we'll explicitly stop listening // for signals here. otherwise the response may // still be received and displayed, giving a false // sense of correctness. sasl->disconnect(this); emit quit(); } } QString arrayToString(const QByteArray &ba) { return QCA::Base64().arrayToString(ba); } QByteArray stringToArray(const QString &s) { return QCA::Base64().stringToArray(s).toByteArray(); } void sendLine(const QString &line) { printf("Writing: {%s}\n", qPrintable(line)); QString s = line + '\n'; QByteArray a = s.toUtf8(); if(mode == 2) // app mode sasl->write(a); // write to sasl else // mech list or sasl negotiation sock->write(a); // write to socket } void processInbuf() { // collect completed lines from inbuf QStringList list; int at; while((at = inbuf.indexOf('\n')) != -1) { list += QString::fromUtf8(inbuf.mid(0, at)); inbuf = inbuf.mid(at + 1); } // process the lines foreach(const QString &line, list) handleLine(line); } void handleLine(const QString &line) { printf("Reading: [%s]\n", qPrintable(line)); if(mode == 0) { // first line is the method list QStringList mechlist = line.split(' '); mode = 1; // switch to sasl negotiation mode sasl->startClient(proto, host, mechlist); } else if(mode == 1) { QString type, rest; int n = line.indexOf(','); if(n != -1) { type = line.mid(0, n); rest = line.mid(n + 1); } else type = line; if(type == "C") { sasl->putStep(stringToArray(rest)); } else if(type == "E") { if(!rest.isEmpty()) printf("Error: server says: %s.\n", qPrintable(rest)); else printf("Error: server error, unspecified.\n"); emit quit(); return; } else if(type == "A") { printf("Authentication success.\n"); mode = 2; // switch to app mode // at this point, the server may send us text // lines for us to display and then close. sock_readyRead(); // any extra data? return; } else { printf("Error: Bad format from peer, closing.\n"); emit quit(); return; } } } }; void usage() { printf("usage: saslclient (options) host(:port) (user) (pass)\n"); printf("options: --proto=x, --authzid=x, --realm=x\n"); } int main(int argc, char **argv) { QCA::Initializer init; QCoreApplication qapp(argc, argv); QStringList args = qapp.arguments(); args.removeFirst(); // options QString proto = "qcatest"; // default protocol QString authzid, realm; bool no_authzid = false; bool no_realm = false; for(int n = 0; n < args.count(); ++n) { if(!args[n].startsWith("--")) continue; QString opt = args[n].mid(2); QString var, val; int at = opt.indexOf('='); if(at != -1) { var = opt.mid(0, at); val = opt.mid(at + 1); } else var = opt; if(var == "proto") { proto = val; } else if(var == "authzid") { // specifying empty authzid means force unspecified if(val.isEmpty()) no_authzid = true; else authzid = val; } else if(var == "realm") { // specifying empty realm means force unspecified if(val.isEmpty()) no_realm = true; else realm = val; } args.removeAt(n); --n; // adjust position } if(args.count() < 1) { usage(); return 0; } QString host, user, pass; int port = 8001; // default port QString hostinput = args[0]; if(args.count() >= 2) user = args[1]; if(args.count() >= 3) pass = args[2]; int at = hostinput.indexOf(':'); if(at != -1) { host = hostinput.mid(0, at); - port = hostinput.mid(at + 1).toInt(); + port = hostinput.midRef(at + 1).toInt(); } else host = hostinput; if(!QCA::isSupported("sasl")) { printf("Error: SASL support not found.\n"); return 1; } ClientTest client(host, port, proto, authzid, realm, user, pass, no_authzid, no_realm); QObject::connect(&client, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &client, SLOT(start())); qapp.exec(); return 0; } #include "saslclient.moc" diff --git a/examples/saslserver/saslserver.cpp b/examples/saslserver/saslserver.cpp index e0514a23..ad2c6f97 100644 --- a/examples/saslserver/saslserver.cpp +++ b/examples/saslserver/saslserver.cpp @@ -1,514 +1,514 @@ /* Copyright (C) 2003-2008 Justin Karneges Copyright (C) 2006 Michail Pishchagin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include // QtCrypto has the declarations for all of QCA #include #ifdef QT_STATICPLUGIN #include "import_plugins.h" #endif static QString socketErrorToString(QAbstractSocket::SocketError x) { QString s; switch(x) { case QAbstractSocket::ConnectionRefusedError: s = "connection refused or timed out"; break; case QAbstractSocket::RemoteHostClosedError: s = "remote host closed the connection"; break; case QAbstractSocket::HostNotFoundError: s = "host not found"; break; case QAbstractSocket::SocketAccessError: s = "access error"; break; case QAbstractSocket::SocketResourceError: s = "too many sockets"; break; case QAbstractSocket::SocketTimeoutError: s = "operation timed out"; break; case QAbstractSocket::DatagramTooLargeError: s = "datagram was larger than system limit"; break; case QAbstractSocket::NetworkError: s = "network error"; break; case QAbstractSocket::AddressInUseError: s = "address is already in use"; break; case QAbstractSocket::SocketAddressNotAvailableError: s = "address does not belong to the host"; break; case QAbstractSocket::UnsupportedSocketOperationError: s = "operation is not supported by the local operating system"; break; default: s = "unknown socket error"; break; } return s; } static QString saslAuthConditionToString(QCA::SASL::AuthCondition x) { QString s; switch(x) { case QCA::SASL::NoMechanism: s = "no appropriate mechanism could be negotiated"; break; case QCA::SASL::BadProtocol: s = "bad SASL protocol"; break; case QCA::SASL::BadAuth: s = "authentication failed"; break; case QCA::SASL::NoAuthzid: s = "authorization failed"; break; case QCA::SASL::TooWeak: s = "mechanism too weak for this user"; break; case QCA::SASL::NeedEncrypt: s = "encryption is needed to use this mechanism"; break; case QCA::SASL::Expired: s = "passphrase expired"; break; case QCA::SASL::Disabled: s = "account is disabled"; break; case QCA::SASL::NoUser: s = "user not found"; break; case QCA::SASL::RemoteUnavailable: s = "needed remote service is unavailable"; break; // AuthFail or unknown (including those defined for client only) default: s = "generic authentication failure"; break; }; return s; } // --- ServerTest declaration class ServerTest : public QObject { Q_OBJECT private: QString host, proto, realm, str; int port; QTcpServer *tcpServer; QList ids; public: ServerTest(const QString &_host, int _port, const QString &_proto, const QString &_realm, const QString &_str); int reserveId(); void releaseId(int id); public Q_SLOTS: void start(); Q_SIGNALS: void quit(); private Q_SLOTS: void server_newConnection(); }; // --- ServerTestHandler class ServerTestHandler : public QObject { Q_OBJECT private: ServerTest *serverTest; QTcpSocket *sock; QCA::SASL *sasl; int id; QString host, proto, realm, str; int mode; // 0 = receive mechanism list, 1 = sasl negotiation, 2 = app int toWrite; public: ServerTestHandler(ServerTest *_serverTest, QTcpSocket *_sock, const QString &_host, const QString &_proto, const QString &_realm, const QString &_str) : serverTest(_serverTest), sock(_sock), host(_host), proto(_proto), realm(_realm), str(_str) { id = serverTest->reserveId(); sock->setParent(this); connect(sock, SIGNAL(disconnected()), SLOT(sock_disconnected())); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError))); connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(sock_bytesWritten(qint64))); sasl = new QCA::SASL(this); connect(sasl, SIGNAL(authCheck(const QString &, const QString &)), SLOT(sasl_authCheck(const QString &, const QString &))); connect(sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &))); connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); connect(sasl, SIGNAL(readyReadOutgoing()), SLOT(sasl_readyReadOutgoing())); connect(sasl, SIGNAL(error()), SLOT(sasl_error())); connect(sasl, SIGNAL(serverStarted()), SLOT(sasl_serverStarted())); mode = 0; // mech list mode toWrite = 0; int flags = 0; flags |= QCA::SASL::AllowPlain; flags |= QCA::SASL::AllowAnonymous; sasl->setConstraints((QCA::SASL::AuthFlags)flags, 0, 256); printf("%d: Connection received! Starting SASL handshake...\n", id); sasl->startServer(proto, host, realm); } ~ServerTestHandler() { serverTest->releaseId(id); } private Q_SLOTS: void sasl_serverStarted() { sendLine(sasl->mechanismList().join(" ")); } void sock_disconnected() { printf("%d: Connection closed.\n", id); discard(); } void sock_error(QAbstractSocket::SocketError x) { if(x == QAbstractSocket::RemoteHostClosedError) { printf("%d: Error: client closed connection unexpectedly.\n", id); discard(); return; } printf("%d: Error: socket: %s\n", id, qPrintable(socketErrorToString(x))); discard(); } void sock_readyRead() { if(sock->canReadLine()) { QString line = sock->readLine(); line.truncate(line.length() - 1); // chop the newline handleLine(line); } } void sock_bytesWritten(qint64 x) { if(mode == 2) // app mode { toWrite -= sasl->convertBytesWritten(x); if(toWrite == 0) { printf("%d: Sent, closing.\n", id); sock->close(); } } } void sasl_nextStep(const QByteArray &stepData) { QString line = "C"; if(!stepData.isEmpty()) { line += ','; line += arrayToString(stepData); } sendLine(line); } void sasl_authCheck(const QString &user, const QString &authzid) { printf("%d: AuthCheck: User: [%s], Authzid: [%s]\n", id, qPrintable(user), qPrintable(authzid)); // user - who has logged in, confirmed by sasl // authzid - the identity the user wishes to act as, which // could be another user or just any arbitrary string (in // XMPP, this field holds a Jabber ID, for example). this // field is not necessarily confirmed by sasl, and the // decision about whether the user can act as the authzid // must be made by the app. // for this simple example program, we allow anyone to use // the service, and simply continue onward with the // negotiation. sasl->continueAfterAuthCheck(); } void sasl_authenticated() { sendLine("A"); printf("%d: Authentication success.\n", id); mode = 2; // switch to app mode printf("%d: SSF: %d\n", id, sasl->ssf()); sendLine(str); } void sasl_readyRead() { QByteArray a = sasl->read(); printf("%d: Warning, client sent %d bytes unexpectedly.\n", id, a.size()); } void sasl_readyReadOutgoing() { sock->write(sasl->readOutgoing()); } void sasl_error() { int e = sasl->errorCode(); if(e == QCA::SASL::ErrorInit) { printf("%d: Error: sasl: initialization failed.\n", id); } else if(e == QCA::SASL::ErrorHandshake) { QString errstr = saslAuthConditionToString(sasl->authCondition()); sendLine(QString("E,") + errstr); printf("%d: Error: sasl: %s.\n", id, qPrintable(errstr)); } else if(e == QCA::SASL::ErrorCrypt) { printf("%d: Error: sasl: broken security layer.\n", id); } else { printf("%d: Error: sasl: unknown error.\n", id); } sock->close(); } private: void discard() { deleteLater(); } void handleLine(const QString &line) { printf("%d: Reading: [%s]\n", id, qPrintable(line)); if(mode == 0) { int n = line.indexOf(' '); if(n != -1) { QString mech = line.mid(0, n); QString rest = line.mid(n + 1).toUtf8(); sasl->putServerFirstStep(mech, stringToArray(rest)); } else sasl->putServerFirstStep(line); ++mode; } else if(mode == 1) { QString type, rest; int n = line.indexOf(','); if(n != -1) { type = line.mid(0, n); rest = line.mid(n + 1); } else { type = line; rest = ""; } if(type == "C") { sasl->putStep(stringToArray(rest)); } else { printf("%d: Bad format from peer, closing.\n", id); sock->close(); return; } } } QString arrayToString(const QByteArray &ba) { QCA::Base64 encoder; return encoder.arrayToString(ba); } QByteArray stringToArray(const QString &s) { QCA::Base64 decoder(QCA::Decode); return decoder.stringToArray(s).toByteArray(); } void sendLine(const QString &line) { printf("%d: Writing: {%s}\n", id, qPrintable(line)); QString s = line + '\n'; QByteArray a = s.toUtf8(); if(mode == 2) // app mode { toWrite += a.size(); sasl->write(a); // write to sasl } else // mech list or sasl negotiation sock->write(a); // write to socket } }; // --- ServerTest implementation ServerTest::ServerTest(const QString &_host, int _port, const QString &_proto, const QString &_realm, const QString &_str) : host(_host), proto(_proto), realm(_realm), str(_str), port(_port) { tcpServer = new QTcpServer(this); connect(tcpServer, SIGNAL(newConnection()), SLOT(server_newConnection())); } int ServerTest::reserveId() { int n = 0; while(ids.contains(n)) ++n; ids += n; return n; } void ServerTest::releaseId(int id) { ids.removeAll(id); } void ServerTest::start() { if(!tcpServer->listen(QHostAddress::Any, port)) { printf("Error: unable to bind to port %d.\n", port); emit quit(); return; } printf("Serving on %s:%d, for protocol %s ...\n", qPrintable(host), port, qPrintable(proto)); } void ServerTest::server_newConnection() { QTcpSocket *sock = tcpServer->nextPendingConnection(); new ServerTestHandler(this, sock, host, proto, realm, str); } // --- void usage() { printf("usage: saslserver host (message)\n"); printf("options: --proto=x, --realm=x\n"); } int main(int argc, char **argv) { QCA::Initializer init; QCoreApplication qapp(argc, argv); QCA::setAppName("saslserver"); QStringList args = qapp.arguments(); args.removeFirst(); // options QString proto = "qcatest"; // default protocol QString realm; for(int n = 0; n < args.count(); ++n) { if(!args[n].startsWith("--")) continue; QString opt = args[n].mid(2); QString var, val; int at = opt.indexOf('='); if(at != -1) { var = opt.mid(0, at); val = opt.mid(at + 1); } else var = opt; if(var == "proto") proto = val; else if(var == "realm") realm = val; args.removeAt(n); --n; // adjust position } if(args.count() < 1) { usage(); return 0; } QString host; int port = 8001; // default port QString hostinput = args[0]; QString str = "Hello, World"; if(args.count() >= 2) str = args[1]; int at = hostinput.indexOf(':'); if(at != -1) { host = hostinput.mid(0, at); - port = hostinput.mid(at + 1).toInt(); + port = hostinput.midRef(at + 1).toInt(); } else host = hostinput; if(!QCA::isSupported("sasl")) { printf("Error: SASL support not found.\n"); return 1; } ServerTest server(host, port, proto, realm, str); QObject::connect(&server, SIGNAL(quit()), &qapp, SLOT(quit())); QTimer::singleShot(0, &server, SLOT(start())); qapp.exec(); return 0; } #include "saslserver.moc" diff --git a/examples/ssltest/ssltest.cpp b/examples/ssltest/ssltest.cpp index 2648c535..2b09431f 100644 --- a/examples/ssltest/ssltest.cpp +++ b/examples/ssltest/ssltest.cpp @@ -1,336 +1,336 @@ /* Copyright (C) 2003-2005 Justin Karneges Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #ifdef QT_STATICPLUGIN #include "import_plugins.h" #endif char exampleCA_cert[] = "-----BEGIN CERTIFICATE-----\n" "MIICSzCCAbSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA4MRMwEQYDVQQDEwpFeGFt\n" "cGxlIENBMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwHhcNMDYw\n" "MzE1MDY1ODMyWhcNMDYwNDE1MDY1ODMyWjA4MRMwEQYDVQQDEwpFeGFtcGxlIENB\n" "MQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwgZ8wDQYJKoZIhvcN\n" "AQEBBQADgY0AMIGJAoGBAL6ULdOxmpeZ+G/ypV12eNO4qnHSVIPTrYPkQuweXqPy\n" "atwGFheG+hLVsNIh9GGOS0tCe7a3hBBKN0BJg1ppfk2x39cDx7hefYqjBuZvp/0O\n" "8Ja3qlQiJLezITZKLxMBrsibcvcuH8zpfUdys2yaN+YGeqNfjQuoNN3Byl1TwuGJ\n" "AgMBAAGjZTBjMB0GA1UdDgQWBBSQKCUCLNM7uKrAt5o7qv/yQm6qEzASBgNVHRMB\n" "Af8ECDAGAQEBAgEIMB4GA1UdEQQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wDgYD\n" "VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4GBAAh+SIeT1Ao5qInw8oMSoTdO\n" "lQ6h67ec/Jk5KmK4OoskuimmHI0Sp0C5kOCLehXbsVWW8pXsNC2fv0d2HkdaSUcX\n" "hwLzqgyZXd4mupIYlaOTZhuHDwWPCAOZS4LVsi2tndTRHKCP12441JjNKhmZRhkR\n" "u5zzD60nWgM9dKTaxuZM\n" "-----END CERTIFICATE-----\n"; void showCertInfo(const QCA::Certificate &cert) { printf("-- Cert --\n"); printf(" CN: %s\n", qPrintable(cert.commonName())); printf(" Valid from: %s, until %s\n", qPrintable(cert.notValidBefore().toString()), qPrintable(cert.notValidAfter().toString())); printf(" PEM:\n%s\n", qPrintable(cert.toPEM())); } static QString validityToString(QCA::Validity v) { QString s; switch(v) { case QCA::ValidityGood: s = "Validated"; break; case QCA::ErrorRejected: s = "Root CA is marked to reject the specified purpose"; break; case QCA::ErrorUntrusted: s = "Certificate not trusted for the required purpose"; break; case QCA::ErrorSignatureFailed: s = "Invalid signature"; break; case QCA::ErrorInvalidCA: s = "Invalid CA certificate"; break; case QCA::ErrorInvalidPurpose: s = "Invalid certificate purpose"; break; case QCA::ErrorSelfSigned: s = "Certificate is self-signed"; break; case QCA::ErrorRevoked: s = "Certificate has been revoked"; break; case QCA::ErrorPathLengthExceeded: s = "Maximum certificate chain length exceeded"; break; case QCA::ErrorExpired: s = "Certificate has expired"; break; case QCA::ErrorExpiredCA: s = "CA has expired"; break; case QCA::ErrorValidityUnknown: default: s = "General certificate validation error"; break; } return s; } class SecureTest : public QObject { Q_OBJECT public: SecureTest() { sock_done = false; ssl_done = false; sock = new QTcpSocket; connect(sock, SIGNAL(connected()), SLOT(sock_connected())); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError))); ssl = new QCA::TLS; connect(ssl, SIGNAL(certificateRequested()), SLOT(ssl_certificateRequested())); connect(ssl, SIGNAL(handshaken()), SLOT(ssl_handshaken())); connect(ssl, SIGNAL(readyRead()), SLOT(ssl_readyRead())); connect(ssl, SIGNAL(readyReadOutgoing()), SLOT(ssl_readyReadOutgoing())); connect(ssl, SIGNAL(closed()), SLOT(ssl_closed())); connect(ssl, SIGNAL(error()), SLOT(ssl_error())); } ~SecureTest() { delete ssl; delete sock; } void start(const QString &_host) { int n = _host.indexOf(':'); int port; if(n != -1) { host = _host.mid(0, n); - port = _host.mid(n+1).toInt(); + port = _host.midRef(n+1).toInt(); } else { host = _host; port = 443; } printf("Trying %s:%d...\n", qPrintable(host), port); sock->connectToHost(host, port); } Q_SIGNALS: void quit(); private Q_SLOTS: void sock_connected() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; printf("Connected, starting TLS handshake...\n"); QCA::CertificateCollection rootCerts = QCA::systemStore(); // We add this one to show how, and to make it work with // the server example. rootCerts.addCertificate(QCA::Certificate::fromPEM(exampleCA_cert)); if(!QCA::haveSystemStore()) printf("Warning: no root certs\n"); else ssl->setTrustedCertificates(rootCerts); ssl->startClient(host); } void sock_readyRead() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; ssl->writeIncoming(sock->readAll()); } void sock_connectionClosed() { printf("\nConnection closed.\n"); sock_done = true; if(ssl_done && sock_done) emit quit(); } void sock_error(QAbstractSocket::SocketError x) { if(x == QAbstractSocket::RemoteHostClosedError) { sock_connectionClosed(); return; } printf("\nSocket error.\n"); emit quit(); } void ssl_handshaken() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; QCA::TLS::IdentityResult r = ssl->peerIdentityResult(); printf("Successful SSL handshake using %s (%i of %i bits)\n", qPrintable(ssl->cipherSuite()), ssl->cipherBits(), ssl->cipherMaxBits() ); if(r != QCA::TLS::NoCertificate) { cert = ssl->peerCertificateChain().primary(); if(!cert.isNull()) showCertInfo(cert); } QString str = "Peer Identity: "; if(r == QCA::TLS::Valid) str += "Valid"; else if(r == QCA::TLS::HostMismatch) str += "Error: Wrong certificate"; else if(r == QCA::TLS::InvalidCertificate) str += "Error: Invalid certificate.\n -> Reason: " + validityToString(ssl->peerCertificateValidity()); else str += "Error: No certificate"; printf("%s\n", qPrintable(str)); ssl->continueAfterStep(); printf("Let's try a GET request now.\n"); QString req = "GET / HTTP/1.0\nHost: " + host + "\n\n"; ssl->write(req.toLatin1()); } void ssl_certificateRequested() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; printf("Server requested client certificate.\n"); QList issuerList = ssl->issuerList(); if(!issuerList.isEmpty()) { printf("Allowed issuers:\n"); foreach(QCA::CertificateInfoOrdered i, issuerList) printf(" %s\n", qPrintable(i.toString())); } ssl->continueAfterStep(); } void ssl_readyRead() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; QByteArray a = ssl->read(); printf("%s", a.data()); } void ssl_readyReadOutgoing() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; sock->write(ssl->readOutgoing()); } void ssl_closed() { printf("SSL session closed.\n"); ssl_done = true; if(ssl_done && sock_done) emit quit(); } void ssl_error() { // We just do this to help doxygen... QCA::TLS *ssl = SecureTest::ssl; int x = ssl->errorCode(); if(x == QCA::TLS::ErrorHandshake) { printf("SSL Handshake Error!\n"); emit quit(); } else { printf("SSL Error!\n"); emit quit(); } } private: QString host; QTcpSocket *sock; QCA::TLS *ssl; QCA::Certificate cert; bool sock_done, ssl_done; }; #include "ssltest.moc" int main(int argc, char **argv) { QCA::Initializer init; QCoreApplication app(argc, argv); QString host = argc > 1 ? argv[1] : "andbit.net"; if(!QCA::isSupported("tls")) { printf("TLS not supported!\n"); return 1; } SecureTest *s = new SecureTest; QObject::connect(s, SIGNAL(quit()), &app, SLOT(quit())); s->start(host); app.exec(); delete s; return 0; } diff --git a/plugins/qca-pkcs11/qca-pkcs11.cpp b/plugins/qca-pkcs11/qca-pkcs11.cpp index 1917462f..7d41bfc9 100644 --- a/plugins/qca-pkcs11/qca-pkcs11.cpp +++ b/plugins/qca-pkcs11/qca-pkcs11.cpp @@ -1,3039 +1,3039 @@ /* * Copyright (C) 2004 Justin Karneges * Copyright (C) 2006-2007 Alon Bar-Lev * * 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 WITHANY 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 #include #include #include #include #include #include #include using namespace QCA; // qPrintable is ASCII only!!! #define myPrintable(s) (s).toUtf8 ().constData () static inline QString certificateHash ( const Certificate &cert ) { if (cert.isNull ()) { return QString(); } else { return Hash ("sha1").hashToString (cert.toDER ()); } } //---------------------------------------------------------------------------- // pkcs11Provider //---------------------------------------------------------------------------- class pkcs11Provider : public Provider { private: static const int _CONFIG_MAX_PROVIDERS; bool _lowLevelInitialized; bool _slotEventsActive; bool _slotEventsLowLevelActive; QStringList _providers; public: bool _allowLoadRootCA; public: pkcs11Provider (); ~pkcs11Provider (); public: int qcaVersion() const override; void init () override; void deinit () override; QString name () const override; QStringList features () const override; Context * createContext ( const QString &type ) override; void startSlotEvents (); void stopSlotEvents (); QVariantMap defaultConfig () const override; void configChanged (const QVariantMap &config) override; protected: static void __logHook ( void * const global_data, const unsigned flags, const char * const format, va_list args ); static void __slotEventHook ( void * const global_data ); static PKCS11H_BOOL __tokenPromptHook ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry ); static PKCS11H_BOOL __pinPromptHook ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry, char * const pin, const size_t pin_max ); void _logHook ( const unsigned flags, const char * const format, va_list args ); void _slotEventHook (); PKCS11H_BOOL _tokenPromptHook ( void * const user_data, const pkcs11h_token_id_t token ); PKCS11H_BOOL _pinPromptHook ( void * const user_data, const pkcs11h_token_id_t token, char * const pin, const size_t pin_max ); }; namespace pkcs11QCAPlugin { class pkcs11KeyStoreEntryContext; //---------------------------------------------------------------------------- // pkcs11KeyStoreListContext //---------------------------------------------------------------------------- class pkcs11KeyStoreListContext : public KeyStoreListContext { Q_OBJECT private: struct pkcs11KeyStoreItem { protected: int _id; pkcs11h_token_id_t _token_id; QList _certs; public: pkcs11KeyStoreItem ( const int id, const pkcs11h_token_id_t token_id ) { _id = id;; pkcs11h_token_duplicateTokenId (&_token_id, token_id); } ~pkcs11KeyStoreItem () { if (_token_id != NULL) { pkcs11h_token_freeTokenId (_token_id); } } inline int id () const { return _id; } inline pkcs11h_token_id_t tokenId () const { return _token_id; } void registerCertificates ( const QList &certs ) { foreach (Certificate i, certs) { if (std::find (_certs.begin (), _certs.end (), i) == _certs.end ()) { _certs += i; } } } QMap friendlyNames () { QStringList names = makeFriendlyNames (_certs); QMap friendlyNames; for (int i=0;i _stores_t; _stores_t _stores; QHash _storesById; QMutex _mutexStores; bool _initialized; public: pkcs11KeyStoreListContext (Provider *p); ~pkcs11KeyStoreListContext (); Provider::Context * clone () const override; public: void start () override; void setUpdatesEnabled (bool enabled) override; KeyStoreEntryContext * entry ( int id, const QString &entryId ) override; KeyStoreEntryContext * entryPassive ( const QString &serialized ) override; KeyStore::Type type (int id) const override; QString storeId (int id) const override; QString name (int id) const override; QList entryTypes (int id) const override; QList keyStores () override; QList entryList (int id) override; bool _tokenPrompt ( void * const user_data, const pkcs11h_token_id_t token_id ); bool _pinPrompt ( void * const user_data, const pkcs11h_token_id_t token_id, SecureArray &pin ); void _emit_diagnosticText ( const QString &t ); private Q_SLOTS: void doReady (); void doUpdated (); private: pkcs11KeyStoreItem * _registerTokenId ( const pkcs11h_token_id_t token_id ); void _clearStores (); pkcs11KeyStoreEntryContext * _keyStoreEntryByCertificateId ( const pkcs11h_certificate_id_t certificate_id, const bool has_private, const CertificateChain &chain, const QString &description ) const; QString _tokenId2storeId ( const pkcs11h_token_id_t token_id ) const; QString _serializeCertificate ( const pkcs11h_certificate_id_t certificate_id, const CertificateChain &chain, const bool has_private ) const; void _deserializeCertificate ( const QString &from, pkcs11h_certificate_id_t * const p_certificate_id, bool * const p_has_private, CertificateChain &chain ) const; QString _escapeString ( const QString &from ) const; QString _unescapeString ( const QString &from ) const; }; static pkcs11KeyStoreListContext *s_keyStoreList = NULL; //---------------------------------------------------------------------------- // pkcs11Exception //---------------------------------------------------------------------------- class pkcs11Exception { private: CK_RV _rv; QString _msg; private: pkcs11Exception () {} public: pkcs11Exception (const CK_RV rv, const QString &msg) { _rv = rv; _msg = msg; } pkcs11Exception (const pkcs11Exception &other) { *this = other; } pkcs11Exception & operator = (const pkcs11Exception &other) { _rv = other._rv; _msg = other._msg; return *this; } CK_RV rv () const { return _rv; } QString message () const { return _msg + QString (" ") + pkcs11h_getMessage (_rv); } }; //---------------------------------------------------------------------------- // pkcs11RSAContext //---------------------------------------------------------------------------- class pkcs11RSAContext : public RSAContext { Q_OBJECT private: bool _has_privateKeyRole; pkcs11h_certificate_id_t _pkcs11h_certificate_id; pkcs11h_certificate_t _pkcs11h_certificate; RSAPublicKey _pubkey; QString _serialized; struct _sign_data_s { SignatureAlgorithm alg; Hash *hash; QByteArray raw; _sign_data_s() { hash = NULL; } } _sign_data; public: pkcs11RSAContext ( Provider *p, const pkcs11h_certificate_id_t pkcs11h_certificate_id, const QString &serialized, const RSAPublicKey &pubkey ) : RSAContext (p) { CK_RV rv; QCA_logTextMessage ( "pkcs11RSAContext::pkcs11RSAContext1 - entry", Logger::Debug ); _has_privateKeyRole = true; _pkcs11h_certificate_id = NULL; _pkcs11h_certificate = NULL; _pubkey = pubkey; _serialized = serialized; _clearSign (); if ( (rv = pkcs11h_certificate_duplicateCertificateId ( &_pkcs11h_certificate_id, pkcs11h_certificate_id )) != CKR_OK ) { throw pkcs11Exception (rv, "Memory error"); } QCA_logTextMessage ( "pkcs11RSAContext::pkcs11RSAContext1 - return", Logger::Debug ); } pkcs11RSAContext (const pkcs11RSAContext &from) : RSAContext (from.provider ()) { CK_RV rv; QCA_logTextMessage ( "pkcs11RSAContext::pkcs11RSAContextC - entry", Logger::Debug ); _has_privateKeyRole = from._has_privateKeyRole; _pkcs11h_certificate_id = NULL; _pkcs11h_certificate = NULL; _pubkey = from._pubkey; _serialized = from._serialized; _sign_data.hash = NULL; _clearSign (); if ( (rv = pkcs11h_certificate_duplicateCertificateId ( &_pkcs11h_certificate_id, from._pkcs11h_certificate_id )) != CKR_OK ) { throw pkcs11Exception (rv, "Memory error"); } QCA_logTextMessage ( "pkcs11RSAContext::pkcs11RSAContextC - return", Logger::Debug ); } ~pkcs11RSAContext () { QCA_logTextMessage ( "pkcs11RSAContext::~pkcs11RSAContext - entry", Logger::Debug ); _clearSign (); if (_pkcs11h_certificate != NULL) { pkcs11h_certificate_freeCertificate (_pkcs11h_certificate); _pkcs11h_certificate = NULL; } if (_pkcs11h_certificate_id != NULL) { pkcs11h_certificate_freeCertificateId (_pkcs11h_certificate_id); _pkcs11h_certificate_id = NULL; } QCA_logTextMessage ( "pkcs11RSAContext::~pkcs11RSAContext - return", Logger::Debug ); } Provider::Context * clone () const override { return new pkcs11RSAContext (*this); } public: bool isNull () const override { return _pubkey.isNull (); } PKey::Type type () const override { return _pubkey.type (); } bool isPrivate () const override { return _has_privateKeyRole; } bool canExport () const override { return !_has_privateKeyRole; } void convertToPublic () override { QCA_logTextMessage ( "pkcs11RSAContext::convertToPublic - entry", Logger::Debug ); if (_has_privateKeyRole) { if (_pkcs11h_certificate != NULL) { pkcs11h_certificate_freeCertificate (_pkcs11h_certificate); _pkcs11h_certificate = NULL; } _has_privateKeyRole = false; } QCA_logTextMessage ( "pkcs11RSAContext::convertToPublic - return", Logger::Debug ); } int bits () const override { return _pubkey.bitSize (); } int maximumEncryptSize ( EncryptionAlgorithm alg ) const override { return _pubkey.maximumEncryptSize (alg); } SecureArray encrypt ( const SecureArray &in, EncryptionAlgorithm alg ) override { return _pubkey.encrypt (in, alg); } bool decrypt ( const SecureArray &in, SecureArray *out, EncryptionAlgorithm alg ) override { bool session_locked = false; bool ret = false; QCA_logTextMessage ( QString::asprintf ( "pkcs11RSAContext::decrypt - decrypt in.size()=%d, alg=%d", in.size (), (int)alg ), Logger::Debug ); try { CK_MECHANISM_TYPE mech; CK_RV rv; size_t my_size; switch (alg) { case EME_PKCS1v15: mech = CKM_RSA_PKCS; break; case EME_PKCS1_OAEP: mech = CKM_RSA_PKCS_OAEP; break; default: throw pkcs11Exception (CKR_FUNCTION_NOT_SUPPORTED, "Invalid algorithm"); break; } _ensureCertificate (); if ( (rv = pkcs11h_certificate_lockSession ( _pkcs11h_certificate )) != CKR_OK ) { throw pkcs11Exception (rv, "Cannot lock session"); } session_locked = true; if ( (rv = pkcs11h_certificate_decryptAny ( _pkcs11h_certificate, mech, (const unsigned char *)in.constData (), in.size (), NULL, &my_size )) != CKR_OK ) { throw pkcs11Exception (rv, "Decryption error"); } out->resize (my_size); if ( (rv = pkcs11h_certificate_decryptAny ( _pkcs11h_certificate, mech, (const unsigned char *)in.constData (), in.size (), (unsigned char *)out->data (), &my_size )) != CKR_OK ) { throw pkcs11Exception (rv, "Decryption error"); } rv = out->resize (my_size); if ( (rv = pkcs11h_certificate_releaseSession ( _pkcs11h_certificate )) != CKR_OK ) { throw pkcs11Exception (rv, "Cannot release session"); } session_locked = false; ret = true; } catch (const pkcs11Exception &e) { if (session_locked) { pkcs11h_certificate_releaseSession ( _pkcs11h_certificate ); session_locked = false; } if (s_keyStoreList != NULL) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Cannot decrypt: %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } } QCA_logTextMessage ( QString::asprintf ( "pkcs11RSAContext::decrypt - decrypt out->size()=%d", out->size () ), Logger::Debug ); return ret; } void startSign ( SignatureAlgorithm alg, SignatureFormat ) override { _clearSign (); _sign_data.alg = alg; switch (_sign_data.alg) { case EMSA3_SHA1: _sign_data.hash = new Hash ("sha1"); break; case EMSA3_MD5: _sign_data.hash = new Hash ("md5"); break; case EMSA3_MD2: _sign_data.hash = new Hash ("md2"); break; case EMSA3_Raw: break; case SignatureUnknown: case EMSA1_SHA1: case EMSA3_RIPEMD160: default: QCA_logTextMessage ( QString::asprintf ( "PKCS#11: Invalid hash algorithm %d", _sign_data.alg ), Logger::Warning ); break; } } void startVerify ( SignatureAlgorithm alg, SignatureFormat sf ) override { _pubkey.startVerify (alg, sf); } void update ( const MemoryRegion &in ) override { if (_has_privateKeyRole) { if (_sign_data.hash != NULL) { _sign_data.hash->update (in); } else { _sign_data.raw.append (in.toByteArray ()); } } else { _pubkey.update (in); } } QByteArray endSign () override { QByteArray result; bool session_locked = false; QCA_logTextMessage ( "pkcs11RSAContext::endSign - entry", Logger::Debug ); try { QByteArray final; CK_RV rv; // from some strange reason I got 2047... (for some) <---- BUG?!?!?! int myrsa_size=(_pubkey.bitSize () + 7) / 8; if (_sign_data.hash != NULL) { final = emsa3Encode ( _sign_data.hash->type (), _sign_data.hash->final ().toByteArray (), myrsa_size ); } else { final = _sign_data.raw; } if (final.size () == 0) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Cannot encode signature"); } _ensureCertificate (); size_t my_size; if ( (rv = pkcs11h_certificate_lockSession ( _pkcs11h_certificate )) != CKR_OK ) { throw pkcs11Exception (rv, "Cannot lock session"); } session_locked = true; if ( (rv = pkcs11h_certificate_signAny ( _pkcs11h_certificate, CKM_RSA_PKCS, (const unsigned char *)final.constData (), (size_t)final.size (), NULL, &my_size )) != CKR_OK ) { throw pkcs11Exception (rv, "Signature failed"); } result.resize (my_size); if ( (rv = pkcs11h_certificate_signAny ( _pkcs11h_certificate, CKM_RSA_PKCS, (const unsigned char *)final.constData (), (size_t)final.size (), (unsigned char *)result.data (), &my_size )) != CKR_OK ) { throw pkcs11Exception (rv, "Signature failed"); } result.resize (my_size); if ( (rv = pkcs11h_certificate_releaseSession ( _pkcs11h_certificate )) != CKR_OK ) { throw pkcs11Exception (rv, "Cannot release session"); } session_locked = false; } catch (const pkcs11Exception &e) { result.clear (); if (session_locked) { pkcs11h_certificate_releaseSession ( _pkcs11h_certificate ); session_locked = false; } if (s_keyStoreList != NULL) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Cannot sign: %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } } _clearSign (); QCA_logTextMessage ( QString::asprintf ( "pkcs11RSAContext::endSign - return result.size ()=%d", result.size () ), Logger::Debug ); return result; } virtual bool validSignature ( const QByteArray &sig ) { return _pubkey.validSignature (sig); } void createPrivate ( int bits, int exp, bool block ) override { Q_UNUSED(bits); Q_UNUSED(exp); Q_UNUSED(block); } void createPrivate ( const BigInteger &n, const BigInteger &e, const BigInteger &p, const BigInteger &q, const BigInteger &d ) override { Q_UNUSED(n); Q_UNUSED(e); Q_UNUSED(p); Q_UNUSED(q); Q_UNUSED(d); } void createPublic ( const BigInteger &n, const BigInteger &e ) override { Q_UNUSED(n); Q_UNUSED(e); } BigInteger n () const override { return _pubkey.n (); } BigInteger e () const override { return _pubkey.e (); } BigInteger p () const override { return BigInteger(); } BigInteger q () const override { return BigInteger(); } BigInteger d () const override { return BigInteger(); } public: PublicKey _publicKey () const { return _pubkey; } bool _isTokenAvailable() const { bool ret; QCA_logTextMessage ( "pkcs11RSAContext::_ensureTokenAvailable - entry", Logger::Debug ); ret = pkcs11h_token_ensureAccess ( _pkcs11h_certificate_id->token_id, NULL, 0 ) == CKR_OK; QCA_logTextMessage ( QString::asprintf ( "pkcs11RSAContext::_ensureTokenAvailable - return ret=%d", ret ? 1 : 0 ), Logger::Debug ); return ret; } bool _ensureTokenAccess () { bool ret; QCA_logTextMessage ( "pkcs11RSAContext::_ensureTokenAccess - entry", Logger::Debug ); ret = pkcs11h_token_ensureAccess ( _pkcs11h_certificate_id->token_id, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL ) == CKR_OK; QCA_logTextMessage ( QString::asprintf ( "pkcs11RSAContext::_ensureTokenAccess - return ret=%d", ret ? 1 : 0 ), Logger::Debug ); return ret; } private: void _clearSign () { _sign_data.raw.clear (); _sign_data.alg = SignatureUnknown; delete _sign_data.hash; _sign_data.hash = NULL; } void _ensureCertificate () { CK_RV rv; QCA_logTextMessage ( "pkcs11RSAContext::_ensureCertificate - entry", Logger::Debug ); if (_pkcs11h_certificate == NULL) { if ( (rv = pkcs11h_certificate_create ( _pkcs11h_certificate_id, &_serialized, PKCS11H_PROMPT_MASK_ALLOW_ALL, PKCS11H_PIN_CACHE_INFINITE, &_pkcs11h_certificate )) != CKR_OK ) { throw pkcs11Exception (rv, "Cannot create low-level certificate"); } } QCA_logTextMessage ( "pkcs11RSAContext::_ensureCertificate - return", Logger::Debug ); } }; //---------------------------------------------------------------------------- // pkcs11PKeyContext //---------------------------------------------------------------------------- class pkcs11PKeyContext : public PKeyContext { private: PKeyBase *_k; public: pkcs11PKeyContext (Provider *p) : PKeyContext (p) { _k = NULL; } ~pkcs11PKeyContext () { delete _k; _k = NULL; } Provider::Context * clone () const override { pkcs11PKeyContext *c = new pkcs11PKeyContext (*this); c->_k = (PKeyBase *)_k->clone(); return c; } public: QList supportedTypes () const override { QList list; list += PKey::RSA; return list; } QList supportedIOTypes () const override { QList list; list += PKey::RSA; return list; } QList supportedPBEAlgorithms () const override { QList list; return list; } PKeyBase * key () override { return _k; } const PKeyBase * key () const override { return _k; } void setKey (PKeyBase *key) override { delete _k; _k = key; } bool importKey ( const PKeyBase *key ) override { Q_UNUSED(key); return false; } static int passphrase_cb ( char *buf, int size, int rwflag, void *u ) { Q_UNUSED(buf); Q_UNUSED(size); Q_UNUSED(rwflag); Q_UNUSED(u); return 0; } QByteArray publicToDER () const override { return static_cast(_k)->_publicKey ().toDER (); } QString publicToPEM () const override { return static_cast(_k)->_publicKey ().toPEM (); } ConvertResult publicFromDER ( const QByteArray &in ) override { Q_UNUSED(in); return ErrorDecode; } ConvertResult publicFromPEM ( const QString &s ) override { Q_UNUSED(s); return ErrorDecode; } SecureArray privateToDER( const SecureArray &passphrase, PBEAlgorithm pbe ) const override { Q_UNUSED(passphrase); Q_UNUSED(pbe); return SecureArray (); } QString privateToPEM ( const SecureArray &passphrase, PBEAlgorithm pbe ) const override { Q_UNUSED(passphrase); Q_UNUSED(pbe); return QString (); } ConvertResult privateFromDER ( const SecureArray &in, const SecureArray &passphrase ) override { Q_UNUSED(in); Q_UNUSED(passphrase); return ErrorDecode; } ConvertResult privateFromPEM ( const QString &s, const SecureArray &passphrase ) override { Q_UNUSED(s); Q_UNUSED(passphrase); return ErrorDecode; } }; //---------------------------------------------------------------------------- // pkcs11KeyStoreEntryContext //---------------------------------------------------------------------------- class pkcs11KeyStoreEntryContext : public KeyStoreEntryContext { private: KeyStoreEntry::Type _item_type; KeyBundle _key; Certificate _cert; QString _storeId; QString _id; QString _serialized; QString _storeName; QString _name; public: pkcs11KeyStoreEntryContext ( const Certificate &cert, const QString &storeId, const QString &serialized, const QString &storeName, const QString &name, Provider *p ) : KeyStoreEntryContext(p) { _item_type = KeyStoreEntry::TypeCertificate; _cert = cert; _storeId = storeId; _id = certificateHash (_cert); _serialized = serialized; _storeName = storeName; _name = name; } pkcs11KeyStoreEntryContext ( const KeyBundle &key, const QString &storeId, const QString &serialized, const QString &storeName, const QString &name, Provider *p ) : KeyStoreEntryContext(p) { _item_type = KeyStoreEntry::TypeKeyBundle; _key = key; _storeId = storeId, _id = certificateHash (key.certificateChain ().primary ()); _serialized = serialized; _storeName = storeName; _name = name; } pkcs11KeyStoreEntryContext ( const pkcs11KeyStoreEntryContext &from ) : KeyStoreEntryContext(from) { _item_type = from._item_type; _key = from._key; _storeId = from._storeId; _id = from._id; _serialized = from._serialized; _storeName = from._storeName; _name = from._name; } Provider::Context * clone () const override { return new pkcs11KeyStoreEntryContext (*this); } public: KeyStoreEntry::Type type () const override { return _item_type; } QString name () const override { return _name; } QString id () const override { return _id; } KeyBundle keyBundle () const override { return _key; } Certificate certificate () const override { return _cert; } QString storeId () const override { return _storeId; } QString storeName () const override { return _storeName; } bool isAvailable() const override { return static_cast(static_cast(_key.privateKey ().context ())->key ())->_isTokenAvailable (); } bool ensureAccess () override { return static_cast(static_cast(_key.privateKey ().context ())->key ())->_ensureTokenAccess (); } QString serialize () const override { return _serialized; } }; //---------------------------------------------------------------------------- // pkcs11QCACrypto //---------------------------------------------------------------------------- class pkcs11QCACrypto { private: static int _pkcs11h_crypto_qca_initialize ( void * const global_data ) { Q_UNUSED(global_data); return TRUE; //krazy:exclude=captruefalse } static int _pkcs11h_crypto_qca_uninitialize ( void * const global_data ) { Q_UNUSED(global_data); return TRUE; //krazy:exclude=captruefalse } static int _pkcs11h_crypto_qca_certificate_get_expiration ( void * const global_data, const unsigned char * const blob, const size_t blob_size, time_t * const expiration ) { Q_UNUSED(global_data); Certificate cert = Certificate::fromDER ( QByteArray ( (char *)blob, blob_size ) ); *expiration = cert.notValidAfter ().toTime_t (); return TRUE; //krazy:exclude=captruefalse } static int _pkcs11h_crypto_qca_certificate_get_dn ( void * const global_data, const unsigned char * const blob, const size_t blob_size, char * const dn, const size_t dn_max ) { Q_UNUSED(global_data); Certificate cert = Certificate::fromDER ( QByteArray ( (char *)blob, blob_size ) ); QString qdn = cert.subjectInfoOrdered ().toString (); if ((size_t)qdn.length () > dn_max-1) { return FALSE; //krazy:exclude=captruefalse } else { qstrcpy (dn, myPrintable (qdn)); return TRUE; //krazy:exclude=captruefalse } } static int _pkcs11h_crypto_qca_certificate_is_issuer ( void * const global_data, const unsigned char * const signer_blob, const size_t signer_blob_size, const unsigned char * const cert_blob, const size_t cert_blob_size ) { Q_UNUSED(global_data); Certificate signer = Certificate::fromDER ( QByteArray ( (char *)signer_blob, signer_blob_size ) ); Certificate cert = Certificate::fromDER ( QByteArray ( (char *)cert_blob, cert_blob_size ) ); return signer.isIssuerOf (cert); } public: static pkcs11h_engine_crypto_t crypto; }; pkcs11h_engine_crypto_t pkcs11QCACrypto::crypto = { NULL, _pkcs11h_crypto_qca_initialize, _pkcs11h_crypto_qca_uninitialize, _pkcs11h_crypto_qca_certificate_get_expiration, _pkcs11h_crypto_qca_certificate_get_dn, _pkcs11h_crypto_qca_certificate_is_issuer }; //---------------------------------------------------------------------------- // pkcs11KeyStoreListContext //---------------------------------------------------------------------------- pkcs11KeyStoreListContext::pkcs11KeyStoreListContext (Provider *p) : KeyStoreListContext(p) { QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::pkcs11KeyStoreListContext - entry Provider=%p", (void *)p ), Logger::Debug ); _last_id = 0; _initialized = false; QCA_logTextMessage ( "pkcs11KeyStoreListContext::pkcs11KeyStoreListContext - return", Logger::Debug ); } pkcs11KeyStoreListContext::~pkcs11KeyStoreListContext () { QCA_logTextMessage ( "pkcs11KeyStoreListContext::~pkcs11KeyStoreListContext - entry", Logger::Debug ); s_keyStoreList = NULL; _clearStores (); QCA_logTextMessage ( "pkcs11KeyStoreListContext::~pkcs11KeyStoreListContext - return", Logger::Debug ); } Provider::Context * pkcs11KeyStoreListContext::clone () const { QCA_logTextMessage ( "pkcs11KeyStoreListContext::clone - entry/return", Logger::Debug ); return NULL; } void pkcs11KeyStoreListContext::start () { QCA_logTextMessage ( "pkcs11KeyStoreListContext::start - entry", Logger::Debug ); QMetaObject::invokeMethod(this, "doReady", Qt::QueuedConnection); QCA_logTextMessage ( "pkcs11KeyStoreListContext::start - return", Logger::Debug ); } void pkcs11KeyStoreListContext::setUpdatesEnabled (bool enabled) { QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::setUpdatesEnabled - entry enabled=%d", enabled ? 1 : 0 ), Logger::Debug ); try { pkcs11Provider *p = static_cast(provider ()); if (enabled) { p->startSlotEvents (); } else { p->stopSlotEvents (); } } catch (const pkcs11Exception &e) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Start event failed %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } QCA_logTextMessage ( "pkcs11KeyStoreListContext::setUpdatesEnabled - return", Logger::Debug ); } KeyStoreEntryContext * pkcs11KeyStoreListContext::entry ( int id, const QString &entryId ) { QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::entry - entry/return id=%d entryId='%s'", id, myPrintable (entryId) ), Logger::Debug ); Q_UNUSED(id); Q_UNUSED(entryId); return NULL; } KeyStoreEntryContext * pkcs11KeyStoreListContext::entryPassive ( const QString &serialized ) { KeyStoreEntryContext *entry = NULL; pkcs11h_certificate_id_t certificate_id = NULL; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::entryPassive - entry serialized='%s'", myPrintable (serialized) ), Logger::Debug ); try { if (serialized.startsWith ("qca-pkcs11/")) { CertificateChain chain; bool has_private; _deserializeCertificate (serialized, &certificate_id, &has_private, chain); pkcs11KeyStoreItem *sentry = _registerTokenId (certificate_id->token_id); sentry->registerCertificates (chain); QMap friendlyNames = sentry->friendlyNames (); entry = _keyStoreEntryByCertificateId ( certificate_id, has_private, chain, friendlyNames[certificateHash (chain.primary ())] ); } } catch (const pkcs11Exception &e) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Add key store entry %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } if (certificate_id != NULL) { pkcs11h_certificate_freeCertificateId (certificate_id); certificate_id = NULL; } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::entryPassive - return entry=%p", (void *)entry ), Logger::Debug ); return entry; } KeyStore::Type pkcs11KeyStoreListContext::type (int id) const { Q_UNUSED(id); QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::type - entry/return id=%d", id ), Logger::Debug ); return KeyStore::SmartCard; } QString pkcs11KeyStoreListContext::storeId (int id) const { QString ret; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::storeId - entry id=%d", id ), Logger::Debug ); if (_storesById.contains (id)) { ret = _tokenId2storeId (_storesById[id]->tokenId ()); } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::storeId - return ret=%s", myPrintable (ret) ), Logger::Debug ); return ret; } QString pkcs11KeyStoreListContext::name (int id) const { QString ret; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::name - entry id=%d", id ), Logger::Debug ); if (_storesById.contains (id)) { ret = _storesById[id]->tokenId ()->label; } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::name - return ret=%s", myPrintable (ret) ), Logger::Debug ); return ret; } QList pkcs11KeyStoreListContext::entryTypes (int id) const { Q_UNUSED(id); QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::entryTypes - entry/return id=%d", id ), Logger::Debug ); QList list; list += KeyStoreEntry::TypeKeyBundle; list += KeyStoreEntry::TypeCertificate; return list; } QList pkcs11KeyStoreListContext::keyStores () { pkcs11h_token_id_list_t tokens = NULL; QList out; QCA_logTextMessage ( "pkcs11KeyStoreListContext::keyStores - entry", Logger::Debug ); try { CK_RV rv; /* * Get available tokens */ if ( (rv = pkcs11h_token_enumTokenIds ( PKCS11H_ENUM_METHOD_CACHE_EXIST, &tokens )) != CKR_OK ) { throw pkcs11Exception (rv, "Enumerating tokens"); } /* * Register all tokens, unmark * them from remove list */ QList to_remove = _storesById.keys (); for ( pkcs11h_token_id_list_t entry = tokens; entry != NULL; entry = entry->next ) { pkcs11KeyStoreItem *item = _registerTokenId (entry->token_id); out += item->id (); to_remove.removeAll (item->id ()); } /* * Remove all items * that were not discovered */ { QMutexLocker l(&_mutexStores); foreach (int i, to_remove) { pkcs11KeyStoreItem *item = _storesById[i]; _storesById.remove (item->id ()); _stores.removeAll (item); delete item; item = NULL; } } } catch (const pkcs11Exception &e) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Cannot get key stores: %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } if (tokens != NULL) { pkcs11h_token_freeTokenIdList (tokens); } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::keyStores - return out.size()=%d", out.size () ), Logger::Debug ); return out; } QList pkcs11KeyStoreListContext::entryList (int id) { pkcs11h_certificate_id_list_t certs = NULL; QList out; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::entryList - entry id=%d", id ), Logger::Debug ); try { CK_RV rv; if (_storesById.contains (id)) { pkcs11KeyStoreItem *entry = _storesById[id]; pkcs11h_certificate_id_list_t issuers = NULL; pkcs11h_certificate_id_list_t current = NULL; QList listCerts; QList listIssuers; QList listIds; int i = 0; if ( (rv = pkcs11h_certificate_enumTokenCertificateIds ( entry->tokenId (), PKCS11H_ENUM_METHOD_CACHE, NULL, PKCS11H_PROMPT_MASK_ALLOW_ALL, &issuers, &certs )) != CKR_OK ) { throw pkcs11Exception (rv, "Enumerate certificates"); } for ( current=certs; current!=NULL; current=current->next ) { if (current->certificate_id->certificate_blob_size > 0) { listCerts += Certificate::fromDER ( QByteArray ( (char *)current->certificate_id->certificate_blob, current->certificate_id->certificate_blob_size ) ); } } for ( current=issuers; current!=NULL; current=current->next ) { if (current->certificate_id->certificate_blob_size > 0) { listIssuers += Certificate::fromDER ( QByteArray ( (char *)current->certificate_id->certificate_blob, current->certificate_id->certificate_blob_size ) ); } } entry->registerCertificates (listIssuers + listCerts); QMap friendlyNames = entry->friendlyNames (); QList listIssuersForComplete; if (dynamic_cast (provider ())->_allowLoadRootCA) { listIssuersForComplete = listIssuers; } else { foreach (Certificate c, listIssuers) { if (!c.isSelfSigned ()) { listIssuersForComplete += c; } } } for ( i=0, current=issuers; current!=NULL; i++, current=current->next ) { try { if (listIssuers[i].isNull ()) { throw pkcs11Exception (CKR_ARGUMENTS_BAD, "Invalid certificate"); } if ( listIssuers[i].isSelfSigned () && dynamic_cast (provider ())->_allowLoadRootCA ) { CertificateChain chain = CertificateChain (listIssuers[i]).complete (listIssuersForComplete); out += _keyStoreEntryByCertificateId ( current->certificate_id, false, chain, friendlyNames[certificateHash (chain.primary ())] ); } } catch (const pkcs11Exception &e) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Add key store entry %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } } for ( i=0, current=certs; current!=NULL; i++, current=current->next ) { try { if (listCerts[i].isNull ()) { throw pkcs11Exception (CKR_ARGUMENTS_BAD, "Invalid certificate"); } CertificateChain chain = CertificateChain (listCerts[i]).complete (listIssuersForComplete); out += _keyStoreEntryByCertificateId ( current->certificate_id, true, chain, friendlyNames[certificateHash (chain.primary ())] ); } catch (const pkcs11Exception &e) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Add key store entry %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } } } } catch (const pkcs11Exception &e) { s_keyStoreList->_emit_diagnosticText ( QString::asprintf ( "PKCS#11: Enumerating store failed %lu-'%s'.\n", e.rv (), myPrintable (e.message ()) ) ); } if (certs != NULL) { pkcs11h_certificate_freeCertificateIdList (certs); } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::entryList - return out.size()=%d", out.size () ), Logger::Debug ); return out; } bool pkcs11KeyStoreListContext::_tokenPrompt ( void * const user_data, const pkcs11h_token_id_t token_id ) { KeyStoreEntry entry; KeyStoreEntryContext *context = NULL; QString storeId, storeName; bool ret = false; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_tokenPrompt - entry user_data=%p, token_id=%p", user_data, (void *)token_id ), Logger::Debug ); if (user_data != NULL) { QString *serialized = (QString *)user_data; context = entryPassive (*serialized); storeId = context->storeId (); storeName = context->storeName (); entry.change (context); } else { _registerTokenId (token_id); storeId = _tokenId2storeId (token_id); storeName = token_id->label; } TokenAsker asker; asker.ask ( KeyStoreInfo (KeyStore::SmartCard, storeId, storeName), entry, context ); asker.waitForResponse (); if (asker.accepted ()) { ret = true; } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_tokenPrompt - return ret=%d", ret ? 1 : 0 ), Logger::Debug ); return ret; } bool pkcs11KeyStoreListContext::_pinPrompt ( void * const user_data, const pkcs11h_token_id_t token_id, SecureArray &pin ) { KeyStoreEntry entry; KeyStoreEntryContext *context = NULL; QString storeId, storeName; bool ret = false; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_pinPrompt - entry user_data=%p, token_id=%p", user_data, (void *)token_id ), Logger::Debug ); pin = SecureArray(); if (user_data != NULL) { QString *serialized = (QString *)user_data; context = entryPassive (*serialized); storeId = context->storeId (); storeName = context->storeName (); entry.change (context); } else { _registerTokenId (token_id); storeId = _tokenId2storeId (token_id); storeName = token_id->label; } PasswordAsker asker; asker.ask ( Event::StylePIN, KeyStoreInfo (KeyStore::SmartCard, storeId, storeName), entry, context ); asker.waitForResponse (); if (asker.accepted ()) { ret = true; pin = asker.password (); } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_pinPrompt - return ret=%d", ret ? 1 : 0 ), Logger::Debug ); return ret; } void pkcs11KeyStoreListContext::_emit_diagnosticText ( const QString &t ) { QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_emit_diagnosticText - entry t='%s'", myPrintable (t) ), Logger::Debug ); QCA_logTextMessage (t, Logger::Warning); emit diagnosticText (t); QCA_logTextMessage ( "pkcs11KeyStoreListContext::_emit_diagnosticText - return", Logger::Debug ); } void pkcs11KeyStoreListContext::doReady () { QCA_logTextMessage ( "pkcs11KeyStoreListContext::doReady - entry", Logger::Debug ); emit busyEnd (); QCA_logTextMessage ( "pkcs11KeyStoreListContext::doReady - return", Logger::Debug ); } void pkcs11KeyStoreListContext::doUpdated () { QCA_logTextMessage ( "pkcs11KeyStoreListContext::doUpdated - entry", Logger::Debug ); emit updated (); QCA_logTextMessage ( "pkcs11KeyStoreListContext::doUpdated - return", Logger::Debug ); } pkcs11KeyStoreListContext::pkcs11KeyStoreItem * pkcs11KeyStoreListContext::_registerTokenId ( const pkcs11h_token_id_t token_id ) { QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_registerTokenId - entry token_id=%p", (void *)token_id ), Logger::Debug ); QMutexLocker l(&_mutexStores); _stores_t::iterator i=_stores.begin (); while ( i != _stores.end () && !pkcs11h_token_sameTokenId ( token_id, (*i)->tokenId () ) ) { i++; } pkcs11KeyStoreItem *entry = NULL; if (i == _stores.end ()) { /* * Deal with last_id overlap */ while (_storesById.find (++_last_id) != _storesById.end ()); entry = new pkcs11KeyStoreItem (_last_id, token_id); _stores += entry; _storesById.insert (entry->id (), entry); } else { entry = (*i); } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_registerTokenId - return entry=%p", (void *)token_id ), Logger::Debug ); return entry; } void pkcs11KeyStoreListContext::_clearStores () { QCA_logTextMessage ( "pkcs11KeyStoreListContext::_clearStores - entry", Logger::Debug ); QMutexLocker l(&_mutexStores); _storesById.clear (); foreach (pkcs11KeyStoreItem *i, _stores) { delete i; } _stores.clear (); QCA_logTextMessage ( "pkcs11KeyStoreListContext::_clearStores - return", Logger::Debug ); } pkcs11KeyStoreEntryContext * pkcs11KeyStoreListContext::_keyStoreEntryByCertificateId ( const pkcs11h_certificate_id_t certificate_id, const bool has_private, const CertificateChain &chain, const QString &_description ) const { pkcs11KeyStoreEntryContext *entry = NULL; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_keyStoreEntryByCertificateId - entry certificate_id=%p, has_private=%d, chain.size()=%d", (void *)certificate_id, has_private ? 1 : 0, chain.size () ), Logger::Debug ); if (certificate_id == NULL) { throw pkcs11Exception (CKR_ARGUMENTS_BAD, "Missing certificate object"); } QString serialized = _serializeCertificate ( certificate_id, chain, has_private ); QString description = _description; Certificate cert = chain.primary (); if (description.isEmpty ()) { description = cert.subjectInfoOrdered ().toString () + " by " + cert.issuerInfo ().value (CommonName, "Unknown"); } if (has_private) { pkcs11RSAContext *rsakey = new pkcs11RSAContext ( provider(), certificate_id, serialized, cert.subjectPublicKey ().toRSA () ); pkcs11PKeyContext *pkc = new pkcs11PKeyContext (provider ()); pkc->setKey (rsakey); PrivateKey privkey; privkey.change (pkc); KeyBundle key; key.setCertificateChainAndKey ( chain, privkey ); entry = new pkcs11KeyStoreEntryContext ( key, _tokenId2storeId (certificate_id->token_id), serialized, certificate_id->token_id->label, description, provider () ); } else { entry = new pkcs11KeyStoreEntryContext ( cert, _tokenId2storeId (certificate_id->token_id), serialized, certificate_id->token_id->label, description, provider() ); } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_keyStoreEntryByCertificateId - return entry=%p", (void *)entry ), Logger::Debug ); return entry; } QString pkcs11KeyStoreListContext::_tokenId2storeId ( const pkcs11h_token_id_t token_id ) const { QString storeId; size_t len; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_tokenId2storeId - entry token_id=%p", (void *)token_id ), Logger::Debug ); if ( pkcs11h_token_serializeTokenId ( NULL, &len, token_id ) != CKR_OK ) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Cannot serialize token id"); } QByteArray buf; buf.resize ((int)len); if ( pkcs11h_token_serializeTokenId ( buf.data (), &len, token_id ) != CKR_OK ) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Cannot serialize token id"); } buf.resize ((int)len); storeId = "qca-pkcs11/" + _escapeString (QString::fromUtf8 (buf)); QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_tokenId2storeId - return storeId='%s'", myPrintable (storeId) ), Logger::Debug ); return storeId; } QString pkcs11KeyStoreListContext::_serializeCertificate ( const pkcs11h_certificate_id_t certificate_id, const CertificateChain &chain, const bool has_private ) const { QString serialized; size_t len; QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_serializeCertificate - entry certificate_id=%p, xx, has_private=%d", (void *)certificate_id, has_private ? 1 : 0 ), Logger::Debug ); if ( pkcs11h_certificate_serializeCertificateId ( NULL, &len, certificate_id ) != CKR_OK ) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Cannot serialize certificate id"); } QByteArray buf; buf.resize ((int)len); if ( pkcs11h_certificate_serializeCertificateId ( buf.data (), &len, certificate_id ) != CKR_OK ) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Cannot serialize certificate id"); } buf.resize ((int)len); serialized = QString::asprintf ( "qca-pkcs11/0/%s/%d/", myPrintable(_escapeString (QString::fromUtf8 (buf))), has_private ? 1 : 0 ); QStringList list; foreach (Certificate i, chain) { list += _escapeString (Base64 ().arrayToString (i.toDER ())); } serialized.append (list.join ("/")); QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_serializeCertificate - return serialized='%s'", myPrintable (serialized) ), Logger::Debug ); return serialized; } void pkcs11KeyStoreListContext::_deserializeCertificate ( const QString &from, pkcs11h_certificate_id_t * const p_certificate_id, bool * const p_has_private, CertificateChain &chain ) const { pkcs11h_certificate_id_t certificate_id = NULL; chain.clear (); QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_deserializeCertificate - entry from='%s', p_certificate_id=%p, p_has_private=%p", myPrintable (from), (void *)p_certificate_id, (void *)p_has_private ), Logger::Debug ); try { int n = 0; CK_RV rv; *p_certificate_id = NULL; *p_has_private = false; QStringList list = from.split ("/"); if (list.size () < 5) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Invalid serialization"); } if (list[n++] != "qca-pkcs11") { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Invalid serialization"); } if (list[n++].toInt () != 0) { throw pkcs11Exception (CKR_FUNCTION_FAILED, "Invalid serialization version"); } if ( (rv = pkcs11h_certificate_deserializeCertificateId ( &certificate_id, myPrintable (_unescapeString (list[n++])) )) != CKR_OK ) { throw pkcs11Exception (rv, "Invalid serialization"); } *p_has_private = list[n++].toInt () != 0; QByteArray endCertificateBytes = Base64 ().stringToArray (_unescapeString (list[n++])).toByteArray (); Certificate endCertificate = Certificate::fromDER (endCertificateBytes); if (endCertificate.isNull ()) { throw pkcs11Exception (rv, "Invalid certificate"); } if ( (rv = pkcs11h_certificate_setCertificateIdCertificateBlob ( certificate_id, (unsigned char *)endCertificateBytes.data (), (size_t)endCertificateBytes.size () )) != CKR_OK ) { throw pkcs11Exception (rv, "Invalid serialization"); } chain = endCertificate; while (n < list.size ()) { Certificate cert = Certificate::fromDER ( Base64 ().stringToArray (_unescapeString (list[n++])).toByteArray () ); if (cert.isNull ()) { throw pkcs11Exception (rv, "Invalid certificate"); } chain += cert; } *p_certificate_id = certificate_id; certificate_id = NULL; } catch (...) { if (certificate_id != NULL) { pkcs11h_certificate_freeCertificateId (certificate_id); certificate_id = NULL; } throw; } QCA_logTextMessage ( QString::asprintf ( "pkcs11KeyStoreListContext::_deserializeCertificate - return *p_certificate_id=%p, chain.size()=%d", (void *)*p_certificate_id, chain.size () ), Logger::Debug ); } QString pkcs11KeyStoreListContext::_escapeString ( const QString &from ) const { QString to; foreach (QChar c, from) { if (c == '/' || c == '\\') { to += QString::asprintf ("\\x%04x", c.unicode ()); } else { to += c; } } return to; } QString pkcs11KeyStoreListContext::_unescapeString ( const QString &from ) const { QString to; for (int i=0;i_logHook (flags, format, args); } void pkcs11Provider::__slotEventHook ( void * const global_data ) { pkcs11Provider *me = (pkcs11Provider *)global_data; me->_slotEventHook (); } PKCS11H_BOOL pkcs11Provider::__tokenPromptHook ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry ) { Q_UNUSED(retry); pkcs11Provider *me = (pkcs11Provider *)global_data; return me->_tokenPromptHook (user_data, token); } PKCS11H_BOOL pkcs11Provider::__pinPromptHook ( void * const global_data, void * const user_data, const pkcs11h_token_id_t token, const unsigned retry, char * const pin, const size_t pin_max ) { Q_UNUSED(retry); pkcs11Provider *me = (pkcs11Provider *)global_data; return me->_pinPromptHook (user_data, token, pin, pin_max); } void pkcs11Provider::_logHook ( const unsigned flags, const char * const format, va_list args ) { Logger::Severity severity; switch (flags) { case PKCS11H_LOG_DEBUG2: case PKCS11H_LOG_DEBUG1: severity = Logger::Debug; break; case PKCS11H_LOG_INFO: severity = Logger::Information; break; case PKCS11H_LOG_WARN: severity = Logger::Warning; break; case PKCS11H_LOG_ERROR: severity = Logger::Error; break; default: severity = Logger::Debug; break; } //@BEGIN-WORKAROUND // Qt vsprintf cannot can NULL for %s as vsprintf does. // QCA_logTextMessage (QString ().vsprintf (format, args), severity); char buffer[2048]; qvsnprintf (buffer, sizeof (buffer)-1, format, args); buffer[sizeof (buffer)-1] = '\x0'; QCA_logTextMessage (buffer, severity); //@END-WORKAROUND } void pkcs11Provider::_slotEventHook () { /* * This is called from a separate * thread. */ if (s_keyStoreList != NULL && _slotEventsActive) { QMetaObject::invokeMethod(s_keyStoreList, "doUpdated", Qt::QueuedConnection); } } PKCS11H_BOOL pkcs11Provider::_tokenPromptHook ( void * const user_data, const pkcs11h_token_id_t token ) { if (s_keyStoreList != NULL) { return s_keyStoreList->_tokenPrompt (user_data, token) ? TRUE : FALSE; //krazy:exclude=captruefalse } return FALSE; //krazy:exclude=captruefalse } PKCS11H_BOOL pkcs11Provider::_pinPromptHook ( void * const user_data, const pkcs11h_token_id_t token, char * const pin, const size_t pin_max ) { PKCS11H_BOOL ret = FALSE; //krazy:exclude=captruefalse if (s_keyStoreList != NULL) { SecureArray qpin; if (s_keyStoreList->_pinPrompt (user_data, token, qpin)) { if ((size_t)qpin.size () < pin_max-1) { memmove (pin, qpin.constData (), qpin.size ()); pin[qpin.size ()] = '\0'; ret = TRUE; //krazy:exclude=captruefalse } } } return ret; //krazy:exclude=captruefalse } class pkcs11Plugin : public QObject, public QCAPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0") Q_INTERFACES(QCAPlugin) public: Provider *createProvider() override { return new pkcs11Provider; } }; #include "qca-pkcs11.moc" diff --git a/plugins/qca-softstore/qca-softstore.cpp b/plugins/qca-softstore/qca-softstore.cpp index 50f17629..4a640615 100644 --- a/plugins/qca-softstore/qca-softstore.cpp +++ b/plugins/qca-softstore/qca-softstore.cpp @@ -1,1455 +1,1455 @@ /* * Copyright (C) 2007 Alon Bar-Lev * * 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 WITHANY 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 #include #include #include using namespace QCA; // qPrintable is ASCII only!!! #define myPrintable(s) (s).toUtf8 ().constData () namespace softstoreQCAPlugin { class softstoreKeyStoreListContext; static softstoreKeyStoreListContext *s_keyStoreList = NULL; enum KeyType { keyTypeInvalid, keyTypePKCS12, keyTypePKCS8Inline, keyTypePKCS8FilePEM, keyTypePKCS8FileDER }; enum PublicType { publicTypeInvalid, publicTypeX509Chain }; struct SoftStoreEntry { QString name; CertificateChain chain; KeyType keyReferenceType; QString keyReference; bool noPassphrase; int unlockTimeout; }; class softstorePKeyBase : public PKeyBase { Q_OBJECT private: bool _has_privateKeyRole; SoftStoreEntry _entry; QString _serialized; PrivateKey _privkey; PrivateKey _privkeySign; PublicKey _pubkey; QDateTime dueTime; public: static inline QString typeToString (PKey::Type t) { switch (t) { case PKey::RSA: return "rsa"; case PKey::DSA: return "dsa"; case PKey::DH: return "dh"; default: return ""; } } softstorePKeyBase ( const SoftStoreEntry &entry, const QString &serialized, Provider *p ) : PKeyBase (p, "rsa"/*typeToString (entry.chain.primary ().subjectPublicKey ().type ())*/) { QCA_logTextMessage ( "softstorePKeyBase::softstorePKeyBase1 - entry", Logger::Debug ); _has_privateKeyRole = true; _entry = entry; _serialized = serialized; _pubkey = _entry.chain.primary ().subjectPublicKey (); QCA_logTextMessage ( "softstorePKeyBase::softstorePKeyBase1 - return", Logger::Debug ); } softstorePKeyBase (const softstorePKeyBase &from) : PKeyBase (from.provider (), "rsa"/*typeToString (from._pubkey.type ())*/) { QCA_logTextMessage ( "softstorePKeyBase::softstorePKeyBaseC - entry", Logger::Debug ); _has_privateKeyRole = from._has_privateKeyRole; _entry = from._entry; _serialized = from._serialized; _pubkey = from._pubkey; _privkey = from._privkey; QCA_logTextMessage ( "softstorePKeyBase::softstorePKeyBaseC - return", Logger::Debug ); } ~softstorePKeyBase () { QCA_logTextMessage ( "softstorePKeyBase::~softstorePKeyBase - entry", Logger::Debug ); QCA_logTextMessage ( "softstorePKeyBase::~softstorePKeyBase - return", Logger::Debug ); } Provider::Context * clone () const override { return new softstorePKeyBase (*this); } public: bool isNull () const override { return _pubkey.isNull (); } PKey::Type type () const override { return _pubkey.type (); } bool isPrivate () const override { return _has_privateKeyRole; } bool canExport () const override { return !_has_privateKeyRole; } void convertToPublic () override { QCA_logTextMessage ( "softstorePKeyBase::convertToPublic - entry", Logger::Debug ); if (_has_privateKeyRole) { _has_privateKeyRole = false; } QCA_logTextMessage ( "softstorePKeyBase::convertToPublic - return", Logger::Debug ); } int bits () const override { return _pubkey.bitSize (); } int maximumEncryptSize ( EncryptionAlgorithm alg ) const override { return _pubkey.maximumEncryptSize (alg); } SecureArray encrypt ( const SecureArray &in, EncryptionAlgorithm alg ) override { return _pubkey.encrypt (in, alg); } bool decrypt ( const SecureArray &in, SecureArray *out, EncryptionAlgorithm alg ) override { if (_ensureAccess ()) { return _privkey.decrypt (in, out, alg); } else { return false; } } void startSign ( SignatureAlgorithm alg, SignatureFormat format ) override { if (_ensureAccess ()) { /* * We must use one object thought * signing, so it won't expire by * it-self or during passphrase. */ _privkeySign = _privkey; _privkeySign.startSign (alg, format); } } void startVerify ( SignatureAlgorithm alg, SignatureFormat sf ) override { _pubkey.startVerify (alg, sf); } void update ( const MemoryRegion &in ) override { if (_has_privateKeyRole) { _privkeySign.update (in); } else { _pubkey.update (in); } } QByteArray endSign () override { QByteArray r = _privkeySign.signature (); _privkeySign = PrivateKey (); return r; } virtual bool validSignature ( const QByteArray &sig ) { return _pubkey.validSignature (sig); } virtual void createPrivate ( int bits, int exp, bool block ) { Q_UNUSED(bits); Q_UNUSED(exp); Q_UNUSED(block); } virtual void createPrivate ( const BigInteger &n, const BigInteger &e, const BigInteger &p, const BigInteger &q, const BigInteger &d ) { Q_UNUSED(n); Q_UNUSED(e); Q_UNUSED(p); Q_UNUSED(q); Q_UNUSED(d); } virtual void createPublic ( const BigInteger &n, const BigInteger &e ) { Q_UNUSED(n); Q_UNUSED(e); } public: PublicKey _publicKey () const { return _pubkey; } bool _ensureAccess () { bool ret = false; QCA_logTextMessage ( "softstorePKeyBase::_ensureAccess - entry", Logger::Debug ); if (_entry.unlockTimeout != -1) { if (dueTime >= QDateTime::currentDateTime ()) { QCA_logTextMessage ( "softstorePKeyBase::_ensureAccess - dueTime reached, clearing", Logger::Debug ); _privkey = PrivateKey (); } } if (!_privkey.isNull ()) { ret = true; } else { KeyStoreEntry entry; KeyStoreEntryContext *context = NULL; QString storeId, storeName; ConvertResult cresult; QCA_logTextMessage ( "softstorePKeyBase::_ensureAccess - no current key, creating", Logger::Debug ); // too lazy to create scope context = reinterpret_cast (s_keyStoreList)->entryPassive (_serialized); if (context != NULL) { storeId = context->storeId (); storeName = context->storeName (); entry.change (context); } while (!ret) { SecureArray passphrase; switch (_entry.keyReferenceType) { case keyTypeInvalid: case keyTypePKCS8Inline: break; case keyTypePKCS12: case keyTypePKCS8FilePEM: case keyTypePKCS8FileDER: { QFile file (_entry.keyReference); while (!file.open (QIODevice::ReadOnly)) { TokenAsker asker; asker.ask ( KeyStoreInfo (KeyStore::SmartCard, storeId, storeName), entry, context ); asker.waitForResponse (); if (!asker.accepted ()) { goto cleanup1; } } } break; } if (!_entry.noPassphrase) { PasswordAsker asker; asker.ask ( Event::StylePassphrase, KeyStoreInfo (KeyStore::User, storeId, storeName), entry, context ); asker.waitForResponse (); passphrase = asker.password (); if (!asker.accepted ()) { goto cleanup1; } } switch (_entry.keyReferenceType) { case keyTypeInvalid: break; case keyTypePKCS12: { KeyBundle bundle = KeyBundle::fromFile ( _entry.keyReference, passphrase, &cresult ); if (cresult == ConvertGood) { _privkey = bundle.privateKey (); ret = true; } } break; case keyTypePKCS8Inline: { PrivateKey k = PrivateKey::fromDER ( Base64 ().stringToArray (_entry.keyReference), passphrase, &cresult ); if (cresult == ConvertGood) { _privkey = k; ret = true; } } break; case keyTypePKCS8FilePEM: { PrivateKey k = PrivateKey::fromPEMFile ( _entry.keyReference, passphrase, &cresult ); if (cresult == ConvertGood) { _privkey = k; ret = true; } } break; case keyTypePKCS8FileDER: { QFile file (_entry.keyReference); if (file.open (QIODevice::ReadOnly)) { QByteArray contents = file.readAll (); PrivateKey k = PrivateKey::fromDER ( contents, passphrase, &cresult ); if (cresult == ConvertGood) { _privkey = k; ret = true; } } } break; } } if (_entry.unlockTimeout != -1) { dueTime = QDateTime::currentDateTime ().addSecs (_entry.unlockTimeout); } cleanup1: ; } QCA_logTextMessage ( QString::asprintf ( "softstorePKeyBase::_ensureAccess - return ret=%d", ret ? 1 : 0 ), Logger::Debug ); return ret; } }; class softstorePKeyContext : public PKeyContext { private: PKeyBase *_k; public: softstorePKeyContext (Provider *p) : PKeyContext (p) { _k = NULL; } ~softstorePKeyContext () { delete _k; _k = NULL; } Provider::Context * clone () const override { softstorePKeyContext *c = new softstorePKeyContext (*this); c->_k = (PKeyBase *)_k->clone(); return c; } public: QList supportedTypes () const override { QList list; list += static_cast(_k)->_publicKey ().type (); return list; } QList supportedIOTypes () const override { QList list; list += static_cast(_k)->_publicKey ().type (); return list; } QList supportedPBEAlgorithms () const override { QList list; return list; } PKeyBase * key () override { return _k; } const PKeyBase * key () const override { return _k; } void setKey (PKeyBase *key) override { delete _k; _k = key; } bool importKey ( const PKeyBase *key ) override { Q_UNUSED(key); return false; } static int passphrase_cb ( char *buf, int size, int rwflag, void *u ) { Q_UNUSED(buf); Q_UNUSED(size); Q_UNUSED(rwflag); Q_UNUSED(u); return 0; } QByteArray publicToDER () const override { return static_cast(_k)->_publicKey ().toDER (); } QString publicToPEM () const override { return static_cast(_k)->_publicKey ().toPEM (); } ConvertResult publicFromDER ( const QByteArray &in ) override { Q_UNUSED(in); return ErrorDecode; } ConvertResult publicFromPEM ( const QString &s ) override { Q_UNUSED(s); return ErrorDecode; } SecureArray privateToDER( const SecureArray &passphrase, PBEAlgorithm pbe ) const override { Q_UNUSED(passphrase); Q_UNUSED(pbe); return SecureArray (); } QString privateToPEM ( const SecureArray &passphrase, PBEAlgorithm pbe ) const override { Q_UNUSED(passphrase); Q_UNUSED(pbe); return QString (); } ConvertResult privateFromDER ( const SecureArray &in, const SecureArray &passphrase ) override { Q_UNUSED(in); Q_UNUSED(passphrase); return ErrorDecode; } ConvertResult privateFromPEM ( const QString &s, const SecureArray &passphrase ) override { Q_UNUSED(s); Q_UNUSED(passphrase); return ErrorDecode; } }; class softstoreKeyStoreEntryContext : public KeyStoreEntryContext { private: KeyStoreEntry::Type _item_type; KeyBundle _key; SoftStoreEntry _entry; QString _serialized; public: softstoreKeyStoreEntryContext ( const KeyBundle &key, const SoftStoreEntry &entry, const QString &serialized, Provider *p ) : KeyStoreEntryContext(p) { _item_type = KeyStoreEntry::TypeKeyBundle; _key = key; _entry = entry; _serialized = serialized; } softstoreKeyStoreEntryContext ( const softstoreKeyStoreEntryContext &from ) : KeyStoreEntryContext(from) { _item_type = from._item_type; _key = from._key; _entry = from._entry; _serialized = from._serialized; } Provider::Context * clone () const override { return new softstoreKeyStoreEntryContext (*this); } public: KeyStoreEntry::Type type () const override { return KeyStoreEntry::TypeKeyBundle; } QString name () const override { return _entry.name; } QString id () const override { return _entry.name; } KeyBundle keyBundle () const override { return _key; } Certificate certificate () const override { return _entry.chain.primary (); } QString storeId () const override { return QString::asprintf ("%s/%s", "qca-softstore", myPrintable (_entry.name)); } QString storeName () const override { return _entry.name; } bool ensureAccess () override { return static_cast(static_cast(_key.privateKey ().context ())->key ())->_ensureAccess (); } QString serialize () const override { return _serialized; } }; class softstoreKeyStoreListContext : public KeyStoreListContext { Q_OBJECT private: int _last_id; QList _entries; public: softstoreKeyStoreListContext (Provider *p) : KeyStoreListContext (p) { QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::softstoreKeyStoreListContext - entry Provider=%p", (void *)p ), Logger::Debug ); _last_id = 0; QCA_logTextMessage ( "softstoreKeyStoreListContext::softstoreKeyStoreListContext - return", Logger::Debug ); } ~softstoreKeyStoreListContext () { QCA_logTextMessage ( "softstoreKeyStoreListContext::~softstoreKeyStoreListContext - entry", Logger::Debug ); s_keyStoreList = NULL; QCA_logTextMessage ( "softstoreKeyStoreListContext::~softstoreKeyStoreListContext - return", Logger::Debug ); } Provider::Context * clone () const override { QCA_logTextMessage ( "softstoreKeyStoreListContext::clone - entry/return", Logger::Debug ); return NULL; } public: void start () override { QCA_logTextMessage ( "softstoreKeyStoreListContext::start - entry", Logger::Debug ); QMetaObject::invokeMethod(this, "doReady", Qt::QueuedConnection); QCA_logTextMessage ( "softstoreKeyStoreListContext::start - return", Logger::Debug ); } void setUpdatesEnabled (bool enabled) override { QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::setUpdatesEnabled - entry/return enabled=%d", enabled ? 1 : 0 ), Logger::Debug ); } KeyStoreEntryContext * entry ( int id, const QString &entryId ) override { QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::entry - entry/return id=%d entryId='%s'", id, myPrintable (entryId) ), Logger::Debug ); Q_UNUSED(id); Q_UNUSED(entryId); return NULL; } KeyStoreEntryContext * entryPassive ( const QString &serialized ) override { KeyStoreEntryContext *entry = NULL; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::entryPassive - entry serialized='%s'", myPrintable (serialized) ), Logger::Debug ); if (serialized.startsWith ("qca-softstore/")) { SoftStoreEntry sentry; if (_deserializeSoftStoreEntry (serialized, sentry)) { entry = _keyStoreEntryBySoftStoreEntry (sentry); } } QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::entryPassive - return entry=%p", (void *)entry ), Logger::Debug ); return entry; } KeyStore::Type type (int id) const override { Q_UNUSED(id); QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::type - entry/return id=%d", id ), Logger::Debug ); return KeyStore::User; } QString storeId (int id) const override { QString ret; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::storeId - entry id=%d", id ), Logger::Debug ); ret = "qca-softstore"; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::storeId - return ret=%s", myPrintable (ret) ), Logger::Debug ); return ret; } QString name (int id) const override { QString ret; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::name - entry id=%d", id ), Logger::Debug ); ret = "User Software Store"; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::name - return ret=%s", myPrintable (ret) ), Logger::Debug ); return ret; } QList entryTypes (int id) const override { Q_UNUSED(id); QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::entryTypes - entry/return id=%d", id ), Logger::Debug ); QList list; list += KeyStoreEntry::TypeKeyBundle; list += KeyStoreEntry::TypeCertificate; return list; } QList keyStores () override { QList list; QCA_logTextMessage ( "softstoreKeyStoreListContext::keyStores - entry", Logger::Debug ); list += _last_id; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::keyStores - return out.size()=%d", list.size () ), Logger::Debug ); return list; } QList entryList (int id) override { QList list; QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::entryList - entry id=%d", id ), Logger::Debug ); foreach (const SoftStoreEntry &e, _entries) { list += _keyStoreEntryBySoftStoreEntry (e); } QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::entryList - return out.size()=%d", list.size () ), Logger::Debug ); return list; } void _emit_diagnosticText ( const QString &t ) { QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::_emit_diagnosticText - entry t='%s'", myPrintable (t) ), Logger::Debug ); QCA_logTextMessage (t, Logger::Warning); emit diagnosticText (t); QCA_logTextMessage ( "softstoreKeyStoreListContext::_emit_diagnosticText - return", Logger::Debug ); } private Q_SLOTS: void doReady () { QCA_logTextMessage ( "softstoreKeyStoreListContext::doReady - entry", Logger::Debug ); emit busyEnd (); QCA_logTextMessage ( "softstoreKeyStoreListContext::doReady - return", Logger::Debug ); } void doUpdated () { QCA_logTextMessage ( "softstoreKeyStoreListContext::doUpdated - entry", Logger::Debug ); emit updated (); QCA_logTextMessage ( "softstoreKeyStoreListContext::doUpdated - return", Logger::Debug ); } public: void _updateConfig (const QVariantMap &config, const int maxEntries) { QCA_logTextMessage ( "softstoreKeyStoreListContext::_updateConfig - entry", Logger::Debug ); QMap keyTypeMap; keyTypeMap["pkcs12"] = keyTypePKCS12; keyTypeMap["pkcs8"] = keyTypePKCS8Inline; keyTypeMap["pkcs8-file-pem"] = keyTypePKCS8FilePEM; keyTypeMap["pkcs8-file-der"] = keyTypePKCS8FileDER; QMap publicTypeMap; publicTypeMap["x509chain"] = publicTypeX509Chain; _last_id++; _entries.clear (); for (int i=0;isetKey (pkey); PrivateKey privkey; privkey.change (pkc); KeyBundle key; key.setCertificateChainAndKey ( sentry.chain, privkey ); entry = new softstoreKeyStoreEntryContext ( key, sentry, serialized, provider () ); QCA_logTextMessage ( QString::asprintf ( "softstoreKeyStoreListContext::_keyStoreEntryBySoftStoreEntry - return entry=%p", (void *)entry ), Logger::Debug ); return entry; } QString _escapeString ( const QString &from ) const { QString to; foreach (const QChar &c, from) { if (c == '/' || c == '\\') { to += QString::asprintf ("\\x%04x", c.unicode ()); } else { to += c; } } return to; } QString _unescapeString ( const QString &from ) const { QString to; for (int i=0;i_updateConfig (_config, _CONFIG_MAX_ENTRIES); } context = s_keyStoreList; } QCA_logTextMessage ( QString::asprintf ( "softstoreProvider::createContext - return context=%p", (void *)context ), Logger::Debug ); return context; } QVariantMap defaultConfig () const override { QVariantMap mytemplate; QCA_logTextMessage ( "softstoreProvider::defaultConfig - entry/return", Logger::Debug ); mytemplate["formtype"] = "http://affinix.com/qca/forms/qca-softstore#1.0"; for (int i=0;i<_CONFIG_MAX_ENTRIES;i++) { mytemplate[QString::asprintf ("entry_%02d_enabled", i)] = false; mytemplate[QString::asprintf ("entry_%02d_name", i)] = ""; mytemplate[QString::asprintf ("entry_%02d_public_type", i)] = ""; mytemplate[QString::asprintf ("entry_%02d_private_type", i)] = ""; mytemplate[QString::asprintf ("entry_%02d_public", i)] = ""; mytemplate[QString::asprintf ("entry_%02d_private", i)] = ""; mytemplate[QString::asprintf ("entry_%02d_unlock_timeout", i)] = -1; mytemplate[QString::asprintf ("entry_%02d_no_passphrase", i)] = false; } return mytemplate; } void configChanged (const QVariantMap &config) override { QCA_logTextMessage ( "softstoreProvider::configChanged - entry", Logger::Debug ); _config = config; if (s_keyStoreList != NULL) { s_keyStoreList->_updateConfig (_config, _CONFIG_MAX_ENTRIES); } QCA_logTextMessage ( "softstoreProvider::configChanged - return", Logger::Debug ); } }; const int softstoreProvider::_CONFIG_MAX_ENTRIES = 50; class softstorePlugin : public QObject, public QCAPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0") Q_INTERFACES(QCAPlugin) public: Provider *createProvider() override { return new softstoreProvider; } }; #include "qca-softstore.moc" diff --git a/src/qca_default.cpp b/src/qca_default.cpp index 7488c7ed..8a484e3a 100644 --- a/src/qca_default.cpp +++ b/src/qca_default.cpp @@ -1,1325 +1,1325 @@ /* * Copyright (C) 2003-2007 Justin Karneges * Copyright (C) 2004,2005 Brad Hards * * 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 #include "qca_textfilter.h" #include "qca_cert.h" #include "qcaprovider.h" #ifndef QCA_NO_SYSTEMSTORE # include "qca_systemstore.h" #endif #define FRIENDLY_NAMES namespace QCA { class DefaultShared { private: mutable QMutex m; bool _use_system; QString _roots_file; QStringList _skip_plugins; QStringList _plugin_priorities; public: DefaultShared() : _use_system(true) { } bool use_system() const { QMutexLocker locker(&m); return _use_system; } QString roots_file() const { QMutexLocker locker(&m); return _roots_file; } QStringList skip_plugins() const { QMutexLocker locker(&m); return _skip_plugins; } QStringList plugin_priorities() const { QMutexLocker locker(&m); return _plugin_priorities; } void set(bool use_system, const QString &roots_file, const QStringList &skip_plugins, const QStringList &plugin_priorities) { QMutexLocker locker(&m); _use_system = use_system; _roots_file = roots_file; _skip_plugins = skip_plugins; _plugin_priorities = plugin_priorities; } }; //---------------------------------------------------------------------------- // DefaultRandomContext //---------------------------------------------------------------------------- class DefaultRandomContext : public RandomContext { public: DefaultRandomContext(Provider *p) : RandomContext(p) {} Provider::Context *clone() const override { return new DefaultRandomContext(provider()); } SecureArray nextBytes(int size) override { SecureArray buf(size); for(int n = 0; n < (int)buf.size(); ++n) buf[n] = (char)qrand(); return buf; } }; //---------------------------------------------------------------------------- // DefaultMD5Context //---------------------------------------------------------------------------- /* NOTE: the following code was modified to not need BYTE_ORDER -- Justin */ /* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id$ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef quint8 md5_byte_t; /* 8-bit byte */ typedef quint32 md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ struct md5_state_t { md5_word_t count[2]; // 2 /* message length in bits, lsw first */ md5_word_t abcd[4]; // 4 /* digest buffer */ md5_byte_t buf[64]; // 64 /* accumulate block */ md5_state_t() { memset(count, 0, 2 * sizeof(md5_word_t)); memset(abcd, 0, 4 * sizeof(md5_word_t)); memset(buf, 0, 64 * sizeof(md5_byte_t)); } md5_state_t(const md5_state_t &from) { *this = from; } md5_state_t & operator=(const md5_state_t &from) { *this = from; return *this; } }; /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; { if(QSysInfo::ByteOrder == QSysInfo::BigEndian) { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; X = xbuf; /* (dynamic only) */ for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } else /* dynamic big-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. On arm do copying always */ #ifndef Q_PROCESSOR_ARM if (!((data - static_cast(0)) & 3)) { /* data are properly aligned */ X = reinterpret_cast(data); } else #endif { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } class DefaultMD5Context : public HashContext { public: DefaultMD5Context(Provider *p) : HashContext(p, "md5") { clear(); } Provider::Context *clone() const override { return new DefaultMD5Context(*this); } void clear() override { secure = true; md5_init(&md5); } void update(const MemoryRegion &in) override { if(!in.isSecure()) secure = false; md5_append(&md5, (const md5_byte_t *)in.data(), in.size()); } MemoryRegion final() override { if(secure) { SecureArray b(16, 0); md5_finish(&md5, (md5_byte_t *)b.data()); return b; } else { QByteArray b(16, 0); md5_finish(&md5, (md5_byte_t *)b.data()); return b; } } bool secure; md5_state_t md5; }; //---------------------------------------------------------------------------- // DefaultSHA1Context //---------------------------------------------------------------------------- // SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com) #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #ifdef Q_PROCESSOR_ARM #define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15]^block.l[(i+2)&15]^block.l[i&15],1)) #else #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1)) #endif /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); struct SHA1_CONTEXT { quint32 state[5]; // 5 quint32 count[2]; // 2 unsigned char buffer[64]; // 64 SHA1_CONTEXT() { memset(state, 0, 5 * sizeof(quint32)); memset(count, 0, 2 * sizeof(quint32)); memset(buffer, 0, 64 * sizeof(unsigned char)); } SHA1_CONTEXT(const SHA1_CONTEXT &from) { *this = from; } SHA1_CONTEXT & operator=(const SHA1_CONTEXT &from) { *this = from; return *this; } }; typedef union { unsigned char c[64]; quint32 l[16]; } CHAR64LONG16; class DefaultSHA1Context : public HashContext { public: SHA1_CONTEXT _context; #ifdef Q_PROCESSOR_ARM CHAR64LONG16 block; #else CHAR64LONG16 *block; #endif bool secure; DefaultSHA1Context(Provider *p) : HashContext(p, "sha1") { clear(); } Provider::Context *clone() const override { return new DefaultSHA1Context(*this); } void clear() override { secure = true; sha1_init(&_context); } void update(const MemoryRegion &in) override { if(!in.isSecure()) secure = false; sha1_update(&_context, (unsigned char *)in.data(), (unsigned int)in.size()); } MemoryRegion final() override { if(secure) { SecureArray b(20, 0); sha1_final((unsigned char *)b.data(), &_context); return b; } else { QByteArray b(20, 0); sha1_final((unsigned char *)b.data(), &_context); return b; } } inline unsigned long blk0(quint32 i) { if(QSysInfo::ByteOrder == QSysInfo::BigEndian) #ifdef Q_PROCESSOR_ARM return block.l[i]; #else return block->l[i]; #endif else #ifdef Q_PROCESSOR_ARM return (block.l[i] = (rol(block.l[i],24)&0xFF00FF00) | (rol(block.l[i],8)&0x00FF00FF)); #else return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF)); #endif } // Hash a single 512-bit block. This is the core of the algorithm. void transform(quint32 state[5], unsigned char buffer[64]) { quint32 a, b, c, d, e; #ifdef Q_PROCESSOR_ARM memcpy(&block, buffer, sizeof(block)); #else block = reinterpret_cast(buffer); #endif // Copy context->state[] to working vars a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; // 4 rounds of 20 operations each. Loop unrolled. R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); // Add the working vars back into context.state[] state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; // Wipe variables a = b = c = d = e = 0; } // SHA1Init - Initialize new context void sha1_init(SHA1_CONTEXT* context) { // SHA1 initialization constants context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } // Run your data through this void sha1_update(SHA1_CONTEXT* context, unsigned char* data, quint32 len) { quint32 i, j; j = (context->count[0] >> 3) & 63; if((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } // Add padding and return the message digest void sha1_final(unsigned char digest[20], SHA1_CONTEXT* context) { quint32 i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); // Endian independent } sha1_update(context, (unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) { sha1_update(context, (unsigned char *)"\0", 1); } sha1_update(context, finalcount, 8); // Should cause a transform() for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } // Wipe variables i = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(&finalcount, 0, 8); } }; //---------------------------------------------------------------------------- // DefaultKeyStoreEntry //---------------------------------------------------------------------------- // this escapes colons, commas, and newlines. colons and commas so that they // are available as delimiters, and newlines so that our output can be a // single line of text. static QString escape_string(const QString &in) { QString out; for(int n = 0; n < in.length(); ++n) { if(in[n] == '\\') out += "\\\\"; else if(in[n] == ':') out += "\\c"; else if(in[n] == ',') out += "\\o"; else if(in[n] == '\n') out += "\\n"; else out += in[n]; } return out; } static bool unescape_string(const QString &in, QString *_out) { QString out; for(int n = 0; n < in.length(); ++n) { if(in[n] == '\\') { if(n + 1 >= in.length()) return false; if(in[n + 1] == '\\') out += '\\'; else if(in[n + 1] == 'c') out += ':'; else if(in[n + 1] == 'o') out += ','; else if(in[n + 1] == 'n') out += '\n'; else return false; ++n; } else out += in[n]; } *_out = out; return true; } static QString escape_stringlist(const QStringList &in) { QStringList list; for(int n = 0; n < in.count(); ++n) list += escape_string(in[n]); return list.join(":"); } static bool unescape_stringlist(const QString &in, QStringList *_out) { QStringList out; QStringList list = in.split(':'); for(int n = 0; n < list.count(); ++n) { QString str; if(!unescape_string(list[n], &str)) return false; out += str; } *_out = out; return true; } // serialization format is a colon separated list of 7 escaped strings // 0 - "qca_def_1" (header) // 1 - store id // 2 - store name // 3 - entry id // 4 - entry name // 5 - entry type (e.g. "cert") // 6 - string encoding of object (e.g. DER encoded in Base64) static QString entry_serialize(const QString &storeId, const QString &storeName, const QString &entryId, const QString &entryName, const QString &entryType, const QString &data) { QStringList out; out += "qca_def"; out += storeId; out += storeName; out += entryId; out += entryName; out += entryType; out += data; return escape_stringlist(out); } static bool entry_deserialize(const QString &in, QString *storeId, QString *storeName, QString *entryId, QString *entryName, QString *entryType, QString *data) { QStringList list; if(!unescape_stringlist(in, &list)) return false; if(list.count() != 7) return false; if(list[0] != "qca_def") return false; *storeId = list[1]; *storeName = list[2]; *entryId = list[3]; *entryName = list[4]; *entryType = list[5]; *data = list[6]; return true; } class DefaultKeyStoreEntry : public KeyStoreEntryContext { public: KeyStoreEntry::Type _type; QString _id, _name, _storeId, _storeName; Certificate _cert; CRL _crl; mutable QString _serialized; DefaultKeyStoreEntry(const Certificate &cert, const QString &storeId, const QString &storeName, Provider *p) : KeyStoreEntryContext(p) { _type = KeyStoreEntry::TypeCertificate; _storeId = storeId; _storeName = storeName; _cert = cert; } DefaultKeyStoreEntry(const CRL &crl, const QString &storeId, const QString &storeName, Provider *p) : KeyStoreEntryContext(p) { _type = KeyStoreEntry::TypeCRL; _storeId = storeId; _storeName = storeName; _crl = crl; } Provider::Context *clone() const override { return new DefaultKeyStoreEntry(*this); } KeyStoreEntry::Type type() const override { return _type; } QString id() const override { return _id; } QString name() const override { return _name; } QString storeId() const override { return _storeId; } QString storeName() const override { return _storeName; } Certificate certificate() const override { return _cert; } CRL crl() const override { return _crl; } QString serialize() const override { if(_serialized.isEmpty()) { QString typestr; QString datastr; if(_type == KeyStoreEntry::TypeCertificate) { typestr = "cert"; datastr = Base64().arrayToString(_cert.toDER()); } else { typestr = "crl"; datastr = Base64().arrayToString(_crl.toDER()); } _serialized = entry_serialize(_storeId, _storeName, _id, _name, typestr, datastr); } return _serialized; } static DefaultKeyStoreEntry *deserialize(const QString &in, Provider *provider) { QString storeId, storeName, id, name, typestr, datastr; if(entry_deserialize(in, &storeId, &storeName, &id, &name, &typestr, &datastr)) { QByteArray data = Base64().stringToArray(datastr).toByteArray(); DefaultKeyStoreEntry *c; if(typestr == "cert") { Certificate cert = Certificate::fromDER(data); if(cert.isNull()) return 0; c = new DefaultKeyStoreEntry(cert, storeId, storeName, provider); } else if(typestr == "crl") { CRL crl = CRL::fromDER(data); if(crl.isNull()) return 0; c = new DefaultKeyStoreEntry(crl, storeId, storeName, provider); } else return 0; c->_id = id; c->_name = name; c->_serialized = in; return c; } return 0; } QString simpleId() const { if(_type == KeyStoreEntry::TypeCertificate) return QString::number(qHash(_cert.toDER())); else return QString::number(qHash(_crl.toDER())); } QString simpleName() const { // use the common name, else orgname if(_type == KeyStoreEntry::TypeCertificate) { QString str = _cert.commonName(); if(str.isEmpty()) str = _cert.subjectInfo().value(Organization); return str; } else return _crl.issuerInfo().value(CommonName); } }; //---------------------------------------------------------------------------- // DefaultKeyStoreList //---------------------------------------------------------------------------- class DefaultKeyStoreList : public KeyStoreListContext { Q_OBJECT public: bool x509_supported; DefaultShared *shared; DefaultKeyStoreList(Provider *p, DefaultShared *_shared) : KeyStoreListContext(p), shared(_shared) { } ~DefaultKeyStoreList() { } Provider::Context *clone() const override { return 0; } void start() override { x509_supported = false; QMetaObject::invokeMethod(this, "busyEnd", Qt::QueuedConnection); } QList keyStores() override { if(!x509_supported) { if(isSupported("cert") && isSupported("crl")) x509_supported = true; } bool have_systemstore = false; #ifndef QCA_NO_SYSTEMSTORE if(shared->use_system()) have_systemstore = qca_have_systemstore(); #endif QList list; // system store only shows up if the OS store is available or // there is a configured store file if(x509_supported && (have_systemstore || !shared->roots_file().isEmpty())) list += 0; return list; } KeyStore::Type type(int) const override { return KeyStore::System; } QString storeId(int) const override { return "qca-default-systemstore"; } QString name(int) const override { return "System Trusted Certificates"; } QList entryTypes(int) const override { QList list; list += KeyStoreEntry::TypeCertificate; list += KeyStoreEntry::TypeCRL; return list; } QList entryList(int) override { QList out; QList certs; QList crls; if(shared->use_system()) { CertificateCollection col; #ifndef QCA_NO_SYSTEMSTORE col = qca_get_systemstore(QString()); #endif certs += col.certificates(); crls += col.crls(); } QString roots = shared->roots_file(); if(!roots.isEmpty()) { CertificateCollection col = CertificateCollection::fromFlatTextFile(roots); certs += col.certificates(); crls += col.crls(); } #ifdef FRIENDLY_NAMES QStringList names = makeFriendlyNames(certs); #endif for(int n = 0; n < certs.count(); ++n) { DefaultKeyStoreEntry *c = new DefaultKeyStoreEntry(certs[n], storeId(0), name(0), provider()); c->_id = c->simpleId(); #ifdef FRIENDLY_NAMES c->_name = names[n]; #else c->_name = c->simpleName(); #endif out.append(c); } for(int n = 0; n < crls.count(); ++n) { DefaultKeyStoreEntry *c = new DefaultKeyStoreEntry(crls[n], storeId(0), name(0), provider()); c->_id = c->simpleId(); c->_name = c->simpleName(); out.append(c); } return out; } KeyStoreEntryContext *entryPassive(const QString &serialized) override { return DefaultKeyStoreEntry::deserialize(serialized, provider()); } }; //---------------------------------------------------------------------------- // DefaultProvider //---------------------------------------------------------------------------- static bool unescape_config_stringlist(const QString &in, QStringList *_out) { QStringList out; QStringList list = in.split(','); for(int n = 0; n < list.count(); ++n) { QString str; if(!unescape_string(list[n], &str)) return false; out += str.trimmed(); } *_out = out; return true; } class DefaultProvider : public Provider { public: DefaultShared shared; void init() override { QDateTime now = QDateTime::currentDateTime(); uint t = now.toTime_t(); if(now.time().msec() > 0) t /= now.time().msec(); qsrand(t); } int version() const override { return QCA_VERSION; } int qcaVersion() const override { return QCA_VERSION; } QString name() const override { return "default"; } QStringList features() const override { QStringList list; list += "random"; list += "md5"; list += "sha1"; list += "keystorelist"; return list; } Provider::Context *createContext(const QString &type) override { if(type == "random") return new DefaultRandomContext(this); else if(type == "md5") return new DefaultMD5Context(this); else if(type == "sha1") return new DefaultSHA1Context(this); else if(type == "keystorelist") return new DefaultKeyStoreList(this, &shared); else return 0; } QVariantMap defaultConfig() const override { QVariantMap config; config["formtype"] = "http://affinix.com/qca/forms/default#1.0"; config["use_system"] = true; config["roots_file"] = QString(); config["skip_plugins"] = QString(); config["plugin_priorities"] = QString(); return config; } void configChanged(const QVariantMap &config) override { bool use_system = config["use_system"].toBool(); QString roots_file = config["roots_file"].toString(); QString skip_plugins_str = config["skip_plugins"].toString(); QString plugin_priorities_str = config["plugin_priorities"].toString(); QStringList tmp; QStringList skip_plugins; if(unescape_config_stringlist(skip_plugins_str, &tmp)) skip_plugins = tmp; QStringList plugin_priorities; if(unescape_config_stringlist(plugin_priorities_str, &tmp)) plugin_priorities = tmp; for(int n = 0; n < plugin_priorities.count(); ++n) { QString &s = plugin_priorities[n]; // make sure the entry ends with ":number" int x = s.indexOf(':'); bool ok = false; if(x != -1) - s.mid(x + 1).toInt(&ok); + s.midRef(x + 1).toInt(&ok); if(!ok) { plugin_priorities.removeAt(n); --n; } } shared.set(use_system, roots_file, skip_plugins, plugin_priorities); } }; Provider *create_default_provider() { return new DefaultProvider; } QStringList skip_plugins(Provider *defaultProvider) { DefaultProvider *that = (DefaultProvider *)defaultProvider; return that->shared.skip_plugins(); } QStringList plugin_priorities(Provider *defaultProvider) { DefaultProvider *that = (DefaultProvider *)defaultProvider; return that->shared.plugin_priorities(); } #include "qca_default.moc" } diff --git a/src/qca_plugin.cpp b/src/qca_plugin.cpp index 5ad08790..696c4f67 100644 --- a/src/qca_plugin.cpp +++ b/src/qca_plugin.cpp @@ -1,810 +1,810 @@ /* * Copyright (C) 2004-2008 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ // Note: The basic thread-safety approach with the plugin manager is that // it is safe to add/get providers, however it is unsafe to remove them. // The expectation is that providers will almost always be unloaded on // application shutdown. For safe provider unload, ensure no threads are // using the manager, the provider in question, nor any sub-objects from // the provider. #include "qca_plugin.h" #include "qcaprovider.h" #include #include #include #include #include #define PLUGIN_SUBDIR "crypto" namespace QCA { // from qca_core.cpp QVariantMap getProviderConfig_internal(Provider *p); // from qca_default.cpp QStringList skip_plugins(Provider *defaultProvider); QStringList plugin_priorities(Provider *defaultProvider); // stupidly simple log truncation function. if the log exceeds size chars, // then throw out the top half, to nearest line. QString truncate_log(const QString &in, int size) { if(size < 2 || in.length() < size) return in; // start by pointing at the last chars int at = in.length() - (size / 2); // if the previous char is a newline, then this is a perfect cut. // otherwise, we need to skip to after the next newline. if(in[at - 1] != '\n') { while(at < in.length() && in[at] != '\n') { ++at; } // at this point we either reached a newline, or end of // the entire buffer if(in[at] == '\n') ++at; } return in.mid(at); } static ProviderManager *g_pluginman = 0; static void logDebug(const QString &str) { if(g_pluginman) g_pluginman->appendDiagnosticText(str + '\n'); } static bool validVersion(int ver) { // major version must be equal, minor version must be equal or lesser if((ver & 0xff0000) == (QCA_VERSION & 0xff0000) && (ver & 0xff00) <= (QCA_VERSION & 0xff00)) return true; return false; } class PluginInstance { private: QPluginLoader *_loader; QObject *_instance; bool _ownInstance; PluginInstance() { } public: static PluginInstance *fromFile(const QString &fname, QString *errstr = 0) { QPluginLoader *loader = new QPluginLoader(fname); if(!loader->load()) { if(errstr) *errstr = QString("failed to load: %1").arg(loader->errorString()); delete loader; return 0; } QObject *obj = loader->instance(); if(!obj) { if(errstr) *errstr = "failed to get instance"; loader->unload(); delete loader; return 0; } PluginInstance *i = new PluginInstance; i->_loader = loader; i->_instance = obj; i->_ownInstance = true; return i; } static PluginInstance *fromStatic(QObject *obj) { PluginInstance *i = new PluginInstance; i->_loader = 0; i->_instance = obj; i->_ownInstance = false; return i; } static PluginInstance *fromInstance(QObject *obj) { PluginInstance *i = new PluginInstance; i->_loader = 0; i->_instance = obj; i->_ownInstance = true; return i; } ~PluginInstance() { if(_ownInstance) delete _instance; if(_loader) { _loader->unload(); delete _loader; } } void claim() { if(_loader) _loader->moveToThread(0); if(_ownInstance) _instance->moveToThread(0); } QObject *instance() { return _instance; } }; class ProviderItem { public: QString fname; Provider *p; int priority; QMutex m; static ProviderItem *load(const QString &fname, QString *out_errstr = 0) { QString errstr; PluginInstance *i = PluginInstance::fromFile(fname, &errstr); if(!i) { if(out_errstr) *out_errstr = errstr; return 0; } QCAPlugin *plugin = qobject_cast(i->instance()); if(!plugin) { if(out_errstr) *out_errstr = "does not offer QCAPlugin interface"; delete i; return 0; } Provider *p = plugin->createProvider(); if(!p) { if(out_errstr) *out_errstr = "unable to create provider"; delete i; return 0; } ProviderItem *pi = new ProviderItem(i, p); pi->fname = fname; return pi; } static ProviderItem *loadStatic(QObject *instance, QString *errstr = 0) { PluginInstance *i = PluginInstance::fromStatic(instance); QCAPlugin *plugin = qobject_cast(i->instance()); if(!plugin) { if(errstr) *errstr = "does not offer QCAPlugin interface"; delete i; return 0; } Provider *p = plugin->createProvider(); if(!p) { if(errstr) *errstr = "unable to create provider"; delete i; return 0; } ProviderItem *pi = new ProviderItem(i, p); return pi; } static ProviderItem *fromClass(Provider *p) { ProviderItem *pi = new ProviderItem(0, p); return pi; } ~ProviderItem() { delete p; delete instance; } void ensureInit() { QMutexLocker locker(&m); if(init_done) return; init_done = true; p->init(); // load config QVariantMap conf = getProviderConfig_internal(p); if(!conf.isEmpty()) p->configChanged(conf); } bool initted() const { return init_done; } // null if not a plugin QObject *objectInstance() const { if(instance) return instance->instance(); else return 0; } private: PluginInstance *instance; bool init_done; ProviderItem(PluginInstance *_instance, Provider *_p) { instance = _instance; p = _p; init_done = false; // disassociate from threads if(instance) instance->claim(); } }; ProviderManager::ProviderManager() { g_pluginman = this; def = 0; scanned_static = false; } ProviderManager::~ProviderManager() { if(def) def->deinit(); unloadAll(); delete def; g_pluginman = 0; } void ProviderManager::scan() { QMutexLocker locker(&providerMutex); // check static first, but only once if(!scanned_static) { logDebug("Checking Qt static plugins:"); QObjectList list = QPluginLoader::staticInstances(); if(list.isEmpty()) logDebug(" (none)"); for(int n = 0; n < list.count(); ++n) { QObject *instance = list[n]; QString className = QString::fromLatin1(instance->metaObject()->className()); QString errstr; ProviderItem *i = ProviderItem::loadStatic(instance, &errstr); if(!i) { logDebug(QString(" %1: %2").arg(className, errstr)); continue; } QString providerName = i->p->name(); if(haveAlready(providerName)) { logDebug(QString(" %1: (as %2) already loaded provider, skipping").arg(className, providerName)); delete i; continue; } int ver = i->p->qcaVersion(); if(!validVersion(ver)) { errstr = QString::asprintf("plugin version 0x%06x is in the future", ver); logDebug(QString(" %1: (as %2) %3").arg(className, providerName, errstr)); delete i; continue; } addItem(i, get_default_priority(providerName)); logDebug(QString(" %1: loaded as %2").arg(className, providerName)); } scanned_static = true; } #ifndef QCA_NO_PLUGINS if(qgetenv("QCA_NO_PLUGINS") == "1") return; const QStringList dirs = pluginPaths(); if(dirs.isEmpty()) logDebug("No Qt Library Paths"); for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { #ifdef DEVELOPER_MODE logDebug(QString("Checking QCA build tree Path: %1").arg(QDir::toNativeSeparators(*it))); #else logDebug(QString("Checking Qt Library Path: %1").arg(QDir::toNativeSeparators(*it))); #endif QDir libpath(*it); QDir dir(libpath.filePath(PLUGIN_SUBDIR)); if(!dir.exists()) { logDebug(" (No 'crypto' subdirectory)"); continue; } QStringList entryList = dir.entryList(QDir::Files); if(entryList.isEmpty()) { logDebug(" (No files in 'crypto' subdirectory)"); continue; } foreach(const QString &maybeFile, entryList) { QFileInfo fi(dir.filePath(maybeFile)); QString filePath = fi.filePath(); // file name with path QString fileName = fi.fileName(); // just file name if(!QLibrary::isLibrary(filePath)) { logDebug(QString(" %1: not a library, skipping").arg(fileName)); continue; } // make sure we haven't loaded this file before bool haveFile = false; for(int n = 0; n < providerItemList.count(); ++n) { ProviderItem *pi = providerItemList[n]; if(!pi->fname.isEmpty() && pi->fname == filePath) { haveFile = true; break; } } if(haveFile) { logDebug(QString(" %1: already loaded file, skipping").arg(fileName)); continue; } QString errstr; ProviderItem *i = ProviderItem::load(filePath, &errstr); if(!i) { logDebug(QString(" %1: %2").arg(fileName, errstr)); continue; } QString className = QString::fromLatin1(i->objectInstance()->metaObject()->className()); QString providerName = i->p->name(); if(haveAlready(providerName)) { logDebug(QString(" %1: (class: %2, as %3) already loaded provider, skipping").arg(fileName, className, providerName)); delete i; continue; } int ver = i->p->qcaVersion(); if(!validVersion(ver)) { errstr = QString::asprintf("plugin version 0x%06x is in the future", ver); logDebug(QString(" %1: (class: %2, as %3) %4").arg(fileName, className, providerName, errstr)); delete i; continue; } if(skip_plugins(def).contains(providerName)) { logDebug(QString(" %1: (class: %2, as %3) explicitly disabled, skipping").arg(fileName, className, providerName)); delete i; continue; } addItem(i, get_default_priority(providerName)); logDebug(QString(" %1: (class: %2) loaded as %3").arg(fileName, className, providerName)); } } #endif } bool ProviderManager::add(Provider *p, int priority) { QMutexLocker locker(&providerMutex); QString providerName = p->name(); if(haveAlready(providerName)) { logDebug(QString("Directly adding: %1: already loaded provider, skipping").arg(providerName)); return false; } int ver = p->qcaVersion(); if(!validVersion(ver)) { QString errstr = QString::asprintf("plugin version 0x%06x is in the future", ver); logDebug(QString("Directly adding: %1: %2").arg(providerName, errstr)); return false; } ProviderItem *i = ProviderItem::fromClass(p); addItem(i, priority); logDebug(QString("Directly adding: %1: loaded").arg(providerName)); return true; } bool ProviderManager::unload(const QString &name) { for(int n = 0; n < providerItemList.count(); ++n) { ProviderItem *i = providerItemList[n]; if(i->p && i->p->name() == name) { if(i->initted()) i->p->deinit(); delete i; providerItemList.removeAt(n); providerList.removeAt(n); logDebug(QString("Unloaded: %1").arg(name)); return true; } } return false; } void ProviderManager::unloadAll() { foreach(ProviderItem *i, providerItemList) { if(i->initted()) i->p->deinit(); } while(!providerItemList.isEmpty()) { ProviderItem *i = providerItemList.first(); QString name = i->p->name(); delete i; providerItemList.removeFirst(); providerList.removeFirst(); logDebug(QString("Unloaded: %1").arg(name)); } } void ProviderManager::setDefault(Provider *p) { QMutexLocker locker(&providerMutex); if(def) delete def; def = p; if(def) { def->init(); QVariantMap conf = getProviderConfig_internal(def); if(!conf.isEmpty()) def->configChanged(conf); } } Provider *ProviderManager::find(Provider *_p) const { ProviderItem *i = 0; Provider *p = 0; providerMutex.lock(); if(_p == def) { p = def; } else { for(int n = 0; n < providerItemList.count(); ++n) { ProviderItem *pi = providerItemList[n]; if(pi->p && pi->p == _p) { i = pi; p = pi->p; break; } } } providerMutex.unlock(); if(i) i->ensureInit(); return p; } Provider *ProviderManager::find(const QString &name) const { ProviderItem *i = 0; Provider *p = 0; providerMutex.lock(); if(def && name == def->name()) { p = def; } else { for(int n = 0; n < providerItemList.count(); ++n) { ProviderItem *pi = providerItemList[n]; if(pi->p && pi->p->name() == name) { i = pi; p = pi->p; break; } } } providerMutex.unlock(); if(i) i->ensureInit(); return p; } Provider *ProviderManager::findFor(const QString &name, const QString &type) const { if(name.isEmpty()) { providerMutex.lock(); QList list = providerItemList; providerMutex.unlock(); // find the first one that can do it for(int n = 0; n < list.count(); ++n) { ProviderItem *pi = list[n]; pi->ensureInit(); if(pi->p && pi->p->features().contains(type)) return pi->p; } // try the default provider as a last resort providerMutex.lock(); Provider *p = def; providerMutex.unlock(); if(p && p->features().contains(type)) return p; return 0; } else { Provider *p = find(name); if(p && p->features().contains(type)) return p; return 0; } } void ProviderManager::changePriority(const QString &name, int priority) { QMutexLocker locker(&providerMutex); ProviderItem *i = 0; int n = 0; for(; n < providerItemList.count(); ++n) { ProviderItem *pi = providerItemList[n]; if(pi->p && pi->p->name() == name) { i = pi; break; } } if(!i) return; providerItemList.removeAt(n); providerList.removeAt(n); addItem(i, priority); } int ProviderManager::getPriority(const QString &name) { QMutexLocker locker(&providerMutex); ProviderItem *i = 0; for(int n = 0; n < providerItemList.count(); ++n) { ProviderItem *pi = providerItemList[n]; if(pi->p && pi->p->name() == name) { i = pi; break; } } if(!i) return -1; return i->priority; } QStringList ProviderManager::allFeatures() const { QStringList featureList; providerMutex.lock(); Provider *p = def; providerMutex.unlock(); if(p) featureList = p->features(); providerMutex.lock(); QList list = providerItemList; providerMutex.unlock(); for(int n = 0; n < list.count(); ++n) { ProviderItem *i = list[n]; if(i->p) mergeFeatures(&featureList, i->p->features()); } return featureList; } ProviderList ProviderManager::providers() const { QMutexLocker locker(&providerMutex); return providerList; } QString ProviderManager::diagnosticText() const { QMutexLocker locker(&logMutex); return dtext; } void ProviderManager::appendDiagnosticText(const QString &str) { QMutexLocker locker(&logMutex); dtext += str; dtext = truncate_log(dtext, 20000); } void ProviderManager::clearDiagnosticText() { QMutexLocker locker(&logMutex); dtext = QString(); } void ProviderManager::addItem(ProviderItem *item, int priority) { if(priority < 0) { // for -1, make the priority the same as the last item if(!providerItemList.isEmpty()) { ProviderItem *last = providerItemList.last(); item->priority = last->priority; } else item->priority = 0; providerItemList.append(item); providerList.append(item->p); } else { // place the item before any other items with same or greater priority int n = 0; for(; n < providerItemList.count(); ++n) { ProviderItem *i = providerItemList[n]; if(i->priority >= priority) break; } item->priority = priority; providerItemList.insert(n, item); providerList.insert(n, item->p); } } bool ProviderManager::haveAlready(const QString &name) const { if(def && name == def->name()) return true; for(int n = 0; n < providerItemList.count(); ++n) { ProviderItem *pi = providerItemList[n]; if(pi->p && pi->p->name() == name) return true; } return false; } void ProviderManager::mergeFeatures(QStringList *a, const QStringList &b) { for(QStringList::ConstIterator it = b.begin(); it != b.end(); ++it) { if(!a->contains(*it)) a->append(*it); } } int ProviderManager::get_default_priority(const QString &name) const { QStringList list = plugin_priorities(def); foreach(const QString &s, list) { // qca_default already sanity checks the strings int n = s.indexOf(':'); QString sname = s.mid(0, n); - int spriority = s.mid(n + 1).toInt(); + int spriority = s.midRef(n + 1).toInt(); if(sname == name) return spriority; } return -1; } }