Merge #10114: [tests] sync_with_ping should assert that ping hasn't timed out
[bitcoinplatinum.git] / src / qt / paymentrequestplus.cpp
blob01ec41661301ee341bec58c9d9be2e3c53d20d3e
1 // Copyright (c) 2011-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 //
6 // Wraps dumb protocol buffer paymentRequest
7 // with some extra methods
8 //
10 #include "paymentrequestplus.h"
12 #include "util.h"
14 #include <stdexcept>
16 #include <openssl/x509_vfy.h>
18 #include <QDateTime>
19 #include <QDebug>
20 #include <QSslCertificate>
22 class SSLVerifyError : public std::runtime_error
24 public:
25 SSLVerifyError(std::string err) : std::runtime_error(err) { }
28 bool PaymentRequestPlus::parse(const QByteArray& data)
30 bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size());
31 if (!parseOK) {
32 qWarning() << "PaymentRequestPlus::parse: Error parsing payment request";
33 return false;
35 if (paymentRequest.payment_details_version() > 1) {
36 qWarning() << "PaymentRequestPlus::parse: Received up-version payment details, version=" << paymentRequest.payment_details_version();
37 return false;
40 parseOK = details.ParseFromString(paymentRequest.serialized_payment_details());
41 if (!parseOK)
43 qWarning() << "PaymentRequestPlus::parse: Error parsing payment details";
44 paymentRequest.Clear();
45 return false;
47 return true;
50 bool PaymentRequestPlus::SerializeToString(std::string* output) const
52 return paymentRequest.SerializeToString(output);
55 bool PaymentRequestPlus::IsInitialized() const
57 return paymentRequest.IsInitialized();
60 bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) const
62 merchant.clear();
64 if (!IsInitialized())
65 return false;
67 // One day we'll support more PKI types, but just
68 // x509 for now:
69 const EVP_MD* digestAlgorithm = NULL;
70 if (paymentRequest.pki_type() == "x509+sha256") {
71 digestAlgorithm = EVP_sha256();
73 else if (paymentRequest.pki_type() == "x509+sha1") {
74 digestAlgorithm = EVP_sha1();
76 else if (paymentRequest.pki_type() == "none") {
77 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: pki_type == none";
78 return false;
80 else {
81 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type());
82 return false;
85 payments::X509Certificates certChain;
86 if (!certChain.ParseFromString(paymentRequest.pki_data())) {
87 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: error parsing pki_data";
88 return false;
91 std::vector<X509*> certs;
92 const QDateTime currentTime = QDateTime::currentDateTime();
93 for (int i = 0; i < certChain.certificate_size(); i++) {
94 QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size());
95 QSslCertificate qCert(certData, QSsl::Der);
96 if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) {
97 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate expired or not yet active: " << qCert;
98 return false;
100 #if QT_VERSION >= 0x050000
101 if (qCert.isBlacklisted()) {
102 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate blacklisted: " << qCert;
103 return false;
105 #endif
106 const unsigned char *data = (const unsigned char *)certChain.certificate(i).data();
107 X509 *cert = d2i_X509(NULL, &data, certChain.certificate(i).size());
108 if (cert)
109 certs.push_back(cert);
111 if (certs.empty()) {
112 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: empty certificate chain";
113 return false;
116 // The first cert is the signing cert, the rest are untrusted certs that chain
117 // to a valid root authority. OpenSSL needs them separately.
118 STACK_OF(X509) *chain = sk_X509_new_null();
119 for (int i = certs.size() - 1; i > 0; i--) {
120 sk_X509_push(chain, certs[i]);
122 X509 *signing_cert = certs[0];
124 // Now create a "store context", which is a single use object for checking,
125 // load the signing cert into it and verify.
126 X509_STORE_CTX *store_ctx = X509_STORE_CTX_new();
127 if (!store_ctx) {
128 qWarning() << "PaymentRequestPlus::getMerchant: Payment request: error creating X509_STORE_CTX";
129 return false;
132 char *website = NULL;
133 bool fResult = true;
136 if (!X509_STORE_CTX_init(store_ctx, certStore, signing_cert, chain))
138 int error = X509_STORE_CTX_get_error(store_ctx);
139 throw SSLVerifyError(X509_verify_cert_error_string(error));
142 // Now do the verification!
143 int result = X509_verify_cert(store_ctx);
144 if (result != 1) {
145 int error = X509_STORE_CTX_get_error(store_ctx);
146 // For testing payment requests, we allow self signed root certs!
147 // This option is just shown in the UI options, if -help-debug is enabled.
148 if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) {
149 throw SSLVerifyError(X509_verify_cert_error_string(error));
150 } else {
151 qDebug() << "PaymentRequestPlus::getMerchant: Allowing self signed root certificate, because -allowselfsignedrootcertificates is true.";
154 X509_NAME *certname = X509_get_subject_name(signing_cert);
156 // Valid cert; check signature:
157 payments::PaymentRequest rcopy(paymentRequest); // Copy
158 rcopy.set_signature(std::string(""));
159 std::string data_to_verify; // Everything but the signature
160 rcopy.SerializeToString(&data_to_verify);
162 #if HAVE_DECL_EVP_MD_CTX_NEW
163 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
164 if (!ctx) throw SSLVerifyError("Error allocating OpenSSL context.");
165 #else
166 EVP_MD_CTX _ctx;
167 EVP_MD_CTX *ctx;
168 ctx = &_ctx;
169 #endif
170 EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
171 EVP_MD_CTX_init(ctx);
172 if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, NULL) ||
173 !EVP_VerifyUpdate(ctx, data_to_verify.data(), data_to_verify.size()) ||
174 !EVP_VerifyFinal(ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) {
175 throw SSLVerifyError("Bad signature, invalid payment request.");
177 #if HAVE_DECL_EVP_MD_CTX_NEW
178 EVP_MD_CTX_free(ctx);
179 #endif
181 // OpenSSL API for getting human printable strings from certs is baroque.
182 int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
183 website = new char[textlen + 1];
184 if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) {
185 merchant = website;
187 else {
188 throw SSLVerifyError("Bad certificate, missing common name.");
190 // TODO: detect EV certificates and set merchant = business name instead of unfriendly NID_commonName ?
192 catch (const SSLVerifyError& err) {
193 fResult = false;
194 qWarning() << "PaymentRequestPlus::getMerchant: SSL error: " << err.what();
197 if (website)
198 delete[] website;
199 X509_STORE_CTX_free(store_ctx);
200 for (unsigned int i = 0; i < certs.size(); i++)
201 X509_free(certs[i]);
203 return fResult;
206 QList<std::pair<CScript,CAmount> > PaymentRequestPlus::getPayTo() const
208 QList<std::pair<CScript,CAmount> > result;
209 for (int i = 0; i < details.outputs_size(); i++)
211 const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data();
212 CScript s(scriptStr, scriptStr+details.outputs(i).script().size());
214 result.append(std::make_pair(s, details.outputs(i).amount()));
216 return result;