Can decrypt a KDM.
[libdcp.git] / src / kdm.cc
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iomanip>
21 #include <openssl/rsa.h>
22 #include <openssl/pem.h>
23 #include <libcxml/cxml.h>
24 #include "KM_util.h"
25 #include "kdm.h"
26 #include "exceptions.h"
27
28 using std::list;
29 using std::string;
30 using std::stringstream;
31 using std::hex;
32 using std::setw;
33 using std::setfill;
34 using boost::shared_ptr;
35 using namespace libdcp;
36
37 KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
38 {
39         /* Read the private key */
40            
41         FILE* private_key_file = fopen (private_key.string().c_str(), "r");
42         if (!private_key_file) {
43                 throw FileError ("could not find RSA private key file", private_key.string ());
44         }
45         
46         RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
47         fclose (private_key_file);      
48         if (!rsa) {
49                 throw FileError ("could not read RSA private key file", private_key.string ());
50         }
51
52         
53         /* Read the KDM, decrypting it */
54
55         cxml::File f (kdm.string (), "DCinemaSecurityMessage");
56
57         shared_ptr<cxml::Node> authenticated_private = f.node_child ("AuthenticatedPrivate");
58         list<shared_ptr<cxml::Node> > encrypted_keys = authenticated_private->node_children ("EncryptedKey");
59
60         for (list<shared_ptr<cxml::Node> >::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
61
62                 /* Get the base-64-encoded cipher value from the KDM */
63                 shared_ptr<cxml::Node> cipher_data = (*i)->node_child ("CipherData");
64                 shared_ptr<cxml::Node> cipher_value_base64 = cipher_data->node_child ("CipherValue");
65
66                 /* Decode it from base-64 */
67                 unsigned char cipher_value[256];
68                 ui32_t cipher_value_len;
69                 if (Kumu::base64decode (cipher_value_base64->content().c_str(), cipher_value, sizeof (cipher_value), &cipher_value_len)) {
70                         RSA_free (rsa);
71                         throw MiscError ("could not base-64-decode CipherValue from KDM");
72                 }
73
74                 /* Decrypt it */
75                 unsigned char decrypted[256];
76                 unsigned int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
77                 assert (decrypted_len < sizeof (decrypted));
78
79                 _ciphers.push_back (KDMCipher (decrypted, decrypted_len));
80         }
81
82         RSA_free (rsa);
83 }
84
85
86 KDMCipher::KDMCipher (unsigned char const * raw, int len)
87 {
88         switch (len) {
89         case 134:
90                 /* interop */
91                 _structure_id = get (&raw, 16);
92                 _signer_thumbprint = get (&raw, 20);
93                 _cpl_id = get_uuid (&raw, 16);
94                 _key_id = get_uuid (&raw, 16);
95                 _not_valid_before = get (&raw, 25);
96                 _not_valid_after = get (&raw, 25);
97                 _key_data = get_hex (&raw, 16);
98                 break;
99         case 138:
100                 /* SMPTE */
101                 _structure_id = get (&raw, 16);
102                 _signer_thumbprint = get (&raw, 20);
103                 _cpl_id = get_uuid (&raw, 16);
104                 _key_type = get (&raw, 4);
105                 _key_id = get_uuid (&raw, 16);
106                 _not_valid_before = get (&raw, 25);
107                 _not_valid_after = get (&raw, 25);
108                 _key_data = get_hex (&raw, 16);
109                 break;
110         default:
111                 assert (false);
112         }
113 }
114
115 string
116 KDMCipher::get (unsigned char const ** p, int N) const
117 {
118         string g;
119         for (int i = 0; i < N; ++i) {
120                 g += **p;
121                 (*p)++;
122         }
123
124         return g;
125 }
126
127 string
128 KDMCipher::get_uuid (unsigned char const ** p, int N) const
129 {
130         stringstream g;
131         
132         for (int i = 0; i < N; ++i) {
133                 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
134                 (*p)++;
135                 if (i == 3 || i == 5 || i == 7 || i == 9) {
136                         g << '-';
137                 }
138         }
139
140         return g.str ();
141 }
142
143 string
144 KDMCipher::get_hex (unsigned char const ** p, int N) const
145 {
146         stringstream g;
147         
148         for (int i = 0; i < N; ++i) {
149                 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
150                 (*p)++;
151         }
152
153         return g.str ();
154 }