Ignore parameters in PKL type strings when checking them.
[libdcp.git] / test / certificates_test.cc
1 /*
2     Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34 #include "certificate.h"
35 #include "certificate_chain.h"
36 #include "util.h"
37 #include "exceptions.h"
38 #include "test.h"
39 #include <boost/test/unit_test.hpp>
40 #include <iostream>
41
42 using std::list;
43 using std::string;
44 using std::shared_ptr;
45
46 /** Check that loading certificates from files via strings works */
47 BOOST_AUTO_TEST_CASE (certificates1)
48 {
49         dcp::CertificateChain c;
50
51         c.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
52         c.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
53         c.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
54
55         dcp::CertificateChain::List leaf_to_root = c.leaf_to_root ();
56
57         dcp::CertificateChain::List::iterator i = leaf_to_root.begin ();
58
59         /* Leaf */
60         BOOST_CHECK_EQUAL (*i, c.leaf ());
61         BOOST_CHECK_EQUAL (i->thumbprint(), "EZg5wDcihccWqwdg59Y8D+IJpYM=");
62
63         BOOST_CHECK_EQUAL (
64                 c.leaf().issuer(),
65                 "dnQualifier=6eat8r33US71avuQEojmH\\+bjk84=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
66                 );
67
68         BOOST_CHECK_EQUAL (
69                 c.leaf().subject(),
70                 "dnQualifier=QFVlym7fuql6bPOnY38aaO1ZPW4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
71                 );
72
73         ++i;
74
75         /* Intermediate */
76         BOOST_CHECK_EQUAL (i->thumbprint(), "GwM6ex2UVlWclH8f1uV7W1n0EEU=");
77         BOOST_CHECK_EQUAL (
78                 i->issuer(),
79                 "dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
80                 );
81
82         BOOST_CHECK_EQUAL (
83                 i->subject(),
84                 "dnQualifier=6eat8r33US71avuQEojmH\\+bjk84=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
85                 );
86
87         ++i;
88
89         /* Root */
90         BOOST_CHECK_EQUAL (*i, c.root ());
91         BOOST_CHECK_EQUAL (i->thumbprint(), "zU8NVNwI2PYejmSYRntG7c6sdTw=");
92         BOOST_CHECK_EQUAL (
93                 c.root().issuer(),
94                 "dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
95                 );
96
97         BOOST_CHECK_EQUAL (c.root().serial(), "5");
98
99         BOOST_CHECK_EQUAL (
100                 c.root().subject(),
101                 "dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
102                 );
103
104         /* Check that reconstruction from a string works */
105         dcp::Certificate test (c.root().certificate (true));
106         BOOST_CHECK_EQUAL (test.certificate(), c.root().certificate());
107 }
108
109 /** Check some more certificate-from-strings */
110 BOOST_AUTO_TEST_CASE (certificates2)
111 {
112         {
113                 dcp::Certificate c (dcp::file_to_string (private_test / "CA.GDC-TECH.COM_SA2100_A14903.crt.crt"));
114                 BOOST_CHECK_EQUAL (c.certificate(true), dcp::file_to_string (private_test / "CA.GDC-TECH.COM_SA2100_A14903.crt.crt.reformatted"));
115         }
116
117         {
118                 dcp::Certificate c (dcp::file_to_string (private_test / "usl-cert.pem"));
119                 BOOST_CHECK_EQUAL (c.certificate(true), dcp::file_to_string (private_test / "usl-cert.pem.trimmed"));
120         }
121
122         {
123                 /* This is a chain, not an individual certificate, so it should throw an exception */
124                 BOOST_CHECK_THROW (dcp::Certificate (dcp::file_to_string (private_test / "chain.pem")), dcp::MiscError);
125         }
126
127         BOOST_CHECK_THROW (dcp::Certificate (dcp::file_to_string (private_test / "no-begin.pem")), dcp::MiscError);
128         BOOST_CHECK_THROW (dcp::Certificate ("foo"), dcp::MiscError);
129 }
130
131 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
132 BOOST_AUTO_TEST_CASE (certificates_validation1)
133 {
134         dcp::CertificateChain good;
135         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
136         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
137         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
138         BOOST_CHECK (good.chain_valid(good._certificates));
139 }
140
141 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
142 BOOST_AUTO_TEST_CASE (certificates_validation2)
143 {
144         dcp::CertificateChain good;
145         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
146         BOOST_CHECK (good.chain_valid(good._certificates));
147 }
148
149 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
150 BOOST_AUTO_TEST_CASE (certificates_validation3)
151 {
152         dcp::CertificateChain bad;
153         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
154         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
155         BOOST_CHECK (!bad.chain_valid(bad._certificates));
156         BOOST_CHECK_THROW (bad.root_to_leaf(), dcp::CertificateChainError);
157 }
158
159 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
160 BOOST_AUTO_TEST_CASE (certificates_validation4)
161 {
162         dcp::CertificateChain bad;
163         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
164         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
165         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
166         BOOST_CHECK (!bad.chain_valid(bad._certificates));
167         BOOST_CHECK_NO_THROW (bad.root_to_leaf());
168 }
169
170 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
171 BOOST_AUTO_TEST_CASE (certificates_validation5)
172 {
173         dcp::CertificateChain bad;
174         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
175         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
176         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
177         BOOST_CHECK (!bad.chain_valid(bad._certificates));
178         BOOST_CHECK_NO_THROW (bad.root_to_leaf());
179 }
180
181 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
182 BOOST_AUTO_TEST_CASE (certificates_validation6)
183 {
184         dcp::CertificateChain bad;
185         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
186         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
187         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
188         BOOST_CHECK (!bad.chain_valid(bad._certificates));
189         BOOST_CHECK_NO_THROW (bad.root_to_leaf());
190 }
191
192 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
193 BOOST_AUTO_TEST_CASE (certificates_validation7)
194 {
195         dcp::CertificateChain bad;
196         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
197         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
198         BOOST_CHECK (!bad.chain_valid(bad._certificates));
199         BOOST_CHECK_THROW (bad.root_to_leaf(), dcp::CertificateChainError);
200 }
201
202 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
203 BOOST_AUTO_TEST_CASE (certificates_validation8)
204 {
205         dcp::CertificateChain bad;
206         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
207         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
208         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
209         BOOST_CHECK (!bad.chain_valid(bad._certificates));
210         BOOST_CHECK_THROW (bad.root_to_leaf(), dcp::CertificateChainError);
211 }
212
213 /** Check that we can create a valid chain */
214 BOOST_AUTO_TEST_CASE (certificates_validation9)
215 {
216         dcp::CertificateChain good (
217                 boost::filesystem::path ("openssl"),
218                 "dcpomatic.com",
219                 "dcpomatic.com",
220                 ".dcpomatic.smpte-430-2.ROOT",
221                 ".dcpomatic.smpte-430-2.INTERMEDIATE",
222                 "CS.dcpomatic.smpte-430-2.LEAF"
223                 );
224
225         BOOST_CHECK_NO_THROW (good.root_to_leaf());
226 }
227
228 /** Check that we can create a valid chain */
229 BOOST_AUTO_TEST_CASE (certificates_validation10)
230 {
231         dcp::CertificateChain good (boost::filesystem::path ("openssl"));
232         BOOST_CHECK_NO_THROW (good.root_to_leaf());
233 }
234
235 /** Check that dcp::Signer::valid() basically works */
236 BOOST_AUTO_TEST_CASE (signer_validation)
237 {
238         /* Check a valid signer */
239         dcp::CertificateChain chain;
240         chain.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
241         chain.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
242         chain.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
243         chain.set_key (dcp::file_to_string ("test/ref/crypt/leaf.key"));
244         BOOST_CHECK (chain.valid ());
245
246         /* Put in an unrelated key and the signer should no longer be valid */
247         dcp::CertificateChain another_chain (boost::filesystem::path ("openssl"));
248         chain.set_key (another_chain.key().get ());
249         BOOST_CHECK (!chain.valid ());
250 }
251
252 /** Check reading of a certificate chain from a string */
253 BOOST_AUTO_TEST_CASE (certificate_chain_from_string)
254 {
255         dcp::CertificateChain a (dcp::file_to_string (private_test / "chain.pem"));
256         BOOST_CHECK_EQUAL (a.root_to_leaf().size(), 3);
257
258         dcp::CertificateChain b (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem"));
259         BOOST_CHECK_EQUAL (b.root_to_leaf().size(), 1);
260 }
261
262 /** Check not_before and not_after */
263 BOOST_AUTO_TEST_CASE (certificate_not_before_after)
264 {
265         dcp::Certificate c (dcp::file_to_string("test/ref/crypt/ca.self-signed.pem"));
266         struct tm not_before = c.not_before();
267         BOOST_CHECK_EQUAL (not_before.tm_sec, 8);
268         BOOST_CHECK_EQUAL (not_before.tm_min, 20);
269         BOOST_CHECK_EQUAL (not_before.tm_hour, 13);
270         BOOST_CHECK_EQUAL (not_before.tm_mday, 5);
271         BOOST_CHECK_EQUAL (not_before.tm_mon, 5);
272         BOOST_CHECK_EQUAL (not_before.tm_year, 115);
273         struct tm not_after = c.not_after();
274         BOOST_CHECK_EQUAL (not_after.tm_sec, 8);
275         BOOST_CHECK_EQUAL (not_after.tm_min, 20);
276         BOOST_CHECK_EQUAL (not_after.tm_hour, 13);
277         BOOST_CHECK_EQUAL (not_after.tm_mday, 2);
278         BOOST_CHECK_EQUAL (not_after.tm_mon, 5);
279         BOOST_CHECK_EQUAL (not_after.tm_year, 125);
280 }