Move some tests and rename one set.
[libdcp.git] / test / kdm_test.cc
1 /*
2     Copyright (C) 2013-2019 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 #include <boost/foreach.hpp>
50
51 using std::list;
52 using std::string;
53 using std::vector;
54 using boost::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         list<dcp::DecryptedKDMKey> 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         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"
92                 );
93
94 #ifdef LIBDCP_WINDOWS
95         BOOST_CHECK_EQUAL (r, 0);
96 #else
97         BOOST_CHECK_EQUAL (WEXITSTATUS (r), 0);
98 #endif
99 }
100
101 /** Test some of the utility methods of DecryptedKDM */
102 BOOST_AUTO_TEST_CASE (decrypted_kdm_test)
103 {
104         uint8_t* data = new uint8_t[16];
105         uint8_t* p = data;
106         dcp::DecryptedKDM::put_uuid (&p, "8971c838-d0c3-405d-bc57-43afa9d91242");
107
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);
124
125         p = data;
126         BOOST_CHECK_EQUAL (dcp::DecryptedKDM::get_uuid (&p), "8971c838-d0c3-405d-bc57-43afa9d91242");
127
128         delete[] data;
129 }
130
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.
133  */
134 BOOST_AUTO_TEST_CASE (kdm_key_type_scope)
135 {
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")
138                 );
139
140         cxml::Document doc;
141         doc.read_string (kdm.as_xml ());
142
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");
148
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");
152                 }
153         }
154 }
155
156 static cxml::ConstNodePtr
157 kdm_forensic_test (cxml::Document& doc, bool picture, optional<int> audio)
158 {
159         dcp::DecryptedKDM decrypted (
160                 dcp::EncryptedKDM (
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")
162                         ),
163                 dcp::file_to_string ("test/data/private.key")
164                 );
165
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"));
168
169         dcp::EncryptedKDM kdm = decrypted.encrypt (
170                 signer, signer->leaf(), vector<string>(), dcp::MODIFIED_TRANSITIONAL_1, picture, audio
171                 );
172
173         /* Check that we can pass this through correctly */
174         BOOST_CHECK_EQUAL (kdm.as_xml(), dcp::EncryptedKDM(kdm.as_xml()).as_xml());
175
176         doc.read_string (kdm.as_xml());
177
178         return doc.node_child("AuthenticatedPublic")->
179                 node_child("RequiredExtensions")->
180                 node_child("KDMRequiredExtensions")->
181                 optional_node_child("ForensicMarkFlagList");
182 }
183
184 /** Check ForensicMarkFlagList handling: disable picture and all audio */
185 BOOST_AUTO_TEST_CASE (kdm_forensic_test1)
186 {
187         cxml::Document doc;
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");
194 }
195
196 /** Check ForensicMarkFlagList handling: disable picture but not audio */
197 BOOST_AUTO_TEST_CASE (kdm_forensic_test2)
198 {
199         cxml::Document doc;
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");
205 }
206
207 /** Check ForensicMarkFlagList handling: disable audio but not picture */
208 BOOST_AUTO_TEST_CASE (kdm_forensic_test3)
209 {
210         cxml::Document doc;
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");
216 }
217
218 /** Check ForensicMarkFlagList handling: disable picture and audio above channel 3 */
219 BOOST_AUTO_TEST_CASE (kdm_forensic_test4)
220 {
221         cxml::Document doc;
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");
228 }
229
230 /** Check ForensicMarkFlagList handling: disable neither */
231 BOOST_AUTO_TEST_CASE (kdm_forensic_test5)
232 {
233         cxml::Document doc;
234         cxml::ConstNodePtr forensic = kdm_forensic_test(doc, false, optional<int>());
235         BOOST_CHECK (!forensic);
236 }
237
238 /** Check that KDM validity periods are checked for being within the certificate validity */
239 BOOST_AUTO_TEST_CASE (validity_period_test1)
240 {
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"));
243
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));
252         cpl->add(reel);
253
254         /* This certificate_chain is valid from 26/12/2012 to 24/12/2022 */
255
256         /* Inside */
257         BOOST_CHECK_NO_THROW(
258                 dcp::DecryptedKDM(
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>())
261                 );
262
263         /* Starts too early */
264         BOOST_CHECK_THROW(
265                 dcp::DecryptedKDM(
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>()),
268                 dcp::BadKDMDateError
269                 );
270
271         /* Finishes too late */
272         BOOST_CHECK_THROW(
273                 dcp::DecryptedKDM(
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>()),
276                 dcp::BadKDMDateError
277                 );
278
279         /* Starts too early and finishes too late */
280         BOOST_CHECK_THROW(
281                 dcp::DecryptedKDM(
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>()),
284                 dcp::BadKDMDateError
285                 );
286 }