Line data Source code
1 : // Copyright (c) 2009-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 : #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 :
23 2 : X509 *parse_b64der_cert(const char* cert_data)
24 : {
25 2 : std::vector<unsigned char> data = DecodeBase64(cert_data);
26 4 : assert(data.size() > 0);
27 2 : const unsigned char* dptr = &data[0];
28 4 : X509 *cert = d2i_X509(NULL, &dptr, data.size());
29 2 : assert(cert);
30 4 : return cert;
31 : }
32 :
33 : //
34 : // Test payment request handling
35 : //
36 :
37 6 : static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsigned char>& data)
38 : {
39 6 : RecipientCatcher sigCatcher;
40 : QObject::connect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
41 6 : &sigCatcher, SLOT(getRecipient(SendCoinsRecipient)));
42 :
43 : // Write data to a temp file:
44 12 : QTemporaryFile f;
45 : f.open();
46 12 : f.write((const char*)&data[0], data.size());
47 6 : f.close();
48 :
49 : // Create a QObject, install event filter from PaymentServer
50 : // and send a file open event to the object
51 12 : QObject object;
52 6 : object.installEventFilter(server);
53 12 : 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 6 : QCoreApplication::sendEvent(&object, &event);
57 :
58 : QObject::disconnect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
59 6 : &sigCatcher, SLOT(getRecipient(SendCoinsRecipient)));
60 :
61 : // Return results from sigCatcher
62 12 : return sigCatcher.recipient;
63 : }
64 :
65 1 : void PaymentServerTests::paymentServerTests()
66 : {
67 1 : SelectParams(CBaseChainParams::MAIN);
68 1 : OptionsModel optionsModel;
69 1 : PaymentServer* server = new PaymentServer(NULL, false);
70 1 : X509_STORE* caStore = X509_STORE_new();
71 1 : X509_STORE_add_cert(caStore, parse_b64der_cert(caCert1_BASE64));
72 1 : PaymentServer::LoadRootCAs(caStore);
73 1 : server->setOptionsModel(&optionsModel);
74 1 : server->uiReady();
75 :
76 : std::vector<unsigned char> data;
77 2 : SendCoinsRecipient r;
78 1 : 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 2 : data = DecodeBase64(paymentrequest1_cert1_BASE64);
85 1 : r = handleRequest(server, data);
86 1 : r.paymentRequest.getMerchant(caStore, merchant);
87 1 : QCOMPARE(merchant, QString("testmerchant.org"));
88 :
89 : // Signed, but expired, merchant cert in the request:
90 2 : data = DecodeBase64(paymentrequest2_cert1_BASE64);
91 1 : r = handleRequest(server, data);
92 1 : r.paymentRequest.getMerchant(caStore, merchant);
93 1 : QCOMPARE(merchant, QString(""));
94 :
95 : // 10-long certificate chain, all intermediates valid:
96 2 : data = DecodeBase64(paymentrequest3_cert1_BASE64);
97 1 : r = handleRequest(server, data);
98 1 : r.paymentRequest.getMerchant(caStore, merchant);
99 1 : QCOMPARE(merchant, QString("testmerchant8.org"));
100 :
101 : // Long certificate chain, with an expired certificate in the middle:
102 2 : data = DecodeBase64(paymentrequest4_cert1_BASE64);
103 1 : r = handleRequest(server, data);
104 1 : r.paymentRequest.getMerchant(caStore, merchant);
105 1 : QCOMPARE(merchant, QString(""));
106 :
107 : // Validly signed, but by a CA not in our root CA list:
108 2 : data = DecodeBase64(paymentrequest5_cert1_BASE64);
109 1 : r = handleRequest(server, data);
110 1 : r.paymentRequest.getMerchant(caStore, merchant);
111 1 : QCOMPARE(merchant, QString(""));
112 :
113 : // Try again with no root CA's, verifiedMerchant should be empty:
114 1 : caStore = X509_STORE_new();
115 1 : PaymentServer::LoadRootCAs(caStore);
116 2 : data = DecodeBase64(paymentrequest1_cert1_BASE64);
117 1 : r = handleRequest(server, data);
118 1 : r.paymentRequest.getMerchant(caStore, merchant);
119 1 : QCOMPARE(merchant, QString(""));
120 :
121 : // Load second root certificate
122 1 : caStore = X509_STORE_new();
123 1 : X509_STORE_add_cert(caStore, parse_b64der_cert(caCert2_BASE64));
124 1 : PaymentServer::LoadRootCAs(caStore);
125 :
126 1 : 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 2 : data = DecodeBase64(paymentrequest1_cert2_BASE64);
142 2 : byteArray = QByteArray((const char*)&data[0], data.size());
143 1 : 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 1 : QVERIFY(r.paymentRequest.IsInitialized());
147 1 : QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false);
148 :
149 : // Expired payment request (expires is set to 1 = 1970-01-01 00:00:01):
150 2 : data = DecodeBase64(paymentrequest2_cert2_BASE64);
151 2 : byteArray = QByteArray((const char*)&data[0], data.size());
152 1 : r.paymentRequest.parse(byteArray);
153 : // Ensure the request is initialized
154 1 : QVERIFY(r.paymentRequest.IsInitialized());
155 : // compares 1 < GetTime() == false (treated as expired payment request)
156 1 : 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 2 : data = DecodeBase64(paymentrequest3_cert2_BASE64);
162 2 : byteArray = QByteArray((const char*)&data[0], data.size());
163 1 : r.paymentRequest.parse(byteArray);
164 : // Ensure the request is initialized
165 1 : QVERIFY(r.paymentRequest.IsInitialized());
166 : // compares 9223372036854775807 < GetTime() == false (treated as unexpired payment request)
167 1 : 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 2 : data = DecodeBase64(paymentrequest4_cert2_BASE64);
173 2 : byteArray = QByteArray((const char*)&data[0], data.size());
174 1 : r.paymentRequest.parse(byteArray);
175 : // Ensure the request is initialized
176 1 : QVERIFY(r.paymentRequest.IsInitialized());
177 : // compares -9223372036854775808 < GetTime() == true (treated as expired payment request)
178 1 : QCOMPARE(PaymentServer::verifyExpired(r.paymentRequest.getDetails()), true);
179 :
180 : // Test BIP70 DoS protection:
181 1 : unsigned char randData[BIP70_MAX_PAYMENTREQUEST_SIZE + 1];
182 1 : GetRandBytes(randData, sizeof(randData));
183 : // Write data to a temp file:
184 2 : QTemporaryFile tempFile;
185 : tempFile.open();
186 1 : tempFile.write((const char*)randData, sizeof(randData));
187 1 : tempFile.close();
188 : // compares 50001 <= BIP70_MAX_PAYMENTREQUEST_SIZE == false
189 1 : QCOMPARE(PaymentServer::verifySize(tempFile.size()), false);
190 :
191 : // Payment request with amount overflow (amount is set to 21000001 BTC):
192 2 : data = DecodeBase64(paymentrequest5_cert2_BASE64);
193 2 : byteArray = QByteArray((const char*)&data[0], data.size());
194 1 : r.paymentRequest.parse(byteArray);
195 : // Ensure the request is initialized
196 1 : QVERIFY(r.paymentRequest.IsInitialized());
197 : // Extract address and amount from the request
198 2 : QList<std::pair<CScript, CAmount> > sendingTos = r.paymentRequest.getPayTo();
199 7 : Q_FOREACH (const PAIRTYPE(CScript, CAmount)& sendingTo, sendingTos) {
200 : CTxDestination dest;
201 1 : if (ExtractDestination(sendingTo.first, dest))
202 1 : QCOMPARE(PaymentServer::verifyAmount(sendingTo.second), false);
203 : }
204 :
205 2 : delete server;
206 : }
207 :
208 6 : void RecipientCatcher::getRecipient(SendCoinsRecipient r)
209 : {
210 6 : recipient = r;
211 9 : }
|