-
Notifications
You must be signed in to change notification settings - Fork 37
/
paymentrequest-dump.cpp
169 lines (139 loc) · 5.8 KB
/
paymentrequest-dump.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//
// Verify a PaymentRequest, and dump it out in human-readable form
//
// Apple has deprecated OpenSSL in latest MacOS, shut up compiler warnings about it.
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <string>
#include <fstream>
#include <assert.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include "paymentrequest.pb.h"
#include "util.h"
using std::string;
using std::map;
using namespace payments;
// Take binary DER data and return an X509 object suitable for verification or use.
X509 *parse_der_cert(string cert_data) {
const unsigned char *data = (const unsigned char *)cert_data.data();
X509 *cert = d2i_X509(NULL, &data, cert_data.size());
assert(cert);
return cert;
}
int main(int argc, char **argv) {
std::list<string> expected = split("rootcertificates,in", ",");
map<string,string> params;
if (!parse_command_line(argc, argv, expected, params)) {
usage(expected);
exit(1);
}
if (params["rootcertificates"].empty()) {
// Use the certificate-authority-in-a-box root cert by default:
params["rootcertificates"] = string("ca_in_a_box/certs/cacert.pem");
}
SSL_library_init();
ERR_load_BIO_strings();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
// Load all the cert authorities.
// The cert store will have all of the certificates for the trusted root authorities.
X509_STORE *cert_store = X509_STORE_new();
// This tells the store to find certificates by loading them from a file.
// The X509_LOOKUP_file() method here is actually just telling OpenSSL
// how to do it, it doesn't actually look anything up despite the name.
X509_LOOKUP *lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file());
assert(X509_LOOKUP_load_file(lookup, params["rootcertificates"].c_str(), X509_FILETYPE_PEM));
// Load the paymentrequest file from stdin
PaymentRequest request;
if (!request.ParseFromIstream(&std::cin)) {
exit(1);
}
// Dump in raw text format, obviously this is mostly useless as bulk of
// the data is binary.
// printf("%s\n", request.DebugString().c_str());
// Load the certs from the paymentrequest.
PaymentDetails details;
assert(details.ParseFromString(request.serialized_payment_details()));
const EVP_MD* digestAlgorithm = NULL;
// pki_type == "None" : just dump details:
if (request.pki_type() == "None") {
printf("Unsigned payment request.\n");
printf("PaymentRequest details:\n%s\n", details.DebugString().c_str());
exit(0);
}
else if (request.pki_type() == "x509+sha256") {
digestAlgorithm = EVP_sha256();
}
else if (request.pki_type() == "x509+sha1") {
digestAlgorithm = EVP_sha1();
}
else {
printf("Unknown pki_type: %s\n", request.pki_type().c_str());
}
X509Certificates certChain;
assert(certChain.ParseFromString(request.pki_data()));
std::vector<X509*> certs;
for (int i = 0; i < certChain.certificate_size(); i++) {
X509 *cert = parse_der_cert(certChain.certificate(i));
certs.push_back(cert);
}
assert(certs.size() > 0);
// The first cert is the signing cert, the rest are untrusted certs that chain
// to a valid root authority. OpenSSL needs them separately.
STACK_OF(X509) *chain = sk_X509_new_null();
for (int i = certs.size()-1; i > 0; i--) {
sk_X509_push(chain, certs[i]);
}
X509 *signing_cert = certs[0];
// Now create a "store context", which is a single use object for checking,
// load the signing cert into it and verify.
X509_STORE_CTX *store_ctx = X509_STORE_CTX_new();
assert(X509_STORE_CTX_init(store_ctx, cert_store, signing_cert, chain));
// Now do the verification!
int result = X509_verify_cert(store_ctx);
X509_NAME *certname = X509_get_subject_name(signing_cert);
if (result != 1) {
int error = X509_STORE_CTX_get_error(store_ctx);
printf("%d: %s\n", error, X509_verify_cert_error_string(error));
exit(1);
}
// The cert is valid. Now we need to check the signature.
string signature = request.signature();
string data_to_verify; // Everything but the signature
request.set_signature(string(""));
request.SerializeToString(&data_to_verify);
request.set_signature(signature);
EVP_MD_CTX ctx;
EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
EVP_MD_CTX_init(&ctx);
if (!EVP_VerifyInit_ex(&ctx, digestAlgorithm, NULL) ||
!EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size()) ||
!EVP_VerifyFinal(&ctx, (const unsigned char*)signature.data(), signature.size(), pubkey)) {
printf("Bad signature, invalid PaymentRequest.\n");
}
else {
// OpenSSL API for getting human printable strings from certs is baroque.
int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
char *website = new char[textlen + 1];
if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) {
printf("PaymentRequest is valid! Signed by %s\n", website);
}
else {
printf("Bad certificate, missing common name\n");
}
delete[] website;
}
printf("PaymentRequest details:\n%s\n", details.DebugString().c_str());
// Avoid reported memory leaks.
X509_STORE_CTX_free(store_ctx);
for (unsigned int i = 0; i < certs.size(); i++)
X509_free(certs[i]);
X509_STORE_free(cert_store);
google::protobuf::ShutdownProtobufLibrary();
}