2 Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
35 #include "certificate_chain.h"
37 #include "decrypted_kdm.h"
38 #include "encrypted_kdm.h"
39 #include "mono_picture_asset.h"
40 #include "picture_asset_writer.h"
42 #include "reel_mono_picture_asset.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>
57 using std::make_shared;
58 using std::shared_ptr;
59 using boost::optional;
62 /** Check reading and decryption of a KDM */
63 BOOST_AUTO_TEST_CASE (kdm_test)
65 dcp::DecryptedKDM kdm (
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")
69 dcp::file_to_string ("test/data/private.key")
72 auto keys = kdm.keys ();
74 BOOST_CHECK_EQUAL (keys.size(), 2);
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");
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");
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)
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")
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");
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"),
105 /** Test some of the utility methods of DecryptedKDM */
106 BOOST_AUTO_TEST_CASE (decrypted_kdm_test)
108 auto data = new uint8_t[16];
110 dcp::DecryptedKDM::put_uuid (&p, "8971c838-d0c3-405d-bc57-43afa9d91242");
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);
130 BOOST_CHECK_EQUAL (dcp::DecryptedKDM::get_uuid (&p), "8971c838-d0c3-405d-bc57-43afa9d91242");
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.
139 BOOST_AUTO_TEST_CASE (kdm_key_type_scope)
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")
146 doc.read_string (kdm.as_xml ());
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");
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");
162 static cxml::ConstNodePtr
163 kdm_forensic_test (cxml::Document& doc, bool picture, optional<int> audio)
165 dcp::DecryptedKDM decrypted (
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")
169 dcp::file_to_string ("test/data/private.key")
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"));
175 dcp::EncryptedKDM kdm = decrypted.encrypt (
176 signer, signer->leaf(), vector<string>(), dcp::Formulation::MODIFIED_TRANSITIONAL_1, picture, audio
179 /* Check that we can pass this through correctly */
180 BOOST_CHECK_EQUAL (kdm.as_xml(), dcp::EncryptedKDM(kdm.as_xml()).as_xml());
182 doc.read_string (kdm.as_xml());
184 return doc.node_child("AuthenticatedPublic")->
185 node_child("RequiredExtensions")->
186 node_child("KDMRequiredExtensions")->
187 optional_node_child("ForensicMarkFlagList");
191 /** Check ForensicMarkFlagList handling: disable picture and all audio */
192 BOOST_AUTO_TEST_CASE (kdm_forensic_test1)
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");
204 /** Check ForensicMarkFlagList handling: disable picture but not audio */
205 BOOST_AUTO_TEST_CASE (kdm_forensic_test2)
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");
216 /** Check ForensicMarkFlagList handling: disable audio but not picture */
217 BOOST_AUTO_TEST_CASE (kdm_forensic_test3)
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");
228 /** Check ForensicMarkFlagList handling: disable picture and audio above channel 3 */
229 BOOST_AUTO_TEST_CASE (kdm_forensic_test4)
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");
241 /** Check ForensicMarkFlagList handling: disable neither */
242 BOOST_AUTO_TEST_CASE (kdm_forensic_test5)
245 auto forensic = kdm_forensic_test(doc, false, optional<int>());
246 BOOST_CHECK (!forensic);
250 /** Check that KDM validity periods are checked for being within the certificate validity */
251 BOOST_AUTO_TEST_CASE (validity_period_test1)
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"));
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);
266 /* This certificate_chain is valid from 26/12/2012 to 24/12/2022 */
269 BOOST_CHECK_NO_THROW(
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>())
275 /* Starts too early */
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>()),
283 /* Finishes too late */
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>()),
291 /* Starts too early and finishes too late */
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>()),