Bitcoin ABC 0.30.5
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 <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
26bool 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
51bool PaymentRequestPlus::SerializeToString(std::string *output) const {
52 return paymentRequest.SerializeToString(output);
53}
54
56 return paymentRequest.IsInitialized();
57}
58
59bool 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
228QList<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}
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:226
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