LCOV - code coverage report
Current view: top level - src/qt - paymentrequestplus.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 75 102 73.5 %
Date: 2015-10-12 22:39:14 Functions: 7 10 70.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.11