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