2 Copyright (C) 2013-2019 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.
34 #include "encrypted_kdm.h"
35 #include "decrypted_kdm.h"
36 #include "certificate_chain.h"
40 #include "mono_picture_asset.h"
41 #include "reel_mono_picture_asset.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 #include <boost/foreach.hpp>
54 using boost::shared_ptr;
55 using boost::optional;
57 /** Check reading and decryption of a KDM */
58 BOOST_AUTO_TEST_CASE (kdm_test)
60 dcp::DecryptedKDM kdm (
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")
64 dcp::file_to_string ("test/data/private.key")
67 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
69 BOOST_CHECK_EQUAL (keys.size(), 2);
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");
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");
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)
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")
87 shared_ptr<xmlpp::DomParser> parser (new xmlpp::DomParser ());
88 parser->parse_memory (kdm.as_xml ());
89 parser->get_document()->write_to_file_formatted ("build/kdm.xml", "UTF-8");
90 int const r = system (
91 "xmldiff -c test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml build/kdm.xml"
95 BOOST_CHECK_EQUAL (r, 0);
97 BOOST_CHECK_EQUAL (WEXITSTATUS (r), 0);
101 /** Test some of the utility methods of DecryptedKDM */
102 BOOST_AUTO_TEST_CASE (decrypted_kdm_test)
104 uint8_t* data = new uint8_t[16];
106 dcp::DecryptedKDM::put_uuid (&p, "8971c838-d0c3-405d-bc57-43afa9d91242");
108 BOOST_CHECK_EQUAL (data[0], 0x89);
109 BOOST_CHECK_EQUAL (data[1], 0x71);
110 BOOST_CHECK_EQUAL (data[2], 0xc8);
111 BOOST_CHECK_EQUAL (data[3], 0x38);
112 BOOST_CHECK_EQUAL (data[4], 0xd0);
113 BOOST_CHECK_EQUAL (data[5], 0xc3);
114 BOOST_CHECK_EQUAL (data[6], 0x40);
115 BOOST_CHECK_EQUAL (data[7], 0x5d);
116 BOOST_CHECK_EQUAL (data[8], 0xbc);
117 BOOST_CHECK_EQUAL (data[9], 0x57);
118 BOOST_CHECK_EQUAL (data[10], 0x43);
119 BOOST_CHECK_EQUAL (data[11], 0xaf);
120 BOOST_CHECK_EQUAL (data[12], 0xa9);
121 BOOST_CHECK_EQUAL (data[13], 0xd9);
122 BOOST_CHECK_EQUAL (data[14], 0x12);
123 BOOST_CHECK_EQUAL (data[15], 0x42);
126 BOOST_CHECK_EQUAL (dcp::DecryptedKDM::get_uuid (&p), "8971c838-d0c3-405d-bc57-43afa9d91242");
131 /** Check that <KeyType> tags have the scope attribute.
132 * Wolfgang Woehl believes this is compulsory and I am more-or-less inclined to agree.
134 BOOST_AUTO_TEST_CASE (kdm_key_type_scope)
136 dcp::EncryptedKDM kdm (
137 dcp::file_to_string ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml")
141 doc.read_string (kdm.as_xml ());
143 list<cxml::NodePtr> typed_key_ids = doc.node_child("AuthenticatedPublic")->
144 node_child("RequiredExtensions")->
145 node_child("KDMRequiredExtensions")->
146 node_child("KeyIdList")->
147 node_children("TypedKeyId");
149 BOOST_FOREACH (cxml::NodePtr i, typed_key_ids) {
150 BOOST_FOREACH (cxml::NodePtr j, i->node_children("KeyType")) {
151 BOOST_CHECK (j->string_attribute("scope") == "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
156 static cxml::ConstNodePtr
157 kdm_forensic_test (cxml::Document& doc, bool picture, optional<int> audio)
159 dcp::DecryptedKDM decrypted (
161 dcp::file_to_string ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml")
163 dcp::file_to_string ("test/data/private.key")
166 shared_ptr<dcp::CertificateChain> signer(new dcp::CertificateChain(dcp::file_to_string("test/data/certificate_chain")));
167 signer->set_key(dcp::file_to_string("test/data/private.key"));
169 dcp::EncryptedKDM kdm = decrypted.encrypt (
170 signer, signer->leaf(), vector<string>(), dcp::MODIFIED_TRANSITIONAL_1, picture, audio
173 /* Check that we can pass this through correctly */
174 BOOST_CHECK_EQUAL (kdm.as_xml(), dcp::EncryptedKDM(kdm.as_xml()).as_xml());
176 doc.read_string (kdm.as_xml());
178 return doc.node_child("AuthenticatedPublic")->
179 node_child("RequiredExtensions")->
180 node_child("KDMRequiredExtensions")->
181 optional_node_child("ForensicMarkFlagList");
184 /** Check ForensicMarkFlagList handling: disable picture and all audio */
185 BOOST_AUTO_TEST_CASE (kdm_forensic_test1)
188 cxml::ConstNodePtr forensic = kdm_forensic_test(doc, true, 0);
189 BOOST_REQUIRE (forensic);
190 list<cxml::NodePtr> flags = forensic->node_children("ForensicMarkFlag");
191 BOOST_REQUIRE_EQUAL (flags.size(), 2);
192 BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
193 BOOST_CHECK_EQUAL (flags.back()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
196 /** Check ForensicMarkFlagList handling: disable picture but not audio */
197 BOOST_AUTO_TEST_CASE (kdm_forensic_test2)
200 cxml::ConstNodePtr forensic = kdm_forensic_test(doc, true, optional<int>());
201 BOOST_REQUIRE (forensic);
202 list<cxml::NodePtr> flags = forensic->node_children("ForensicMarkFlag");
203 BOOST_REQUIRE_EQUAL (flags.size(), 1);
204 BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
207 /** Check ForensicMarkFlagList handling: disable audio but not picture */
208 BOOST_AUTO_TEST_CASE (kdm_forensic_test3)
211 cxml::ConstNodePtr forensic = kdm_forensic_test(doc, false, 0);
212 BOOST_REQUIRE (forensic);
213 list<cxml::NodePtr> flags = forensic->node_children("ForensicMarkFlag");
214 BOOST_REQUIRE_EQUAL (flags.size(), 1);
215 BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
218 /** Check ForensicMarkFlagList handling: disable picture and audio above channel 3 */
219 BOOST_AUTO_TEST_CASE (kdm_forensic_test4)
222 cxml::ConstNodePtr forensic = kdm_forensic_test(doc, true, 3);
223 BOOST_REQUIRE (forensic);
224 list<cxml::NodePtr> flags = forensic->node_children("ForensicMarkFlag");
225 BOOST_REQUIRE_EQUAL (flags.size(), 2);
226 BOOST_CHECK_EQUAL (flags.front()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
227 BOOST_CHECK_EQUAL (flags.back()->content(), "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable-above-channel-3");
230 /** Check ForensicMarkFlagList handling: disable neither */
231 BOOST_AUTO_TEST_CASE (kdm_forensic_test5)
234 cxml::ConstNodePtr forensic = kdm_forensic_test(doc, false, optional<int>());
235 BOOST_CHECK (!forensic);
238 /** Check that KDM validity periods are checked for being within the certificate validity */
239 BOOST_AUTO_TEST_CASE (validity_period_test1)
241 shared_ptr<dcp::CertificateChain> signer(new dcp::CertificateChain(dcp::file_to_string("test/data/certificate_chain")));
242 signer->set_key(dcp::file_to_string("test/data/private.key"));
244 shared_ptr<dcp::MonoPictureAsset> asset (new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
245 asset->set_key (dcp::Key());
246 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write ("build/test/validity_period_test1.mxf", false);
247 dcp::File frame ("test/data/32x32_red_square.j2c");
248 writer->write (frame.data(), frame.size());
249 shared_ptr<dcp::Reel> reel(new dcp::Reel());
250 reel->add(shared_ptr<dcp::ReelPictureAsset>(new dcp::ReelMonoPictureAsset(asset, 0)));
251 shared_ptr<dcp::CPL> cpl (new dcp::CPL("test", dcp::FEATURE));
254 /* This certificate_chain is valid from 26/12/2012 to 24/12/2022 */
257 BOOST_CHECK_NO_THROW(
259 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"), "", "", ""
260 ).encrypt(signer, signer->leaf(), vector<string>(), dcp::MODIFIED_TRANSITIONAL_1, true, optional<int>())
263 /* Starts too early */
266 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"), "", "", ""
267 ).encrypt(signer, signer->leaf(), vector<string>(), dcp::MODIFIED_TRANSITIONAL_1, true, optional<int>()),
271 /* Finishes too late */
274 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"), "", "", ""
275 ).encrypt(signer, signer->leaf(), vector<string>(), dcp::MODIFIED_TRANSITIONAL_1, true, optional<int>()),
279 /* Starts too early and finishes too late */
282 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"), "", "", ""
283 ).encrypt(signer, signer->leaf(), vector<string>(), dcp::MODIFIED_TRANSITIONAL_1, true, optional<int>()),