Make certificate chain validity a parameter of the constructor.
[libdcp.git] / test / certificates_test.cc
1 /*
2     Copyright (C) 2012 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
20 #include "certificate.h"
21 #include "certificate_chain.h"
22 #include "util.h"
23 #include "exceptions.h"
24 #include "test.h"
25 #include <boost/test/unit_test.hpp>
26 #include <iostream>
27
28 using std::list;
29 using std::string;
30 using boost::shared_ptr;
31
32 /** Check that loading certificates from files via strings works */
33 BOOST_AUTO_TEST_CASE (certificates1)
34 {
35         dcp::CertificateChain c;
36
37         c.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
38         c.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
39         c.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
40
41         dcp::CertificateChain::List leaf_to_root = c.leaf_to_root ();
42
43         dcp::CertificateChain::List::iterator i = leaf_to_root.begin ();
44
45         /* Leaf */
46         BOOST_CHECK_EQUAL (*i, c.leaf ());
47         BOOST_CHECK_EQUAL (i->thumbprint(), "EZg5wDcihccWqwdg59Y8D+IJpYM=");
48
49         BOOST_CHECK_EQUAL (
50                 c.leaf().issuer(),
51                 "dnQualifier=6eat8r33US71avuQEojmH\\+bjk84=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
52                 );
53
54         BOOST_CHECK_EQUAL (
55                 c.leaf().subject(),
56                 "dnQualifier=QFVlym7fuql6bPOnY38aaO1ZPW4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
57                 );
58
59         ++i;
60
61         /* Intermediate */
62         BOOST_CHECK_EQUAL (i->thumbprint(), "GwM6ex2UVlWclH8f1uV7W1n0EEU=");
63         BOOST_CHECK_EQUAL (
64                 i->issuer(),
65                 "dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
66                 );
67
68         BOOST_CHECK_EQUAL (
69                 i->subject(),
70                 "dnQualifier=6eat8r33US71avuQEojmH\\+bjk84=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
71                 );
72
73         ++i;
74
75         /* Root */
76         BOOST_CHECK_EQUAL (*i, c.root ());
77         BOOST_CHECK_EQUAL (i->thumbprint(), "zU8NVNwI2PYejmSYRntG7c6sdTw=");
78         BOOST_CHECK_EQUAL (
79                 c.root().issuer(),
80                 "dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
81                 );
82
83         BOOST_CHECK_EQUAL (c.root().serial(), "5");
84
85         BOOST_CHECK_EQUAL (
86                 c.root().subject(),
87                 "dnQualifier=DCnRdHFbcv4ANVUq2\\+wMVALFSec=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
88                 );
89
90         /* Check that reconstruction from a string works */
91         dcp::Certificate test (c.root().certificate (true));
92         BOOST_CHECK_EQUAL (test.certificate(), c.root().certificate());
93 }
94
95 /** Check some more certificate-from-strings */
96 BOOST_AUTO_TEST_CASE (certificates2)
97 {
98         {
99                 dcp::Certificate c (dcp::file_to_string (private_test / "CA.GDC-TECH.COM_SA2100_A14903.crt.crt"));
100                 BOOST_CHECK_EQUAL (c.certificate(true), dcp::file_to_string (private_test / "CA.GDC-TECH.COM_SA2100_A14903.crt.crt.reformatted"));
101         }
102
103         {
104                 dcp::Certificate c (dcp::file_to_string (private_test / "usl-cert.pem"));
105                 BOOST_CHECK_EQUAL (c.certificate(true), dcp::file_to_string (private_test / "usl-cert.pem.trimmed"));
106         }
107
108         {
109                 /* This is a chain, not an individual certificate, so it should throw an exception */
110                 BOOST_CHECK_THROW (dcp::Certificate (dcp::file_to_string (private_test / "chain.pem")), dcp::MiscError);
111         }
112
113         BOOST_CHECK_THROW (dcp::Certificate (dcp::file_to_string (private_test / "no-begin.pem")), dcp::MiscError);
114         BOOST_CHECK_THROW (dcp::Certificate ("foo"), dcp::MiscError);
115 }
116
117 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
118 BOOST_AUTO_TEST_CASE (certificates_validation1)
119 {
120         dcp::CertificateChain good;
121         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
122         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
123         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
124         BOOST_CHECK (good.chain_valid(good._certificates));
125 }
126
127 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
128 BOOST_AUTO_TEST_CASE (certificates_validation2)
129 {
130         dcp::CertificateChain good;
131         good.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
132         BOOST_CHECK (good.chain_valid(good._certificates));
133 }
134
135 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
136 BOOST_AUTO_TEST_CASE (certificates_validation3)
137 {
138         dcp::CertificateChain bad;
139         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
140         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
141         BOOST_CHECK (!bad.chain_valid(bad._certificates));
142         BOOST_CHECK_THROW (bad.root_to_leaf(), dcp::CertificateChainError);
143 }
144
145 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
146 BOOST_AUTO_TEST_CASE (certificates_validation4)
147 {
148         dcp::CertificateChain bad;
149         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
150         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
151         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
152         BOOST_CHECK (!bad.chain_valid(bad._certificates));
153         BOOST_CHECK_NO_THROW (bad.root_to_leaf());
154 }
155
156 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
157 BOOST_AUTO_TEST_CASE (certificates_validation5)
158 {
159         dcp::CertificateChain bad;
160         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
161         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
162         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
163         BOOST_CHECK (!bad.chain_valid(bad._certificates));
164         BOOST_CHECK_NO_THROW (bad.root_to_leaf());
165 }
166
167 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
168 BOOST_AUTO_TEST_CASE (certificates_validation6)
169 {
170         dcp::CertificateChain bad;
171         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
172         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
173         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
174         BOOST_CHECK (!bad.chain_valid(bad._certificates));
175         BOOST_CHECK_NO_THROW (bad.root_to_leaf());
176 }
177
178 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
179 BOOST_AUTO_TEST_CASE (certificates_validation7)
180 {
181         dcp::CertificateChain bad;
182         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
183         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
184         BOOST_CHECK (!bad.chain_valid(bad._certificates));
185         BOOST_CHECK_THROW (bad.root_to_leaf(), dcp::CertificateChainError);
186 }
187
188 /** Check that dcp::CertificateChain::chain_valid() and ::root_to_leaf() basically work */
189 BOOST_AUTO_TEST_CASE (certificates_validation8)
190 {
191         dcp::CertificateChain bad;
192         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
193         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
194         bad.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
195         BOOST_CHECK (!bad.chain_valid(bad._certificates));
196         BOOST_CHECK_THROW (bad.root_to_leaf(), dcp::CertificateChainError);
197 }
198
199 /** Check that we can create a valid chain */
200 BOOST_AUTO_TEST_CASE (certificates_validation9)
201 {
202         dcp::CertificateChain good (
203                 boost::filesystem::path ("openssl"),
204                 10 * 365,
205                 "dcpomatic.com",
206                 "dcpomatic.com",
207                 ".dcpomatic.smpte-430-2.ROOT",
208                 ".dcpomatic.smpte-430-2.INTERMEDIATE",
209                 "CS.dcpomatic.smpte-430-2.LEAF"
210                 );
211
212         BOOST_CHECK_NO_THROW (good.root_to_leaf());
213 }
214
215 /** Check that we can create a valid chain */
216 BOOST_AUTO_TEST_CASE (certificates_validation10)
217 {
218         dcp::CertificateChain good (boost::filesystem::path ("openssl"), 10 * 365);
219         BOOST_CHECK_NO_THROW (good.root_to_leaf());
220 }
221
222 /** Check that dcp::Signer::valid() basically works */
223 BOOST_AUTO_TEST_CASE (signer_validation)
224 {
225         /* Check a valid signer */
226         dcp::CertificateChain chain;
227         chain.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/ca.self-signed.pem")));
228         chain.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/intermediate.signed.pem")));
229         chain.add (dcp::Certificate (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem")));
230         chain.set_key (dcp::file_to_string ("test/ref/crypt/leaf.key"));
231         BOOST_CHECK (chain.valid ());
232
233         /* Put in an unrelated key and the signer should no longer be valid */
234         dcp::CertificateChain another_chain (boost::filesystem::path ("openssl"), 10 * 365);
235         chain.set_key (another_chain.key().get ());
236         BOOST_CHECK (!chain.valid ());
237 }
238
239 /** Check reading of a certificate chain from a string */
240 BOOST_AUTO_TEST_CASE (certificate_chain_from_string)
241 {
242         dcp::CertificateChain a (dcp::file_to_string (private_test / "chain.pem"), 10 * 365);
243         BOOST_CHECK_EQUAL (a.root_to_leaf().size(), 3);
244
245         dcp::CertificateChain b (dcp::file_to_string ("test/ref/crypt/leaf.signed.pem"), 10 * 365);
246         BOOST_CHECK_EQUAL (b.root_to_leaf().size(), 1);
247 }