Merge branch 'master' of /home/carl/git/libdcp
[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 <boost/algorithm/string.hpp>
22 #include <openssl/rsa.h>
23 #include <openssl/pem.h>
24 #include <openssl/err.h>
25 #include <libcxml/cxml.h>
26 #include "util.h"
27 #include "kdm.h"
28 #include "compose.hpp"
29 #include "exceptions.h"
30
31 using std::list;
32 using std::string;
33 using std::stringstream;
34 using std::hex;
35 using std::setw;
36 using std::setfill;
37 using std::cout;
38 using boost::shared_ptr;
39 using namespace libdcp;
40
41 KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
42 {
43         /* Read the private key */
44            
45         FILE* private_key_file = fopen (private_key.string().c_str(), "r");
46         if (!private_key_file) {
47                 throw FileError ("could not find RSA private key file", private_key.string ());
48         }
49         
50         RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
51         fclose (private_key_file);      
52         if (!rsa) {
53                 throw FileError ("could not read RSA private key file", private_key.string ());
54         }
55
56         
57         /* Read the KDM, decrypting it */
58
59         cxml::File f (kdm.string (), "DCinemaSecurityMessage");
60
61         shared_ptr<cxml::Node> authenticated_private = f.node_child ("AuthenticatedPrivate");
62         list<shared_ptr<cxml::Node> > encrypted_keys = authenticated_private->node_children ("EncryptedKey");
63
64         for (list<shared_ptr<cxml::Node> >::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
65
66                 /* Get the base-64-encoded cipher value from the KDM */
67                 shared_ptr<cxml::Node> cipher_data = (*i)->node_child ("CipherData");
68                 shared_ptr<cxml::Node> cipher_value_base64 = cipher_data->node_child ("CipherValue");
69
70                 /* Decode it from base-64 */
71                 unsigned char cipher_value[256];
72                 int const cipher_value_len = base64_decode (cipher_value_base64->content(), cipher_value, sizeof (cipher_value));
73
74                 /* Decrypt it */
75                 unsigned char* decrypted = new unsigned char[RSA_size(rsa)];
76                 int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
77                 if (decrypted_len == -1) {
78                         delete[] decrypted;
79                         throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
80                 }
81
82                 _ciphers.push_back (KDMCipher (decrypted, decrypted_len));
83                 delete[] decrypted;
84         }
85
86         RSA_free (rsa);
87 }
88
89
90 KDMCipher::KDMCipher (unsigned char const * raw, int len)
91 {
92         switch (len) {
93         case 134:
94                 /* interop */
95                 _structure_id = get (&raw, 16);
96                 _signer_thumbprint = get (&raw, 20);
97                 _cpl_id = get_uuid (&raw, 16);
98                 _key_id = get_uuid (&raw, 16);
99                 _not_valid_before = get (&raw, 25);
100                 _not_valid_after = get (&raw, 25);
101                 memcpy (_key_raw, raw, 16);
102                 _key_string = get_hex (&raw, 16);
103                 break;
104         case 138:
105                 /* SMPTE */
106                 _structure_id = get (&raw, 16);
107                 _signer_thumbprint = get (&raw, 20);
108                 _cpl_id = get_uuid (&raw, 16);
109                 _key_type = get (&raw, 4);
110                 _key_id = get_uuid (&raw, 16);
111                 _not_valid_before = get (&raw, 25);
112                 _not_valid_after = get (&raw, 25);
113                 memcpy (_key_raw, raw, 16);
114                 _key_string = get_hex (&raw, 16);
115                 break;
116         default:
117                 assert (false);
118         }
119 }
120
121 string
122 KDMCipher::get (unsigned char const ** p, int N) const
123 {
124         string g;
125         for (int i = 0; i < N; ++i) {
126                 g += **p;
127                 (*p)++;
128         }
129
130         return g;
131 }
132
133 string
134 KDMCipher::get_uuid (unsigned char const ** p, int N) const
135 {
136         stringstream g;
137         
138         for (int i = 0; i < N; ++i) {
139                 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
140                 (*p)++;
141                 if (i == 3 || i == 5 || i == 7 || i == 9) {
142                         g << '-';
143                 }
144         }
145
146         return g.str ();
147 }
148
149 string
150 KDMCipher::get_hex (unsigned char const ** p, int N) const
151 {
152         stringstream g;
153         
154         for (int i = 0; i < N; ++i) {
155                 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
156                 (*p)++;
157         }
158
159         return g.str ();
160 }