Fix/hide some warnings.
[libdcp.git] / test / kdm_test.cc
1 /*
2     Copyright (C) 2013-2021 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
35 #include "certificate_chain.h"
36 #include "cpl.h"
37 #include "decrypted_kdm.h"
38 #include "encrypted_kdm.h"
39 #include "mono_picture_asset.h"
40 #include "picture_asset_writer.h"
41 #include "reel.h"
42 #include "reel_mono_picture_asset.h"
43 #include "test.h"
44 #include "types.h"
45 #include "util.h"
46 #include "warnings.h"
47 #include <libcxml/cxml.h>
48 LIBDCP_DISABLE_WARNINGS
49 #include <libxml++/libxml++.h>
50 LIBDCP_ENABLE_WARNINGS
51 #include <boost/test/unit_test.hpp>
52
53
54 using std::list;
55 using std::string;
56 using std::vector;
57 using std::make_shared;
58 using std::shared_ptr;
59 using boost::optional;
60
61
62 /** Check reading and decryption of a KDM */
63 BOOST_AUTO_TEST_CASE (kdm_test)
64 {
65         dcp::DecryptedKDM kdm (
66                 dcp::EncryptedKDM (
67                         dcp::file_to_string ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml")
68                         ),
69                 dcp::file_to_string ("test/data/private.key")
70                 );
71
72         auto keys = kdm.keys ();
73
74         BOOST_CHECK_EQUAL (keys.size(), 2);
75
76         BOOST_CHECK_EQUAL (keys.front().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
77         BOOST_CHECK_EQUAL (keys.front().id(), "4ac4f922-8239-4831-b23b-31426d0542c4");
78         BOOST_CHECK_EQUAL (keys.front().key().hex(), "8a2729c3e5b65c45d78305462104c3fb");
79
80         BOOST_CHECK_EQUAL (keys.back().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
81         BOOST_CHECK_EQUAL (keys.back().id(), "73baf5de-e195-4542-ab28-8a465f7d4079");
82         BOOST_CHECK_EQUAL (keys.back().key().hex(), "5327fb7ec2e807bd57059615bf8a169d");
83 }
84
85
86 /** Check that we can read in a KDM and then write it back out again the same */
87 BOOST_AUTO_TEST_CASE (kdm_passthrough_test)
88 {
89         dcp::EncryptedKDM kdm (
90                 dcp::file_to_string ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml")
91                 );
92
93         auto parser = make_shared<xmlpp::DomParser>();
94         parser->parse_memory (kdm.as_xml ());
95         parser->get_document()->write_to_file_formatted ("build/kdm.xml", "UTF-8");
96         check_xml (
97                 dcp::file_to_string("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
98                 dcp::file_to_string("build/kdm.xml"),
99                 {},
100                 true
101                 );
102 }
103
104
105 /** Test some of the utility methods of DecryptedKDM */
106 BOOST_AUTO_TEST_CASE (decrypted_kdm_test)
107 {
108         auto data = new uint8_t[16];
109         auto p = data;
110         dcp::DecryptedKDM::put_uuid (&p, "8971c838-d0c3-405d-bc57-43afa9d91242");
111
112         BOOST_CHECK_EQUAL (data[0], 0x89);
113         BOOST_CHECK_EQUAL (data[1], 0x71);
114         BOOST_CHECK_EQUAL (data[2], 0xc8);
115         BOOST_CHECK_EQUAL (data[3], 0x38);
116         BOOST_CHECK_EQUAL (data[4], 0xd0);
117         BOOST_CHECK_EQUAL (data[5], 0xc3);
118         BOOST_CHECK_EQUAL (data[6], 0x40);
119         BOOST_CHECK_EQUAL (data[7], 0x5d);
120         BOOST_CHECK_EQUAL (data[8], 0xbc);
121         BOOST_CHECK_EQUAL (data[9], 0x57);
122         BOOST_CHECK_EQUAL (data[10], 0x43);
123         BOOST_CHECK_EQUAL (data[11], 0xaf);
124         BOOST_CHECK_EQUAL (data[12], 0xa9);
125         BOOST_CHECK_EQUAL (data[13], 0xd9);
126         BOOST_CHECK_EQUAL (data[14], 0x12);
127         BOOST_CHECK_EQUAL (data[15], 0x42);
128
129         p = data;
130         BOOST_CHECK_EQUAL (dcp::DecryptedKDM::get_uuid (&p), "8971c838-d0c3-405d-bc57-43afa9d91242");
131
132         delete[] data;
133 }
134
135
136 /** Check that <KeyType> tags have the scope attribute.
137  *  Wolfgang Woehl believes this is compulsory and I am more-or-less inclined to agree.
138  */
139 BOOST_AUTO_TEST_CASE (kdm_key_type_scope)
140 {
141         dcp::EncryptedKDM kdm (
142                 dcp::file_to_string ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml")
143                 );
144
145         cxml::Document doc;
146         doc.read_string (kdm.as_xml ());
147
148         auto typed_key_ids = doc.node_child("AuthenticatedPublic")->
149                 node_child("RequiredExtensions")->
150                 node_child("KDMRequiredExtensions")->
151                 node_child("KeyIdList")->
152                 node_children("TypedKeyId");
153
154         for (auto i: typed_key_ids) {
155                 for (auto j: i->node_children("KeyType")) {
156                         BOOST_CHECK (j->string_attribute("scope") == "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
157                 }
158         }
159 }
160
161
162 static cxml::ConstNodePtr
163 kdm_forensic_test (cxml::Document& doc, bool picture, optional<int> audio)
164 {
165         dcp::DecryptedKDM decrypted (
166                 dcp::EncryptedKDM (
167                         dcp::file_to_string ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml")
168                         ),
169                 dcp::file_to_string ("test/data/private.key")
170                 );
171
172         auto signer = make_shared<dcp::CertificateChain>(dcp::file_to_string("test/data/certificate_chain"));
173         signer->set_key(dcp::file_to_string("test/data/private.key"));
174
175         dcp::EncryptedKDM kdm = decrypted.encrypt (
176                 signer, signer->leaf(), vector<string>(), dcp::Formulation::MODIFIED_TRANSITIONAL_1, picture, audio
177                 );
178
179         /* Check that we can pass this through correctly */
180         BOOST_CHECK_EQUAL (kdm.as_xml(), dcp::EncryptedKDM(kdm.as_xml()).as_xml());
181
182         doc.read_string (kdm.as_xml());
183
184         return doc.node_child("AuthenticatedPublic")->
185                 node_child("RequiredExtensions")->
186                 node_child("KDMRequiredExtensions")->
187                 optional_node_child("ForensicMarkFlagList");
188 }
189
190
191 /** Check ForensicMarkFlagList handling: disable picture and all audio */
192 BOOST_AUTO_TEST_CASE (kdm_forensic_test1)
193 {
194         cxml::Document doc;
195         auto forensic = kdm_forensic_test(doc, true, 0);
196         BOOST_REQUIRE (forensic);
197         auto flags = forensic->node_children("ForensicMarkFlag");
198         BOOST_REQUIRE_EQUAL (flags.size(), 2);
199         BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
200         BOOST_CHECK_EQUAL (flags.back()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
201 }
202
203
204 /** Check ForensicMarkFlagList handling: disable picture but not audio */
205 BOOST_AUTO_TEST_CASE (kdm_forensic_test2)
206 {
207         cxml::Document doc;
208         auto forensic = kdm_forensic_test(doc, true, optional<int>());
209         BOOST_REQUIRE (forensic);
210         auto flags = forensic->node_children("ForensicMarkFlag");
211         BOOST_REQUIRE_EQUAL (flags.size(), 1);
212         BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
213 }
214
215
216 /** Check ForensicMarkFlagList handling: disable audio but not picture */
217 BOOST_AUTO_TEST_CASE (kdm_forensic_test3)
218 {
219         cxml::Document doc;
220         auto forensic = kdm_forensic_test(doc, false, 0);
221         BOOST_REQUIRE (forensic);
222         auto flags = forensic->node_children("ForensicMarkFlag");
223         BOOST_REQUIRE_EQUAL (flags.size(), 1);
224         BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
225 }
226
227
228 /** Check ForensicMarkFlagList handling: disable picture and audio above channel 3 */
229 BOOST_AUTO_TEST_CASE (kdm_forensic_test4)
230 {
231         cxml::Document doc;
232         auto forensic = kdm_forensic_test(doc, true, 3);
233         BOOST_REQUIRE (forensic);
234         auto flags = forensic->node_children("ForensicMarkFlag");
235         BOOST_REQUIRE_EQUAL (flags.size(), 2);
236         BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
237         BOOST_CHECK_EQUAL (flags.back()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable-above-channel-3");
238 }
239
240
241 /** Check ForensicMarkFlagList handling: disable neither */
242 BOOST_AUTO_TEST_CASE (kdm_forensic_test5)
243 {
244         cxml::Document doc;
245         auto forensic = kdm_forensic_test(doc, false, optional<int>());
246         BOOST_CHECK (!forensic);
247 }
248
249
250 /** Check that KDM validity periods are checked for being within the certificate validity */
251 BOOST_AUTO_TEST_CASE (validity_period_test1)
252 {
253         auto signer = make_shared<dcp::CertificateChain>(dcp::file_to_string("test/data/certificate_chain"));
254         signer->set_key(dcp::file_to_string("test/data/private.key"));
255
256         auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE);
257         asset->set_key (dcp::Key());
258         auto writer = asset->start_write ("build/test/validity_period_test1.mxf", false);
259         dcp::ArrayData frame ("test/data/flat_red.j2c");
260         writer->write (frame.data(), frame.size());
261         auto reel = make_shared<dcp::Reel>();
262         reel->add(make_shared<dcp::ReelMonoPictureAsset>(asset, 0));
263         auto cpl = make_shared<dcp::CPL>("test", dcp::ContentKind::FEATURE, dcp::Standard::SMPTE);
264         cpl->add(reel);
265
266         /* This certificate_chain is valid from 26/12/2012 to 24/12/2022 */
267
268         /* Inside */
269         BOOST_CHECK_NO_THROW(
270                 dcp::DecryptedKDM(
271                         cpl, dcp::Key(dcp::file_to_string("test/data/private.key")), dcp::LocalTime("2015-01-01T00:00:00"), dcp::LocalTime("2017-07-31T00:00:00"), "", "", ""
272                         ).encrypt(signer, signer->leaf(), vector<string>(), dcp::Formulation::MODIFIED_TRANSITIONAL_1, true, optional<int>())
273                 );
274
275         /* Starts too early */
276         BOOST_CHECK_THROW(
277                 dcp::DecryptedKDM(
278                         cpl, dcp::Key(dcp::file_to_string("test/data/private.key")), dcp::LocalTime("1981-01-01T00:00:00"), dcp::LocalTime("2017-07-31T00:00:00"), "", "", ""
279                         ).encrypt(signer, signer->leaf(), vector<string>(), dcp::Formulation::MODIFIED_TRANSITIONAL_1, true, optional<int>()),
280                 dcp::BadKDMDateError
281                 );
282
283         /* Finishes too late */
284         BOOST_CHECK_THROW(
285                 dcp::DecryptedKDM(
286                         cpl, dcp::Key(dcp::file_to_string("test/data/private.key")), dcp::LocalTime("2015-01-01T00:00:00"), dcp::LocalTime("2035-07-31T00:00:00"), "", "", ""
287                         ).encrypt(signer, signer->leaf(), vector<string>(), dcp::Formulation::MODIFIED_TRANSITIONAL_1, true, optional<int>()),
288                 dcp::BadKDMDateError
289                 );
290
291         /* Starts too early and finishes too late */
292         BOOST_CHECK_THROW(
293                 dcp::DecryptedKDM(
294                         cpl, dcp::Key(dcp::file_to_string("test/data/private.key")), dcp::LocalTime("1981-01-01T00:00:00"), dcp::LocalTime("2035-07-31T00:00:00"), "", "", ""
295                         ).encrypt(signer, signer->leaf(), vector<string>(), dcp::Formulation::MODIFIED_TRANSITIONAL_1, true, optional<int>()),
296                 dcp::BadKDMDateError
297                 );
298 }