PageRenderTime 62ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/src/qt/paymentrequestplus.cpp

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