Master Core  v0.0.9 - 49a5c0d97abf09ef2911ddfe8d9551df59f9efd3-dirty
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
paymentrequestplus.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2013 The Bitcoin developers
2 // Distributed under the MIT/X11 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 <stdexcept>
13 
14 #include <openssl/x509.h>
15 #include <openssl/x509_vfy.h>
16 #include <QDateTime>
17 #include <QDebug>
18 #include <QSslCertificate>
19 
20 
21 class SSLVerifyError : public std::runtime_error
22 {
23 public:
24  SSLVerifyError(std::string err) : std::runtime_error(err) { }
25 };
26 
27 bool PaymentRequestPlus::parse(const QByteArray& data)
28 {
29  bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size());
30  if (!parseOK) {
31  qDebug() << "PaymentRequestPlus::parse : Error parsing payment request";
32  return false;
33  }
35  qDebug() << "PaymentRequestPlus::parse : Received up-version payment details, version=" << paymentRequest.payment_details_version();
36  return false;
37  }
38 
39  parseOK = details.ParseFromString(paymentRequest.serialized_payment_details());
40  if (!parseOK)
41  {
42  qDebug() << "PaymentRequestPlus::parse : Error parsing payment details";
44  return false;
45  }
46  return true;
47 }
48 
49 bool PaymentRequestPlus::SerializeToString(string* output) const
50 {
51  return paymentRequest.SerializeToString(output);
52 }
53 
55 {
57 }
58 
60 {
61  if (!IsInitialized()) return QString("none");
62  return QString::fromStdString(paymentRequest.pki_type());
63 }
64 
65 bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) const
66 {
67  merchant.clear();
68 
69  if (!IsInitialized())
70  return false;
71 
72  // One day we'll support more PKI types, but just
73  // x509 for now:
74  const EVP_MD* digestAlgorithm = NULL;
75  if (paymentRequest.pki_type() == "x509+sha256") {
76  digestAlgorithm = EVP_sha256();
77  }
78  else if (paymentRequest.pki_type() == "x509+sha1") {
79  digestAlgorithm = EVP_sha1();
80  }
81  else if (paymentRequest.pki_type() == "none") {
82  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none";
83  return false;
84  }
85  else {
86  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type());
87  return false;
88  }
89 
91  if (!certChain.ParseFromString(paymentRequest.pki_data())) {
92  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error parsing pki_data";
93  return false;
94  }
95 
96  std::vector<X509*> certs;
97  const QDateTime currentTime = QDateTime::currentDateTime();
98  for (int i = 0; i < certChain.certificate_size(); i++) {
99  QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size());
100  QSslCertificate qCert(certData, QSsl::Der);
101  if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) {
102  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate expired or not yet active: " << qCert;
103  return false;
104  }
105 #if QT_VERSION >= 0x050000
106  if (qCert.isBlacklisted()) {
107  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate blacklisted: " << qCert;
108  return false;
109  }
110 #endif
111  const unsigned char *data = (const unsigned char *)certChain.certificate(i).data();
112  X509 *cert = d2i_X509(NULL, &data, certChain.certificate(i).size());
113  if (cert)
114  certs.push_back(cert);
115  }
116  if (certs.empty()) {
117  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: empty certificate chain";
118  return false;
119  }
120 
121  // The first cert is the signing cert, the rest are untrusted certs that chain
122  // to a valid root authority. OpenSSL needs them separately.
123  STACK_OF(X509) *chain = sk_X509_new_null();
124  for (int i = certs.size()-1; i > 0; i--) {
125  sk_X509_push(chain, certs[i]);
126  }
127  X509 *signing_cert = certs[0];
128 
129  // Now create a "store context", which is a single use object for checking,
130  // load the signing cert into it and verify.
131  X509_STORE_CTX *store_ctx = X509_STORE_CTX_new();
132  if (!store_ctx) {
133  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error creating X509_STORE_CTX";
134  return false;
135  }
136 
137  char *website = NULL;
138  bool fResult = true;
139  try
140  {
141  if (!X509_STORE_CTX_init(store_ctx, certStore, signing_cert, chain))
142  {
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  throw SSLVerifyError(X509_verify_cert_error_string(error));
152  }
153  X509_NAME *certname = X509_get_subject_name(signing_cert);
154 
155  // Valid cert; check signature:
157  rcopy.set_signature(std::string(""));
158  std::string data_to_verify; // Everything but the signature
159  rcopy.SerializeToString(&data_to_verify);
160 
161  EVP_MD_CTX ctx;
162  EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
163  EVP_MD_CTX_init(&ctx);
164  if (!EVP_VerifyInit_ex(&ctx, digestAlgorithm, NULL) ||
165  !EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size()) ||
166  !EVP_VerifyFinal(&ctx, (const unsigned char*)paymentRequest.signature().data(), paymentRequest.signature().size(), pubkey)) {
167 
168  throw SSLVerifyError("Bad signature, invalid PaymentRequest.");
169  }
170 
171  // OpenSSL API for getting human printable strings from certs is baroque.
172  int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
173  website = new char[textlen + 1];
174  if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) {
175  merchant = website;
176  }
177  else {
178  throw SSLVerifyError("Bad certificate, missing common name.");
179  }
180  // TODO: detect EV certificates and set merchant = business name instead of unfriendly NID_commonName ?
181  }
182  catch (SSLVerifyError& err)
183  {
184  fResult = false;
185  qDebug() << "PaymentRequestPlus::getMerchant : SSL error: " << err.what();
186  }
187 
188  if (website)
189  delete[] website;
190  X509_STORE_CTX_free(store_ctx);
191  for (unsigned int i = 0; i < certs.size(); i++)
192  X509_free(certs[i]);
193 
194  return fResult;
195 }
196 
197 QList<std::pair<CScript,qint64> > PaymentRequestPlus::getPayTo() const
198 {
199  QList<std::pair<CScript,qint64> > result;
200  for (int i = 0; i < details.outputs_size(); i++)
201  {
202  const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data();
203  CScript s(scriptStr, scriptStr+details.outputs(i).script().size());
204 
205  result.append(make_pair(s, details.outputs(i).amount()));
206  }
207  return result;
208 }
const ::std::string & certificate(int index) const
bool IsInitialized() const
inline::google::protobuf::uint32 payment_details_version() const
SSLVerifyError(std::string err)
bool getMerchant(X509_STORE *certStore, QString &merchant) const
const ::std::string & pki_data() const
bool SerializeToString(string *output) const
STL namespace.
const ::std::string & signature() const
const ::std::string & script() const
const ::payments::Output & outputs(int index) const
static bool error(const char *format)
Definition: util.h:148
inline::google::protobuf::uint64 amount() const
bool parse(const QByteArray &data)
payments::PaymentDetails details
void set_signature(const ::std::string &value)
payments::PaymentRequest paymentRequest
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:401
const ::std::string & serialized_payment_details() const
QString getPKIType() const
static const CCheckpointData data
Definition: checkpoints.cpp:56
QList< std::pair< CScript, qint64 > > getPayTo() const
const ::std::string & pki_type() const