1 // Copyright (c) 2009-2015 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 #include "paymentservertests.h"
6 
7 #include "optionsmodel.h"
8 #include "paymentrequestdata.h"
9 
10 #include "amount.h"
11 #include "random.h"
12 #include "script/script.h"
13 #include "script/standard.h"
14 #include "util.h"
15 #include "utilstrencodings.h"
16 
17 #include <openssl/x509.h>
18 #include <openssl/x509_vfy.h>
19 
20 #include <QFileOpenEvent>
21 #include <QTemporaryFile>
22 
parse_b64der_cert(const char * cert_data)23 X509 *parse_b64der_cert(const char* cert_data)
24 {
25     std::vector<unsigned char> data = DecodeBase64(cert_data);
26     assert(data.size() > 0);
27     const unsigned char* dptr = &data[0];
28     X509 *cert = d2i_X509(NULL, &dptr, data.size());
29     assert(cert);
30     return cert;
31 }
32 
33 //
34 // Test payment request handling
35 //
36 
handleRequest(PaymentServer * server,std::vector<unsigned char> & data)37 static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsigned char>& data)
38 {
39     RecipientCatcher sigCatcher;
40     QObject::connect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
41         &sigCatcher, SLOT(getRecipient(SendCoinsRecipient)));
42 
43     // Write data to a temp file:
44     QTemporaryFile f;
45     f.open();
46     f.write((const char*)&data[0], data.size());
47     f.close();
48 
49     // Create a QObject, install event filter from PaymentServer
50     // and send a file open event to the object
51     QObject object;
52     object.installEventFilter(server);
53     QFileOpenEvent event(f.fileName());
54     // If sending the event fails, this will cause sigCatcher to be empty,
55     // which will lead to a test failure anyway.
56     QCoreApplication::sendEvent(&object, &event);
57 
58     QObject::disconnect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
59         &sigCatcher, SLOT(getRecipient(SendCoinsRecipient)));
60 
61     // Return results from sigCatcher
62     return sigCatcher.recipient;
63 }
64 
paymentServerTests()65 void PaymentServerTests::paymentServerTests()
66 {
67     SelectParams(CBaseChainParams::MAIN);
68     OptionsModel optionsModel;
69     PaymentServer* server = new PaymentServer(NULL, false);
70     X509_STORE* caStore = X509_STORE_new();
71     X509_STORE_add_cert(caStore, parse_b64der_cert(caCert1_BASE64));
72     PaymentServer::LoadRootCAs(caStore);
73     server->setOptionsModel(&optionsModel);
74     server->uiReady();
75 
76     std::vector<unsigned char> data;
77     SendCoinsRecipient r;
78     QString merchant;
79 
80     // Now feed PaymentRequests to server, and observe signals it produces
81 
82     // This payment request validates directly against the
83     // caCert1 certificate authority:
84     data = DecodeBase64(paymentrequest1_cert1_BASE64);
85     r = handleRequest(server, data);
86     r.paymentRequest.getMerchant(caStore, merchant);
87     QCOMPARE(merchant, QString("testmerchant.org"));
88 
89     // Signed, but expired, merchant cert in the request:
90     data = DecodeBase64(paymentrequest2_cert1_BASE64);
91     r = handleRequest(server, data);
92     r.paymentRequest.getMerchant(caStore, merchant);
93     QCOMPARE(merchant, QString(""));
94 
95     // 10-long certificate chain, all intermediates valid:
96     data = DecodeBase64(paymentrequest3_cert1_BASE64);
97     r = handleRequest(server, data);
98     r.paymentRequest.getMerchant(caStore, merchant);
99     QCOMPARE(merchant, QString("testmerchant8.org"));
100 
101     // Long certificate chain, with an expired certificate in the middle:
102     data = DecodeBase64(paymentrequest4_cert1_BASE64);
103     r = handleRequest(server, data);
104     r.paymentRequest.getMerchant(caStore, merchant);
105     QCOMPARE(merchant, QString(""));
106 
107     // Validly signed, but by a CA not in our root CA list:
108     data = DecodeBase64(paymentrequest5_cert1_BASE64);
109     r = handleRequest(server, data);
110     r.paymentRequest.getMerchant(caStore, merchant);
111     QCOMPARE(merchant, QString(""));
112 
113     // Try again with no root CA's, verifiedMerchant should be empty:
114     caStore = X509_STORE_new();
115     PaymentServer::LoadRootCAs(caStore);
116     data = DecodeBase64(paymentrequest1_cert1_BASE64);
117     r = handleRequest(server, data);
118     r.paymentRequest.getMerchant(caStore, merchant);
119     QCOMPARE(merchant, QString(""));
120 
121     // Load second root certificate
122     caStore = X509_STORE_new();
123     X509_STORE_add_cert(caStore, parse_b64der_cert(caCert2_BASE64));
124     PaymentServer::LoadRootCAs(caStore);
125 
126     QByteArray byteArray;
127 
128     // For the tests below we just need the payment request data from
129     // paymentrequestdata.h parsed + stored in r.paymentRequest.
130     //
131     // These tests require us to bypass the following normal client execution flow
132     // shown below to be able to explicitly just trigger a certain condition!
133     //
134     // handleRequest()
135     // -> PaymentServer::eventFilter()
136     //   -> PaymentServer::handleURIOrFile()
137     //     -> PaymentServer::readPaymentRequestFromFile()
138     //       -> PaymentServer::processPaymentRequest()
139 
140     // Contains a testnet paytoaddress, so payment request network doesn't match client network:
141     data = DecodeBase64(paymentrequest1_cert2_BASE64);
142     byteArray = QByteArray((const char*)&data[0], data.size());
143     r.paymentRequest.parse(byteArray);
144     // Ensure the request is initialized, because network "main" is default, even for
145     // uninizialized payment requests and that will fail our test here.
146     QVERIFY(r.paymentRequest.IsInitialized());
147     QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false);
148 
149     // Expired payment request (expires is set to 1 = 1970-01-01 00:00:01):
150     data = DecodeBase64(paymentrequest2_cert2_BASE64);
151     byteArray = QByteArray((const char*)&data[0], data.size());
152     r.paymentRequest.parse(byteArray);
153     // Ensure the request is initialized
154     QVERIFY(r.paymentRequest.IsInitialized());
155     // compares 1 < GetTime() == false (treated as expired payment request)
156     QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), true);
157 
158     // Unexpired payment request (expires is set to 0x7FFFFFFFFFFFFFFF = max. int64_t):
159     // 9223372036854775807 (uint64), 9223372036854775807 (int64_t) and -1 (int32_t)
160     // -1 is 1969-12-31 23:59:59 (for a 32 bit time values)
161     data = DecodeBase64(paymentrequest3_cert2_BASE64);
162     byteArray = QByteArray((const char*)&data[0], data.size());
163     r.paymentRequest.parse(byteArray);
164     // Ensure the request is initialized
165     QVERIFY(r.paymentRequest.IsInitialized());
166     // compares 9223372036854775807 < GetTime() == false (treated as unexpired payment request)
167     QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), false);
168 
169     // Unexpired payment request (expires is set to 0x8000000000000000 > max. int64_t, allowed uint64):
170     // 9223372036854775808 (uint64), -9223372036854775808 (int64_t) and 0 (int32_t)
171     // 0 is 1970-01-01 00:00:00 (for a 32 bit time values)
172     data = DecodeBase64(paymentrequest4_cert2_BASE64);
173     byteArray = QByteArray((const char*)&data[0], data.size());
174     r.paymentRequest.parse(byteArray);
175     // Ensure the request is initialized
176     QVERIFY(r.paymentRequest.IsInitialized());
177     // compares -9223372036854775808 < GetTime() == true (treated as expired payment request)
178     QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), true);
179 
180     // Test BIP70 DoS protection:
181     unsigned char randData[BIP70_MAX_PAYMENTREQUEST_SIZE + 1];
182     GetRandBytes(randData, sizeof(randData));
183     // Write data to a temp file:
184     QTemporaryFile tempFile;
185     tempFile.open();
186     tempFile.write((const char*)randData, sizeof(randData));
187     tempFile.close();
188     // compares 50001 <= BIP70_MAX_PAYMENTREQUEST_SIZE == false
189     QCOMPARE(PaymentServer::verifySize(tempFile.size()), false);
190 
191     // Payment request with amount overflow (amount is set to 21000001 BTC):
192     data = DecodeBase64(paymentrequest5_cert2_BASE64);
193     byteArray = QByteArray((const char*)&data[0], data.size());
194     r.paymentRequest.parse(byteArray);
195     // Ensure the request is initialized
196     QVERIFY(r.paymentRequest.IsInitialized());
197     // Extract address and amount from the request
198     QList<std::pair<CScript, CAmount> > sendingTos = r.paymentRequest.getPayTo();
199     Q_FOREACH (const PAIRTYPE(CScript, CAmount)& sendingTo, sendingTos) {
200         CTxDestination dest;
201         if (ExtractDestination(sendingTo.first, dest))
202             QCOMPARE(PaymentServer::verifyAmount(sendingTo.second), false);
203     }
204 
205     delete server;
206 }
207 
getRecipient(SendCoinsRecipient r)208 void RecipientCatcher::getRecipient(SendCoinsRecipient r)
209 {
210     recipient = r;
211 }
212