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