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