Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
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 <common/args.h>
12
13#include <openssl/x509_vfy.h>
14
15#include <QDateTime>
16#include <QDebug>
17#include <QSslCertificate>
18
19#include <stdexcept>
20
21class SSLVerifyError : public std::runtime_error {
22public:
23 explicit SSLVerifyError(std::string err) : std::runtime_error(err) {}
24};
25
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
51bool PaymentRequestPlus::SerializeToString(std::string *output) const {
52 return paymentRequest.SerializeToString(output);
53}
54
56 return paymentRequest.IsInitialized();
57}
58
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") {
71 } else if (paymentRequest.pki_type() == "x509+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.
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 {
145 }
146
147 // Now do the verification!
148 int result = X509_verify_cert(store_ctx);
149 if (result != 1) {
151 // For testing payment requests, we allow self signed root certs!
153 gArgs.GetBoolArg("-allowselfsignedrootcertificates",
156 } else {
157 qDebug() << "PaymentRequestPlus::getMerchant: Allowing self "
158 "signed root certificate, because "
159 "-allowselfsignedrootcertificates is true.";
160 }
161 }
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
174 if (!ctx) {
175 throw SSLVerifyError("Error allocating OpenSSL context.");
176 }
177#else
180 ctx = &_ctx;
181#endif
184 if (!EVP_VerifyInit_ex(ctx, digestAlgorithm, nullptr) ||
186 data_to_verify.size()) ||
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
194#endif
195
196 // OpenSSL API for getting human printable strings from certs is
197 // baroque.
198 int textlen =
200 website = new char[textlen + 1];
202 textlen + 1) == textlen &&
203 textlen > 0) {
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
221 for (size_t i = 0; i < certs.size(); i++) {
222 X509_free(certs[i]);
223 }
224
225 return fResult;
226}
227
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}
static constexpr Amount SATOSHI
Definition amount.h:143
ArgsManager gArgs
Definition args.cpp:38
secp256k1_context * ctx
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition args.cpp:556
QList< std::pair< CScript, Amount > > getPayTo() const
bool getMerchant(X509_STORE *certStore, QString &merchant) const
payments::PaymentDetails details
bool parse(const QByteArray &data)
payments::PaymentRequest paymentRequest
bool SerializeToString(std::string *output) const
SSLVerifyError(std::string err)
bool error(const char *fmt, const Args &...args)
Definition logging.h:263
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition rcu.h:259
static const bool DEFAULT_SELFSIGNED_ROOTCERTS
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85