Raven Core  3.0.0
P2P Digital Currency
paymentrequestplus.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 The Bitcoin Core developers
2 // Copyright (c) 2017-2019 The Raven Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 //
7 // Wraps dumb protocol buffer paymentRequest
8 // with some extra methods
9 //
10 
11 #include "paymentrequestplus.h"
12 
13 #include "util.h"
14 
15 #include <stdexcept>
16 
17 #include <openssl/x509_vfy.h>
18 
19 #include <QDateTime>
20 #include <QDebug>
21 #include <QSslCertificate>
22 
23 class SSLVerifyError : public std::runtime_error
24 {
25 public:
26  explicit SSLVerifyError(std::string err) : std::runtime_error(err) { }
27 };
28 
29 bool PaymentRequestPlus::parse(const QByteArray& data)
30 {
31  bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size());
32  if (!parseOK) {
33  qWarning() << "PaymentRequestPlus::parse: Error parsing payment request";
34  return false;
35  }
36  if (paymentRequest.payment_details_version() > 1) {
37  qWarning() << "PaymentRequestPlus::parse: Received up-version payment details, version=" << paymentRequest.payment_details_version();
38  return false;
39  }
40 
41  parseOK = details.ParseFromString(paymentRequest.serialized_payment_details());
42  if (!parseOK)
43  {
44  qWarning() << "PaymentRequestPlus::parse: Error parsing payment details";
45  paymentRequest.Clear();
46  return false;
47  }
48  return true;
49 }
50 
51 bool PaymentRequestPlus::SerializeToString(std::string* output) const
52 {
53  return paymentRequest.SerializeToString(output);
54 }
55 
57 {
58  return paymentRequest.IsInitialized();
59 }
60 
61 bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) const
62 {
63  merchant.clear();
64 
65  if (!IsInitialized())
66  return false;
67 
68  // One day we'll support more PKI types, but just
69  // x509 for now:
70  const EVP_MD* digestAlgorithm = nullptr;
71  if (paymentRequest.pki_type() == "x509+sha256") {
72  digestAlgorithm = EVP_sha256();
73  }
74  else if (paymentRequest.pki_type() == "x509+sha1") {
75  digestAlgorithm = EVP_sha1();
76  }
77  else if (paymentRequest.pki_type() == "none") {
78  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: pki_type == none";
79  return false;
80  }
81  else {
82  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type());
83  return false;
84  }
85 
86  payments::X509Certificates certChain;
87  if (!certChain.ParseFromString(paymentRequest.pki_data())) {
88  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: error parsing pki_data";
89  return false;
90  }
91 
92  std::vector<X509*> certs;
93  const QDateTime currentTime = QDateTime::currentDateTime();
94  for (int i = 0; i < certChain.certificate_size(); i++) {
95  QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size());
96  QSslCertificate qCert(certData, QSsl::Der);
97  if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) {
98  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate expired or not yet active: " << qCert;
99  return false;
100  }
101 #if QT_VERSION >= 0x050000
102  if (qCert.isBlacklisted()) {
103  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: certificate blacklisted: " << qCert;
104  return false;
105  }
106 #endif
107  const unsigned char *data = (const unsigned char *)certChain.certificate(i).data();
108  X509 *cert = d2i_X509(nullptr, &data, certChain.certificate(i).size());
109  if (cert)
110  certs.push_back(cert);
111  }
112  if (certs.empty()) {
113  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: empty certificate chain";
114  return false;
115  }
116 
117  // The first cert is the signing cert, the rest are untrusted certs that chain
118  // to a valid root authority. OpenSSL needs them separately.
119  STACK_OF(X509) *chain = sk_X509_new_null();
120  for (int i = certs.size() - 1; i > 0; i--) {
121  sk_X509_push(chain, certs[i]);
122  }
123  X509 *signing_cert = certs[0];
124 
125  // Now create a "store context", which is a single use object for checking,
126  // load the signing cert into it and verify.
127  X509_STORE_CTX *store_ctx = X509_STORE_CTX_new();
128  if (!store_ctx) {
129  qWarning() << "PaymentRequestPlus::getMerchant: Payment request: error creating X509_STORE_CTX";
130  return false;
131  }
132 
133  char *website = nullptr;
134  bool fResult = true;
135  try
136  {
137  if (!X509_STORE_CTX_init(store_ctx, certStore, signing_cert, chain))
138  {
139  int error = X509_STORE_CTX_get_error(store_ctx);
140  throw SSLVerifyError(X509_verify_cert_error_string(error));
141  }
142 
143  // Now do the verification!
144  int result = X509_verify_cert(store_ctx);
145  if (result != 1) {
146  int error = X509_STORE_CTX_get_error(store_ctx);
147  // For testing payment requests, we allow self signed root certs!
148  // This option is just shown in the UI options, if -help-debug is enabled.
149  if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && gArgs.GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) {
150  throw SSLVerifyError(X509_verify_cert_error_string(error));
151  } else {
152  qDebug() << "PaymentRequestPlus::getMerchant: Allowing self signed root certificate, because -allowselfsignedrootcertificates is true.";
153  }
154  }
155  X509_NAME *certname = X509_get_subject_name(signing_cert);
156 
157  // Valid cert; check signature:
158  payments::PaymentRequest rcopy(paymentRequest); // Copy
159  rcopy.set_signature(std::string(""));
160  std::string data_to_verify; // Everything but the signature
161  rcopy.SerializeToString(&data_to_verify);
162 
163 #if HAVE_DECL_EVP_MD_CTX_NEW
164  EVP_MD_CTX *ctx = EVP_MD_CTX_new();
165  if (!ctx) throw SSLVerifyError("Error allocating OpenSSL context.");
166 #else
167  EVP_MD_CTX _ctx;
168  EVP_MD_CTX *ctx;
169  ctx = &_ctx;
170 #endif
171  EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
172  EVP_MD_CTX_init(ctx);
173  if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, nullptr) ||
174  !EVP_VerifyUpdate(ctx, data_to_verify.data(), data_to_verify.size()) ||
175  !EVP_VerifyFinal(ctx, (const unsigned char*)paymentRequest.signature().data(), (unsigned int)paymentRequest.signature().size(), pubkey)) {
176  throw SSLVerifyError("Bad signature, invalid payment request.");
177  }
178 #if HAVE_DECL_EVP_MD_CTX_NEW
179  EVP_MD_CTX_free(ctx);
180 #endif
181 
182  // OpenSSL API for getting human printable strings from certs is baroque.
183  int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, nullptr, 0);
184  website = new char[textlen + 1];
185  if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) {
186  merchant = website;
187  }
188  else {
189  throw SSLVerifyError("Bad certificate, missing common name.");
190  }
191  // TODO: detect EV certificates and set merchant = business name instead of unfriendly NID_commonName ?
192  }
193  catch (const SSLVerifyError& err) {
194  fResult = false;
195  qWarning() << "PaymentRequestPlus::getMerchant: SSL error: " << err.what();
196  }
197 
198  if (website)
199  delete[] website;
200  X509_STORE_CTX_free(store_ctx);
201  for (unsigned int i = 0; i < certs.size(); i++)
202  X509_free(certs[i]);
203 
204  return fResult;
205 }
206 
207 QList<std::pair<CScript,CAmount> > PaymentRequestPlus::getPayTo() const
208 {
209  QList<std::pair<CScript,CAmount> > result;
210  for (int i = 0; i < details.outputs_size(); i++)
211  {
212  const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data();
213  CScript s(scriptStr, scriptStr+details.outputs(i).script().size());
214 
215  result.append(std::make_pair(s, details.outputs(i).amount()));
216  }
217  return result;
218 }
SSLVerifyError(std::string err)
bool SerializeToString(std::string *output) const
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: util.cpp:470
bool getMerchant(X509_STORE *certStore, QString &merchant) const
QList< std::pair< CScript, CAmount > > getPayTo() const
bool parse(const QByteArray &data)
ArgsManager gArgs
Definition: util.cpp:94
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:396
bool error(const char *fmt, const Args &... args)
Definition: util.h:168