Anoncoin  0.9.4
P2P Digital Currency
paymentrequestplus.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2013 The Bitcoin developers
2 // Copyright (c) 2013-2014 The Anoncoin Core developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 //
7 // Wraps dumb protocol buffer paymentRequest
8 // with some extra methods
9 //
10 
11 #include "paymentrequestplus.h"
12 
13 #include <stdexcept>
14 
15 #include <openssl/x509.h>
16 #include <openssl/x509_vfy.h>
17 #include <QDateTime>
18 #include <QDebug>
19 #include <QSslCertificate>
20 
21 
22 class SSLVerifyError : public std::runtime_error
23 {
24 public:
25  SSLVerifyError(std::string err) : std::runtime_error(err) { }
26 };
27 
28 bool PaymentRequestPlus::parse(const QByteArray& data)
29 {
30  bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size());
31  if (!parseOK) {
32  qDebug() << "PaymentRequestPlus::parse : Error parsing payment request";
33  return false;
34  }
36  qDebug() << "PaymentRequestPlus::parse : Received up-version payment details, version=" << paymentRequest.payment_details_version();
37  return false;
38  }
39 
40  parseOK = details.ParseFromString(paymentRequest.serialized_payment_details());
41  if (!parseOK)
42  {
43  qDebug() << "PaymentRequestPlus::parse : Error parsing payment details";
45  return false;
46  }
47  return true;
48 }
49 
50 bool PaymentRequestPlus::SerializeToString(string* output) const
51 {
52  return paymentRequest.SerializeToString(output);
53 }
54 
56 {
58 }
59 
61 {
62  if (!IsInitialized()) return QString("none");
63  return QString::fromStdString(paymentRequest.pki_type());
64 }
65 
66 bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) const
67 {
68  merchant.clear();
69 
70  if (!IsInitialized())
71  return false;
72 
73  // One day we'll support more PKI types, but just
74  // x509 for now:
75  const EVP_MD* digestAlgorithm = NULL;
76  if (paymentRequest.pki_type() == "x509+sha256") {
77  digestAlgorithm = EVP_sha256();
78  }
79  else if (paymentRequest.pki_type() == "x509+sha1") {
80  digestAlgorithm = EVP_sha1();
81  }
82  else if (paymentRequest.pki_type() == "none") {
83  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none";
84  return false;
85  }
86  else {
87  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type());
88  return false;
89  }
90 
92  if (!certChain.ParseFromString(paymentRequest.pki_data())) {
93  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error parsing pki_data";
94  return false;
95  }
96 
97  std::vector<X509*> certs;
98  const QDateTime currentTime = QDateTime::currentDateTime();
99  for (int i = 0; i < certChain.certificate_size(); i++) {
100  QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size());
101  QSslCertificate qCert(certData, QSsl::Der);
102  if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) {
103  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate expired or not yet active: " << qCert;
104  return false;
105  }
106 #if QT_VERSION >= 0x050000
107  if (qCert.isBlacklisted()) {
108  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate blacklisted: " << qCert;
109  return false;
110  }
111 #endif
112  const unsigned char *data = (const unsigned char *)certChain.certificate(i).data();
113  X509 *cert = d2i_X509(NULL, &data, certChain.certificate(i).size());
114  if (cert)
115  certs.push_back(cert);
116  }
117  if (certs.empty()) {
118  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: empty certificate chain";
119  return false;
120  }
121 
122  // The first cert is the signing cert, the rest are untrusted certs that chain
123  // 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  qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error creating X509_STORE_CTX";
135  return false;
136  }
137 
138  char *website = NULL;
139  bool fResult = true;
140  try
141  {
142  if (!X509_STORE_CTX_init(store_ctx, certStore, signing_cert, chain))
143  {
144  int error = X509_STORE_CTX_get_error(store_ctx);
145  throw SSLVerifyError(X509_verify_cert_error_string(error));
146  }
147 
148  // Now do the verification!
149  int result = X509_verify_cert(store_ctx);
150  if (result != 1) {
151  int error = X509_STORE_CTX_get_error(store_ctx);
152  throw SSLVerifyError(X509_verify_cert_error_string(error));
153  }
154  X509_NAME *certname = X509_get_subject_name(signing_cert);
155 
156  // Valid cert; check signature:
158  rcopy.set_signature(std::string(""));
159  std::string data_to_verify; // Everything but the signature
160  rcopy.SerializeToString(&data_to_verify);
161 
162  EVP_MD_CTX ctx;
163  EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
164  EVP_MD_CTX_init(&ctx);
165  if (!EVP_VerifyInit_ex(&ctx, digestAlgorithm, NULL) ||
166  !EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size()) ||
167  !EVP_VerifyFinal(&ctx, (const unsigned char*)paymentRequest.signature().data(), paymentRequest.signature().size(), pubkey)) {
168 
169  throw SSLVerifyError("Bad signature, invalid PaymentRequest.");
170  }
171 
172  // OpenSSL API for getting human printable strings from certs is baroque.
173  int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
174  website = new char[textlen + 1];
175  if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) {
176  merchant = website;
177  }
178  else {
179  throw SSLVerifyError("Bad certificate, missing common name.");
180  }
181  // TODO: detect EV certificates and set merchant = business name instead of unfriendly NID_commonName ?
182  }
183  catch (SSLVerifyError& err)
184  {
185  fResult = false;
186  qDebug() << "PaymentRequestPlus::getMerchant : SSL error: " << err.what();
187  }
188 
189  if (website)
190  delete[] website;
191  X509_STORE_CTX_free(store_ctx);
192  for (unsigned int i = 0; i < certs.size(); i++)
193  X509_free(certs[i]);
194 
195  return fResult;
196 }
197 
198 QList<std::pair<CScript,qint64> > PaymentRequestPlus::getPayTo() const
199 {
200  QList<std::pair<CScript,qint64> > result;
201  for (int i = 0; i < details.outputs_size(); i++)
202  {
203  const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data();
204  CScript s(scriptStr, scriptStr+details.outputs(i).script().size());
205 
206  result.append(make_pair(s, details.outputs(i).amount()));
207  }
208  return result;
209 }
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
const ::std::string & signature() const
const ::std::string & script() const
const ::payments::Output & outputs(int index) const
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:413
const ::std::string & serialized_payment_details() const
QString getPKIType() const
QList< std::pair< CScript, qint64 > > getPayTo() const
const ::std::string & pki_type() const