qt: cleanup: Move BIP70 functions together in paymentserver
Reduces the number of separate `#ifdefs` spans.
This commit is contained in:
parent
9dcf6c0dfe
commit
38b98507cd
2 changed files with 149 additions and 160 deletions
|
@ -57,21 +57,6 @@ const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
|
||||||
const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment";
|
const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment";
|
||||||
const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack";
|
const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack";
|
||||||
const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest";
|
const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest";
|
||||||
|
|
||||||
struct X509StoreDeleter {
|
|
||||||
void operator()(X509_STORE* b) {
|
|
||||||
X509_STORE_free(b);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct X509Deleter {
|
|
||||||
void operator()(X509* b) { X509_free(b); }
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace // Anon namespace
|
|
||||||
{
|
|
||||||
std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -99,96 +84,6 @@ static QString ipcServerName()
|
||||||
|
|
||||||
static QList<QString> savedPaymentRequests;
|
static QList<QString> savedPaymentRequests;
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
|
||||||
static void ReportInvalidCertificate(const QSslCertificate& cert)
|
|
||||||
{
|
|
||||||
qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Load OpenSSL's list of root certificate authorities
|
|
||||||
//
|
|
||||||
void PaymentServer::LoadRootCAs(X509_STORE* _store)
|
|
||||||
{
|
|
||||||
// Unit tests mostly use this, to pass in fake root CAs:
|
|
||||||
if (_store)
|
|
||||||
{
|
|
||||||
certStore.reset(_store);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal execution, use either -rootcertificates or system certs:
|
|
||||||
certStore.reset(X509_STORE_new());
|
|
||||||
|
|
||||||
// Note: use "-system-" default here so that users can pass -rootcertificates=""
|
|
||||||
// and get 'I don't like X.509 certificates, don't trust anybody' behavior:
|
|
||||||
QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
|
|
||||||
|
|
||||||
// Empty store
|
|
||||||
if (certFile.isEmpty()) {
|
|
||||||
qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QSslCertificate> certList;
|
|
||||||
|
|
||||||
if (certFile != "-system-") {
|
|
||||||
qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
|
|
||||||
|
|
||||||
certList = QSslCertificate::fromPath(certFile);
|
|
||||||
// Use those certificates when fetching payment requests, too:
|
|
||||||
QSslSocket::setDefaultCaCertificates(certList);
|
|
||||||
} else
|
|
||||||
certList = QSslSocket::systemCaCertificates();
|
|
||||||
|
|
||||||
int nRootCerts = 0;
|
|
||||||
const QDateTime currentTime = QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
for (const QSslCertificate& cert : certList) {
|
|
||||||
// Don't log NULL certificates
|
|
||||||
if (cert.isNull())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Not yet active/valid, or expired certificate
|
|
||||||
if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
|
|
||||||
ReportInvalidCertificate(cert);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blacklisted certificate
|
|
||||||
if (cert.isBlacklisted()) {
|
|
||||||
ReportInvalidCertificate(cert);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QByteArray certData = cert.toDer();
|
|
||||||
const unsigned char *data = (const unsigned char *)certData.data();
|
|
||||||
|
|
||||||
std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
|
|
||||||
if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
|
|
||||||
{
|
|
||||||
// Note: X509_STORE increases the reference count to the X509 object,
|
|
||||||
// we still have to release our reference to it.
|
|
||||||
++nRootCerts;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ReportInvalidCertificate(cert);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
|
|
||||||
|
|
||||||
// Project for another day:
|
|
||||||
// Fetch certificate revocation lists, and add them to certStore.
|
|
||||||
// Issues to consider:
|
|
||||||
// performance (start a thread to fetch in background?)
|
|
||||||
// privacy (fetch through tor/proxy so IP address isn't revealed)
|
|
||||||
// would it be easier to just use a compiled-in blacklist?
|
|
||||||
// or use Qt's blacklist?
|
|
||||||
// "certificate stapling" with server-side caching is more efficient
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Sending to the server is done synchronously, at startup.
|
// Sending to the server is done synchronously, at startup.
|
||||||
// If the server isn't already running, startup continues,
|
// If the server isn't already running, startup continues,
|
||||||
|
@ -300,10 +195,10 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
saveURIs(true),
|
saveURIs(true),
|
||||||
uriServer(0),
|
uriServer(0),
|
||||||
#ifdef ENABLE_BIP70
|
|
||||||
netManager(0),
|
|
||||||
#endif
|
|
||||||
optionsModel(0)
|
optionsModel(0)
|
||||||
|
#ifdef ENABLE_BIP70
|
||||||
|
,netManager(0)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
// Verify that the version of the library that we linked against is
|
// Verify that the version of the library that we linked against is
|
||||||
|
@ -367,32 +262,6 @@ bool PaymentServer::eventFilter(QObject *object, QEvent *event)
|
||||||
return QObject::eventFilter(object, event);
|
return QObject::eventFilter(object, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
|
||||||
void PaymentServer::initNetManager()
|
|
||||||
{
|
|
||||||
if (!optionsModel)
|
|
||||||
return;
|
|
||||||
delete netManager;
|
|
||||||
|
|
||||||
// netManager is used to fetch paymentrequests given in bitcoin: URIs
|
|
||||||
netManager = new QNetworkAccessManager(this);
|
|
||||||
|
|
||||||
QNetworkProxy proxy;
|
|
||||||
|
|
||||||
// Query active SOCKS5 proxy
|
|
||||||
if (optionsModel->getProxySettings(proxy)) {
|
|
||||||
netManager->setProxy(proxy);
|
|
||||||
|
|
||||||
qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
|
|
||||||
|
|
||||||
connect(netManager, &QNetworkAccessManager::finished, this, &PaymentServer::netRequestFinished);
|
|
||||||
connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void PaymentServer::uiReady()
|
void PaymentServer::uiReady()
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
|
@ -510,7 +379,140 @@ void PaymentServer::handleURIConnection()
|
||||||
handleURIOrFile(msg);
|
handleURIOrFile(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
|
||||||
|
{
|
||||||
|
this->optionsModel = _optionsModel;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
|
struct X509StoreDeleter {
|
||||||
|
void operator()(X509_STORE* b) {
|
||||||
|
X509_STORE_free(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct X509Deleter {
|
||||||
|
void operator()(X509* b) { X509_free(b); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace // Anon namespace
|
||||||
|
{
|
||||||
|
std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReportInvalidCertificate(const QSslCertificate& cert)
|
||||||
|
{
|
||||||
|
qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Load OpenSSL's list of root certificate authorities
|
||||||
|
//
|
||||||
|
void PaymentServer::LoadRootCAs(X509_STORE* _store)
|
||||||
|
{
|
||||||
|
// Unit tests mostly use this, to pass in fake root CAs:
|
||||||
|
if (_store)
|
||||||
|
{
|
||||||
|
certStore.reset(_store);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal execution, use either -rootcertificates or system certs:
|
||||||
|
certStore.reset(X509_STORE_new());
|
||||||
|
|
||||||
|
// Note: use "-system-" default here so that users can pass -rootcertificates=""
|
||||||
|
// and get 'I don't like X.509 certificates, don't trust anybody' behavior:
|
||||||
|
QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
|
||||||
|
|
||||||
|
// Empty store
|
||||||
|
if (certFile.isEmpty()) {
|
||||||
|
qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QSslCertificate> certList;
|
||||||
|
|
||||||
|
if (certFile != "-system-") {
|
||||||
|
qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
|
||||||
|
|
||||||
|
certList = QSslCertificate::fromPath(certFile);
|
||||||
|
// Use those certificates when fetching payment requests, too:
|
||||||
|
QSslSocket::setDefaultCaCertificates(certList);
|
||||||
|
} else
|
||||||
|
certList = QSslSocket::systemCaCertificates();
|
||||||
|
|
||||||
|
int nRootCerts = 0;
|
||||||
|
const QDateTime currentTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
for (const QSslCertificate& cert : certList) {
|
||||||
|
// Don't log NULL certificates
|
||||||
|
if (cert.isNull())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Not yet active/valid, or expired certificate
|
||||||
|
if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
|
||||||
|
ReportInvalidCertificate(cert);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blacklisted certificate
|
||||||
|
if (cert.isBlacklisted()) {
|
||||||
|
ReportInvalidCertificate(cert);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray certData = cert.toDer();
|
||||||
|
const unsigned char *data = (const unsigned char *)certData.data();
|
||||||
|
|
||||||
|
std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
|
||||||
|
if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
|
||||||
|
{
|
||||||
|
// Note: X509_STORE increases the reference count to the X509 object,
|
||||||
|
// we still have to release our reference to it.
|
||||||
|
++nRootCerts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReportInvalidCertificate(cert);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
|
||||||
|
|
||||||
|
// Project for another day:
|
||||||
|
// Fetch certificate revocation lists, and add them to certStore.
|
||||||
|
// Issues to consider:
|
||||||
|
// performance (start a thread to fetch in background?)
|
||||||
|
// privacy (fetch through tor/proxy so IP address isn't revealed)
|
||||||
|
// would it be easier to just use a compiled-in blacklist?
|
||||||
|
// or use Qt's blacklist?
|
||||||
|
// "certificate stapling" with server-side caching is more efficient
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaymentServer::initNetManager()
|
||||||
|
{
|
||||||
|
if (!optionsModel)
|
||||||
|
return;
|
||||||
|
delete netManager;
|
||||||
|
|
||||||
|
// netManager is used to fetch paymentrequests given in bitcoin: URIs
|
||||||
|
netManager = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
|
QNetworkProxy proxy;
|
||||||
|
|
||||||
|
// Query active SOCKS5 proxy
|
||||||
|
if (optionsModel->getProxySettings(proxy)) {
|
||||||
|
netManager->setProxy(proxy);
|
||||||
|
|
||||||
|
qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
|
||||||
|
|
||||||
|
connect(netManager, &QNetworkAccessManager::finished, this, &PaymentServer::netRequestFinished);
|
||||||
|
connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
|
// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
|
||||||
// so don't use "Q_EMIT message()", but "QMessageBox::"!
|
// so don't use "Q_EMIT message()", but "QMessageBox::"!
|
||||||
|
@ -760,14 +762,7 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError>
|
||||||
}
|
}
|
||||||
Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
|
Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
|
|
||||||
{
|
|
||||||
this->optionsModel = _optionsModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
|
||||||
void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
|
void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
|
||||||
{
|
{
|
||||||
// currently we don't further process or store the paymentACK message
|
// currently we don't further process or store the paymentACK message
|
||||||
|
|
|
@ -79,6 +79,9 @@ public:
|
||||||
explicit PaymentServer(QObject* parent, bool startLocalServer = true);
|
explicit PaymentServer(QObject* parent, bool startLocalServer = true);
|
||||||
~PaymentServer();
|
~PaymentServer();
|
||||||
|
|
||||||
|
// OptionsModel is used for getting proxy settings and display unit
|
||||||
|
void setOptionsModel(OptionsModel *optionsModel);
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
// Load root certificate authorities. Pass nullptr (default)
|
// Load root certificate authorities. Pass nullptr (default)
|
||||||
// to read from the file specified in the -rootcertificates setting,
|
// to read from the file specified in the -rootcertificates setting,
|
||||||
|
@ -89,12 +92,7 @@ public:
|
||||||
|
|
||||||
// Return certificate store
|
// Return certificate store
|
||||||
static X509_STORE* getCertStore();
|
static X509_STORE* getCertStore();
|
||||||
#endif
|
|
||||||
|
|
||||||
// OptionsModel is used for getting proxy settings and display unit
|
|
||||||
void setOptionsModel(OptionsModel *optionsModel);
|
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
|
||||||
// Verify that the payment request network matches the client network
|
// Verify that the payment request network matches the client network
|
||||||
static bool verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails);
|
static bool verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails);
|
||||||
// Verify if the payment request is expired
|
// Verify if the payment request is expired
|
||||||
|
@ -109,27 +107,27 @@ Q_SIGNALS:
|
||||||
// Fired when a valid payment request is received
|
// Fired when a valid payment request is received
|
||||||
void receivedPaymentRequest(SendCoinsRecipient);
|
void receivedPaymentRequest(SendCoinsRecipient);
|
||||||
|
|
||||||
|
// Fired when a message should be reported to the user
|
||||||
|
void message(const QString &title, const QString &message, unsigned int style);
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
// Fired when a valid PaymentACK is received
|
// Fired when a valid PaymentACK is received
|
||||||
void receivedPaymentACK(const QString &paymentACKMsg);
|
void receivedPaymentACK(const QString &paymentACKMsg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Fired when a message should be reported to the user
|
|
||||||
void message(const QString &title, const QString &message, unsigned int style);
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
// Signal this when the main window's UI is ready
|
// Signal this when the main window's UI is ready
|
||||||
// to display payment requests to the user
|
// to display payment requests to the user
|
||||||
void uiReady();
|
void uiReady();
|
||||||
|
|
||||||
|
// Handle an incoming URI, URI with local file scheme or file
|
||||||
|
void handleURIOrFile(const QString& s);
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
// Submit Payment message to a merchant, get back PaymentACK:
|
// Submit Payment message to a merchant, get back PaymentACK:
|
||||||
void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
|
void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Handle an incoming URI, URI with local file scheme or file
|
|
||||||
void handleURIOrFile(const QString& s);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleURIConnection();
|
void handleURIConnection();
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
|
@ -144,6 +142,10 @@ protected:
|
||||||
bool eventFilter(QObject *object, QEvent *event);
|
bool eventFilter(QObject *object, QEvent *event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool saveURIs; // true during startup
|
||||||
|
QLocalServer* uriServer;
|
||||||
|
OptionsModel *optionsModel;
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
#ifdef ENABLE_BIP70
|
||||||
static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request);
|
static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request);
|
||||||
bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient);
|
bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient);
|
||||||
|
@ -151,16 +153,8 @@ private:
|
||||||
|
|
||||||
// Setup networking
|
// Setup networking
|
||||||
void initNetManager();
|
void initNetManager();
|
||||||
#endif
|
|
||||||
|
|
||||||
bool saveURIs; // true during startup
|
|
||||||
QLocalServer* uriServer;
|
|
||||||
|
|
||||||
#ifdef ENABLE_BIP70
|
|
||||||
QNetworkAccessManager* netManager; // Used to fetch payment requests
|
QNetworkAccessManager* netManager; // Used to fetch payment requests
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
OptionsModel *optionsModel;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_QT_PAYMENTSERVER_H
|
#endif // BITCOIN_QT_PAYMENTSERVER_H
|
||||||
|
|
Loading…
Add table
Reference in a new issue