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