1 | /*␊ |
2 | Copyright 2021 Grégory Soutadé␊ |
3 | ␊ |
4 | This file is part of libgourou.␊ |
5 | ␊ |
6 | libgourou is free software: you can redistribute it and/or modify␊ |
7 | it under the terms of the GNU Lesser General Public License as published by␊ |
8 | the Free Software Foundation, either version 3 of the License, or␊ |
9 | (at your option) any later version.␊ |
10 | ␊ |
11 | libgourou 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 Lesser General Public License for more details.␊ |
15 | ␊ |
16 | You should have received a copy of the GNU Lesser General Public License␊ |
17 | along with libgourou. If not, see <http://www.gnu.org/licenses/>.␊ |
18 | */␊ |
19 | ␊ |
20 | #include <arpa/inet.h>␊ |
21 | #include <sys/time.h>␊ |
22 | #include <time.h>␊ |
23 | #include <vector>␊ |
24 | ␊ |
25 | #include <uPDFParser.h>␊ |
26 | ␊ |
27 | #include <libgourou.h>␊ |
28 | #include <libgourou_common.h>␊ |
29 | #include <libgourou_log.h>␊ |
30 | ␊ |
31 | #define LOCAL_ADEPT_DIR "./.adept"␊ |
32 | ␊ |
33 | #define ASN_NONE 0x00␊ |
34 | #define ASN_NS_TAG 0x01␊ |
35 | #define ASN_CHILD 0x02␊ |
36 | #define ASN_END_TAG 0x03␊ |
37 | #define ASN_TEXT 0x04␊ |
38 | #define ASN_ATTRIBUTE 0x05␊ |
39 | ␊ |
40 | namespace gourou␊ |
41 | {␊ |
42 | GOUROU_LOG_LEVEL logLevel = LG_LOG_WARN;␊ |
43 | const std::string DRMProcessor::VERSION = LIBGOUROU_VERSION;␊ |
44 | ␊ |
45 | DRMProcessor::DRMProcessor(DRMProcessorClient* client):client(client), device(0), user(0)␊ |
46 | {␊ |
47 | ␉if (!client)␊ |
48 | ␉ EXCEPTION(GOUROU_INVALID_CLIENT, "DRMProcessorClient is NULL");␊ |
49 | }␊ |
50 | ␊ |
51 | DRMProcessor::DRMProcessor(DRMProcessorClient* client,␊ |
52 | ␉␉␉ const std::string& deviceFile, const std::string& activationFile,␊ |
53 | ␉␉␉ const std::string& deviceKeyFile):␊ |
54 | ␉client(client), device(0), user(0)␊ |
55 | {␊ |
56 | ␉if (!client)␊ |
57 | ␉ EXCEPTION(GOUROU_INVALID_CLIENT, "DRMProcessorClient is NULL");␊ |
58 | ␉␊ |
59 | ␉device = new Device(this, deviceFile, deviceKeyFile);␊ |
60 | ␉user = new User(this, activationFile);␊ |
61 | ␊ |
62 | ␉if (user->getDeviceFingerprint() != "" &&␊ |
63 | ␉ (*device)["fingerprint"] != user->getDeviceFingerprint())␊ |
64 | ␉ EXCEPTION(GOUROU_DEVICE_DOES_NOT_MATCH, "User and device fingerprint does not match");␊ |
65 | }␊ |
66 | ␊ |
67 | DRMProcessor::~DRMProcessor()␊ |
68 | {␊ |
69 | ␉if (device) delete device;␊ |
70 | ␉if (user) delete user;␊ |
71 | }␊ |
72 | ␊ |
73 | DRMProcessor* DRMProcessor::createDRMProcessor(DRMProcessorClient* client, bool randomSerial, std::string dirName,␊ |
74 | ␉␉␉␉␉␉ const std::string& hobbes, const std::string& ACSServer)␊ |
75 | {␊ |
76 | ␉DRMProcessor* processor = new DRMProcessor(client);␊ |
77 | ␊ |
78 | ␉if (dirName == "")␊ |
79 | ␉ dirName = getDefaultAdeptDir();␊ |
80 | ␉␊ |
81 | ␉Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial);␊ |
82 | ␉processor->device = device;␊ |
83 | ␊ |
84 | ␉User* user = User::createUser(processor, dirName, ACSServer);␊ |
85 | ␉processor->user = user;␊ |
86 | ␉␊ |
87 | ␉return processor;␊ |
88 | }␊ |
89 | ␊ |
90 | ␊ |
91 | void DRMProcessor::pushString(void* sha_ctx, const std::string& string)␊ |
92 | {␊ |
93 | ␉int length = string.length();␊ |
94 | ␉uint16_t nlength = htons(length);␊ |
95 | ␉char c;␊ |
96 | ␊ |
97 | ␉if (logLevel >= LG_LOG_TRACE)␊ |
98 | ␉ printf("%02x %02x ", ((uint8_t*)&nlength)[0], ((uint8_t*)&nlength)[1]);␊ |
99 | ␉␊ |
100 | ␉client->digestUpdate(sha_ctx, (unsigned char*)&nlength, sizeof(nlength));␊ |
101 | ␊ |
102 | ␉for(int i=0; i<length; i++)␊ |
103 | ␉{␊ |
104 | ␉ c = string[i];␊ |
105 | ␉ client->digestUpdate(sha_ctx, (unsigned char*)&c, 1);␊ |
106 | ␉ if (logLevel >= LG_LOG_TRACE)␊ |
107 | ␉␉printf("%c", c);␊ |
108 | ␉}␊ |
109 | ␉if (logLevel >= LG_LOG_TRACE)␊ |
110 | ␉ printf("\n");␊ |
111 | }␊ |
112 | ␊ |
113 | void DRMProcessor::pushTag(void* sha_ctx, uint8_t tag)␊ |
114 | {␊ |
115 | ␉client->digestUpdate(sha_ctx, &tag, sizeof(tag));␊ |
116 | ␉if (logLevel >= LG_LOG_TRACE)␊ |
117 | ␉ printf("%02x ", tag);␊ |
118 | }␊ |
119 | ␊ |
120 | void DRMProcessor::hashNode(const pugi::xml_node& root, void *sha_ctx, std::map<std::string,std::string> nsHash)␊ |
121 | {␊ |
122 | ␉switch(root.type())␊ |
123 | ␉{␊ |
124 | ␉case pugi::node_element:␊ |
125 | ␉{␊ |
126 | ␉ std::string name = root.name();␊ |
127 | ␊ |
128 | ␉ // Look for "xmlns[:]" attribute␊ |
129 | ␉ for (pugi::xml_attribute_iterator ait = root.attributes_begin();␊ |
130 | ␉␉ ait != root.attributes_end(); ++ait)␊ |
131 | ␉ {␊ |
132 | ␉␉std::string attrName(ait->name());␊ |
133 | ␊ |
134 | ␉␉if (attrName.find("xmlns") == 0)␊ |
135 | ␉␉{␊ |
136 | ␉␉ std::string ns("GENERICNS");␊ |
137 | ␉␉ // Compound xmlns:Name attribute␊ |
138 | ␉␉ if (attrName.find(':') != std::string::npos)␊ |
139 | ␉␉␉ns = attrName.substr(attrName.find(':')+1);␊ |
140 | ␊ |
141 | ␉␉ nsHash[ns] = ait->value();␊ |
142 | ␉␉ // Don't break here because we may multiple xmlns definitions␊ |
143 | ␉␉ // break;␊ |
144 | ␉␉}␊ |
145 | ␉ }␊ |
146 | ␊ |
147 | ␉ // Remove namespace from tag␊ |
148 | ␉ // If we have a namespace for the first time, put it to hash␊ |
149 | ␉ if (name.find(':') != std::string::npos)␊ |
150 | ␉ {␊ |
151 | ␉␉size_t nsIndex = name.find(':');␊ |
152 | ␉␉std::string nodeNS = name.substr(0, nsIndex);␊ |
153 | ␊ |
154 | ␉␉pushTag(sha_ctx, ASN_NS_TAG);␊ |
155 | ␉␉pushString(sha_ctx, nsHash[nodeNS]);␊ |
156 | ␉␉␊ |
157 | ␉␉name = name.substr(nsIndex+1);␊ |
158 | ␉ }␊ |
159 | ␉ // Global xmlns, always send to hash␊ |
160 | ␉ else if (nsHash.find("GENERICNS") != nsHash.end())␊ |
161 | ␉ {␊ |
162 | ␉␉pushTag(sha_ctx, ASN_NS_TAG);␊ |
163 | ␉␉pushString(sha_ctx, nsHash["GENERICNS"]);␊ |
164 | ␉ }␊ |
165 | ␉ ␊ |
166 | ␉ pushString(sha_ctx, name);␊ |
167 | ␊ |
168 | ␉ std::vector<std::string> attributes;␊ |
169 | ␉ pugi::xml_attribute attr;␊ |
170 | ␉ ␊ |
171 | ␉ for (attr = root.first_attribute();␊ |
172 | ␉␉ attr; attr = attr.next_attribute())␊ |
173 | ␉ {␊ |
174 | ␉␉if (std::string(attr.name()).find("xmlns") != std::string::npos)␊ |
175 | ␉␉ continue;␊ |
176 | ␊ |
177 | ␉␉attributes.push_back(attr.name());␊ |
178 | ␉ }␊ |
179 | ␊ |
180 | ␉ // Attributes must be handled in alphabetical order␊ |
181 | ␉ std::sort(attributes.begin(), attributes.end());␊ |
182 | ␊ |
183 | ␉ std::vector<std::string>::iterator attributesIt;␊ |
184 | ␉ for(attributesIt = attributes.begin();␊ |
185 | ␉␉attributesIt != attributes.end();␊ |
186 | ␉␉attributesIt++)␊ |
187 | ␉ {␊ |
188 | ␉␉attr = root.attribute(attributesIt->c_str());␊ |
189 | ␉␉␊ |
190 | ␉␉pushTag(sha_ctx, ASN_ATTRIBUTE);␊ |
191 | ␉␉pushString(sha_ctx, "");␊ |
192 | ␉␉␊ |
193 | ␉␉pushString(sha_ctx, attr.name());␊ |
194 | ␉␉pushString(sha_ctx, attr.value());␊ |
195 | ␉ }␊ |
196 | ␊ |
197 | ␉ pushTag(sha_ctx, ASN_CHILD);␊ |
198 | ␊ |
199 | ␉ for (pugi::xml_node child : root.children())␊ |
200 | ␉␉hashNode(child, sha_ctx, nsHash);␊ |
201 | ␊ |
202 | ␉ pushTag(sha_ctx, ASN_END_TAG);␊ |
203 | ␉ ␊ |
204 | ␉ break;␊ |
205 | ␉}␊ |
206 | ␉case pugi::node_pcdata:␊ |
207 | ␉{␊ |
208 | ␉ std::string trimmed = root.value();␊ |
209 | ␉ trimmed = trim(trimmed);␊ |
210 | ␊ |
211 | ␉ if (trimmed.length())␊ |
212 | ␉ {␊ |
213 | ␉␉pushTag(sha_ctx, ASN_TEXT);␊ |
214 | ␉␉pushString(sha_ctx, trimmed);␊ |
215 | ␉ }␊ |
216 | ␊ |
217 | ␉ break;␊ |
218 | ␉}␊ |
219 | ␉default:␊ |
220 | ␉ break;␊ |
221 | ␉}␊ |
222 | }␊ |
223 | ␉␉␊ |
224 | void DRMProcessor::hashNode(const pugi::xml_node& root, unsigned char* sha_out)␊ |
225 | {␊ |
226 | ␉void* sha_ctx = client->createDigest("SHA1");␊ |
227 | ␉␊ |
228 | ␉std::map<std::string, std::string> nsHash;␊ |
229 | ␊ |
230 | ␉hashNode(root, sha_ctx, nsHash);␊ |
231 | ␊ |
232 | ␉client->digestFinalize(sha_ctx, sha_out);␊ |
233 | ␉␊ |
234 | ␉dumpBuffer(gourou::LG_LOG_DEBUG, "\nSHA OUT : ", sha_out, SHA1_LEN);␊ |
235 | }␊ |
236 | ␊ |
237 | void DRMProcessor::signNode(pugi::xml_node& rootNode)␊ |
238 | {␊ |
239 | ␉// Compute hash␊ |
240 | ␉unsigned char sha_out[SHA1_LEN];␊ |
241 | ␊ |
242 | ␉hashNode(rootNode, sha_out);␊ |
243 | ␉ ␊ |
244 | ␉// Sign with private key␊ |
245 | ␉unsigned char res[RSA_KEY_SIZE];␊ |
246 | ␉ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);␊ |
247 | ␉std::string pkcs12 = user->getPKCS12();␊ |
248 | ␉ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12);␊ |
249 | ␉␊ |
250 | ␉client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(),␊ |
251 | ␉␉␉␉ RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),␊ |
252 | ␉␉␉␉ sha_out, sizeof(sha_out), res);␊ |
253 | ␉␊ |
254 | ␉dumpBuffer(gourou::LG_LOG_DEBUG, "Sig : ", res, sizeof(res));␊ |
255 | ␊ |
256 | ␉std::string signature = ByteArray(res, sizeof(res)).toBase64();␊ |
257 | ␉appendTextElem(rootNode, "adept:signature", signature);␊ |
258 | }␊ |
259 | ␊ |
260 | void DRMProcessor::addNonce(pugi::xml_node& root)␊ |
261 | {␊ |
262 | ␉/*␊ |
263 | ␉ r4 = tp->time␊ |
264 | ␉ r3 = 0␊ |
265 | ␉ r2 = tm->militime␊ |
266 | ␉ r0 = 0x6f046000␊ |
267 | ␉ r1 = 0x388a␊ |
268 | ␊ |
269 | ␉ r3 += high(r4*1000)␊ |
270 | ␉ r2 += low(r4*1000)␊ |
271 | ␊ |
272 | ␉ r0 += r2␊ |
273 | ␉ r1 += r3␊ |
274 | ␉ */␊ |
275 | ␉struct timeval tv;␊ |
276 | ␉gettimeofday(&tv, 0);␊ |
277 | ␉uint32_t nonce32[2] = {0x6f046000, 0x388a};␊ |
278 | #ifdef STATIC_NONCE␊ |
279 | ␉uint64_t bigtime = 0xAA001122BBCCAAULL;␊ |
280 | #else␊ |
281 | ␉uint64_t bigtime = tv.tv_sec*1000;␊ |
282 | #endif␊ |
283 | ␉nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);␊ |
284 | ␉nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);␊ |
285 | ␉␊ |
286 | ␉ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32));␊ |
287 | ␉uint32_t tmp = 0;␊ |
288 | ␉nonce.append((const unsigned char*)&tmp, sizeof(tmp));␊ |
289 | ␉appendTextElem(root, "adept:nonce", nonce.toBase64().data());␊ |
290 | ␊ |
291 | ␉time_t _time = time(0) + 10*60; // Cur time + 10 minutes␊ |
292 | ␉struct tm* tm_info = gmtime(&_time);␊ |
293 | ␉char buffer[32];␊ |
294 | ␊ |
295 | ␉strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info);␊ |
296 | ␉appendTextElem(root, "adept:expiration", buffer);␊ |
297 | }␊ |
298 | ␊ |
299 | ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map<std::string, std::string>* responseHeaders, int fd, bool resume)␊ |
300 | {␊ |
301 | ␉if (contentType == 0)␊ |
302 | ␉ contentType = "";␊ |
303 | ␉std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders, fd, resume);␊ |
304 | ␊ |
305 | ␉if (fd) return ByteArray();␊ |
306 | ␉␊ |
307 | ␉pugi::xml_document replyDoc;␊ |
308 | ␉replyDoc.load_buffer(reply.c_str(), reply.length());␊ |
309 | ␊ |
310 | ␉pugi::xml_node root = replyDoc.first_child();␊ |
311 | ␉if (std::string(root.name()) == "error")␊ |
312 | ␉{␊ |
313 | ␉ EXCEPTION(GOUROU_ADEPT_ERROR, root.attribute("data").value());␊ |
314 | ␉}␊ |
315 | ␉␊ |
316 | ␉return ByteArray(reply);␊ |
317 | }␊ |
318 | ␊ |
319 | ByteArray DRMProcessor::sendRequest(const pugi::xml_document& document, const std::string& url)␊ |
320 | {␊ |
321 | ␉StringXMLWriter xmlWriter;␊ |
322 | ␉document.save(xmlWriter, " ");␊ |
323 | ␉std::string xmlStr = xmlWriter.getResult();␊ |
324 | ␊ |
325 | ␉return sendRequest(url, xmlStr, (const char*)"application/vnd.adobe.adept+xml");␊ |
326 | }␊ |
327 | ␊ |
328 | void DRMProcessor::buildAuthRequest(pugi::xml_document& authReq)␊ |
329 | {␊ |
330 | ␉pugi::xml_node decl = authReq.append_child(pugi::node_declaration);␊ |
331 | ␉decl.append_attribute("version") = "1.0";␊ |
332 | ␉␊ |
333 | ␉pugi::xml_node root = authReq.append_child("adept:credentials");␊ |
334 | ␉root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
335 | ␊ |
336 | ␉appendTextElem(root, "adept:user", user->getUUID());␊ |
337 | ␊ |
338 | ␉ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);␊ |
339 | ␉unsigned char* pkcs12 = 0;␊ |
340 | ␉unsigned int pkcs12Length;␊ |
341 | ␉ByteArray pkcs12Cert = ByteArray::fromBase64(user->getPKCS12());␊ |
342 | ␉␊ |
343 | ␉client->extractCertificate(pkcs12Cert.data(), pkcs12Cert.length(),␊ |
344 | ␉␉␉␉ RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),␊ |
345 | ␉␉␉␉ &pkcs12, &pkcs12Length);␊ |
346 | ␉ByteArray privateCertificate(pkcs12, pkcs12Length);␊ |
347 | ␉free(pkcs12);␊ |
348 | ␊ |
349 | ␉appendTextElem(root, "adept:certificate", privateCertificate.toBase64());␊ |
350 | ␉appendTextElem(root, "adept:licenseCertificate", user->getProperty("//adept:licenseCertificate"));␊ |
351 | ␉appendTextElem(root, "adept:authenticationCertificate", user->getProperty("//adept:authenticationCertificate"));␊ |
352 | }␊ |
353 | ␊ |
354 | void DRMProcessor::buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL)␊ |
355 | {␊ |
356 | ␉pugi::xml_node decl = initLicReq.append_child(pugi::node_declaration);␊ |
357 | ␉decl.append_attribute("version") = "1.0";␊ |
358 | ␉␊ |
359 | ␉pugi::xml_node root = initLicReq.append_child("adept:licenseServiceRequest");␊ |
360 | ␉root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
361 | ␉root.append_attribute("identity") = "user";␊ |
362 | ␊ |
363 | ␉appendTextElem(root, "adept:operatorURL", operatorURL);␊ |
364 | ␉addNonce(root);␊ |
365 | ␉appendTextElem(root, "adept:user", user->getUUID());␊ |
366 | ␊ |
367 | ␉signNode(root);␊ |
368 | }␊ |
369 | ␊ |
370 | void DRMProcessor::doOperatorAuth(std::string operatorURL)␊ |
371 | {␊ |
372 | ␉pugi::xml_document authReq;␊ |
373 | ␉buildAuthRequest(authReq);␊ |
374 | ␉std::string authURL = operatorURL;␊ |
375 | ␉unsigned int fulfillPos = authURL.rfind("Fulfill");␊ |
376 | ␉if (fulfillPos == (authURL.size() - (sizeof("Fulfill")-1)))␊ |
377 | ␉ authURL = authURL.substr(0, fulfillPos-1);␊ |
378 | ␉ByteArray replyData = sendRequest(authReq, authURL + "/Auth");␊ |
379 | ␊ |
380 | ␉pugi::xml_document initLicReq;␊ |
381 | ␉std::string activationURL = user->getProperty("//adept:activationURL");␊ |
382 | ␉buildInitLicenseServiceRequest(initLicReq, authURL);␊ |
383 | ␉sendRequest(initLicReq, activationURL + "/InitLicenseService");␊ |
384 | }␊ |
385 | ␊ |
386 | void DRMProcessor::operatorAuth(std::string operatorURL)␊ |
387 | {␊ |
388 | ␉pugi::xpath_node_set operatorList = user->getProperties("//adept:operatorURL");␊ |
389 | ␉␊ |
390 | ␉for (pugi::xpath_node_set::const_iterator operatorIt = operatorList.begin();␊ |
391 | ␉ operatorIt != operatorList.end(); ++operatorIt)␊ |
392 | ␉{␊ |
393 | ␉ std::string value = operatorIt->node().first_child().value();␊ |
394 | ␉ if (trim(value) == operatorURL)␊ |
395 | ␉ {␊ |
396 | ␉␉GOUROU_LOG(DEBUG, "Already authenticated to operator " << operatorURL);␊ |
397 | ␉␉return;␊ |
398 | ␉ }␊ |
399 | ␉}␊ |
400 | ␉␊ |
401 | ␉doOperatorAuth(operatorURL);␊ |
402 | ␉␊ |
403 | ␉// Add new operatorURL to list␊ |
404 | ␉pugi::xml_document activationDoc;␊ |
405 | ␉user->readActivation(activationDoc);␊ |
406 | ␊ |
407 | ␉pugi::xml_node root;␊ |
408 | ␉pugi::xpath_node xpathRes = activationDoc.select_node("//adept:operatorURLList");␊ |
409 | ␊ |
410 | ␉// Create adept:operatorURLList if it doesn't exists␊ |
411 | ␉if (!xpathRes)␊ |
412 | ␉{␊ |
413 | ␉ xpathRes = activationDoc.select_node("/activationInfo");␊ |
414 | ␉ root = xpathRes.node();␊ |
415 | ␉ root = root.append_child("adept:operatorURLList");␊ |
416 | ␉ root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
417 | ␊ |
418 | ␉ appendTextElem(root, "adept:user", user->getUUID());␊ |
419 | ␉}␊ |
420 | ␉else␊ |
421 | ␉ root = xpathRes.node();␊ |
422 | ␊ |
423 | ␉appendTextElem(root, "adept:operatorURL", operatorURL);␊ |
424 | ␊ |
425 | ␉user->updateActivationFile(activationDoc);␊ |
426 | }␊ |
427 | ␊ |
428 | void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)␊ |
429 | {␊ |
430 | ␉pugi::xml_node decl = fulfillReq.append_child(pugi::node_declaration);␊ |
431 | ␉decl.append_attribute("version") = "1.0";␊ |
432 | ␉␊ |
433 | ␉pugi::xml_node root = fulfillReq.append_child("adept:fulfill");␊ |
434 | ␉root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
435 | ␊ |
436 | ␉appendTextElem(root, "adept:user", user->getUUID());␊ |
437 | ␉appendTextElem(root, "adept:device", user->getDeviceUUID());␊ |
438 | ␉appendTextElem(root, "adept:deviceType", (*device)["deviceType"]);␊ |
439 | ␊ |
440 | ␉root.append_copy(acsmDoc.first_child());␊ |
441 | ␊ |
442 | ␉pugi::xml_node targetDevice = root.append_child("adept:targetDevice");␊ |
443 | ␉appendTextElem(targetDevice, "adept:softwareVersion", (*device)["hobbes"]);␊ |
444 | ␉appendTextElem(targetDevice, "adept:clientOS", (*device)["clientOS"]);␊ |
445 | ␉appendTextElem(targetDevice, "adept:clientLocale", (*device)["clientLocale"]);␊ |
446 | ␉appendTextElem(targetDevice, "adept:clientVersion", (*device)["deviceClass"]);␊ |
447 | ␉appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]);␊ |
448 | ␉appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]);␊ |
449 | ␉␊ |
450 | ␉pugi::xml_node activationToken = targetDevice.append_child("adept:activationToken");␊ |
451 | ␉appendTextElem(activationToken, "adept:user", user->getUUID());␊ |
452 | ␉appendTextElem(activationToken, "adept:device", user->getDeviceUUID());␊ |
453 | }␊ |
454 | ␊ |
455 | void DRMProcessor::fetchLicenseServiceCertificate(const std::string& licenseURL,␊ |
456 | ␉␉␉␉␉␉ const std::string& operatorURL)␊ |
457 | {␊ |
458 | ␉if (user->getLicenseServiceCertificate(licenseURL) != "")␊ |
459 | ␉ return;␊ |
460 | ␊ |
461 | ␉std::string licenseServiceInfoReq = operatorURL + "/LicenseServiceInfo?licenseURL=" + licenseURL;␊ |
462 | ␉␊ |
463 | ␉ByteArray replyData;␊ |
464 | ␉replyData = sendRequest(licenseServiceInfoReq);␊ |
465 | ␊ |
466 | ␉pugi::xml_document licenseServicesDoc;␊ |
467 | ␉licenseServicesDoc.load_buffer(replyData.data(), replyData.length());␊ |
468 | ␊ |
469 | ␉// Add new license certificate␊ |
470 | ␉pugi::xml_document activationDoc;␊ |
471 | ␉user->readActivation(activationDoc);␊ |
472 | ␊ |
473 | ␉pugi::xml_node root;␊ |
474 | ␉pugi::xpath_node xpathRes = activationDoc.select_node("//adept:licenseServices");␊ |
475 | ␊ |
476 | ␉// Create adept:licenseServices if it doesn't exists␊ |
477 | ␉if (!xpathRes)␊ |
478 | ␉{␊ |
479 | ␉ xpathRes = activationDoc.select_node("/activationInfo");␊ |
480 | ␉ root = xpathRes.node();␊ |
481 | ␉ root = root.append_child("adept:licenseServices");␊ |
482 | ␉ root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
483 | ␉}␊ |
484 | ␉else␊ |
485 | ␉ root = xpathRes.node();␊ |
486 | ␊ |
487 | ␉root = root.append_child("adept:licenseServiceInfo");␊ |
488 | ␊ |
489 | ␉std::string certificate = extractTextElem(licenseServicesDoc,␊ |
490 | ␉␉␉␉␉␉ "/licenseServiceInfo/certificate");␊ |
491 | ␊ |
492 | ␉appendTextElem(root, "adept:licenseURL", licenseURL);␊ |
493 | ␉appendTextElem(root, "adept:certificate", certificate);␊ |
494 | ␊ |
495 | ␉user->updateActivationFile(activationDoc);␊ |
496 | }␊ |
497 | ␊ |
498 | FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile)␊ |
499 | {␊ |
500 | ␉if (!user->getPKCS12().length())␊ |
501 | ␉ EXCEPTION(FF_NOT_ACTIVATED, "Device not activated");␊ |
502 | ␉␊ |
503 | ␉pugi::xml_document acsmDoc;␊ |
504 | ␊ |
505 | ␉if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8))␊ |
506 | ␉ EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile);␊ |
507 | ␊ |
508 | ␉// Could be an server internal error␊ |
509 | ␉pugi::xml_node rootNode = acsmDoc.first_child();␊ |
510 | ␉if (std::string(rootNode.name()) == "error")␊ |
511 | ␉{␊ |
512 | ␉ EXCEPTION(FF_SERVER_INTERNAL_ERROR, rootNode.attribute("data").value());␊ |
513 | ␉}␊ |
514 | ␊ |
515 | ␉GOUROU_LOG(INFO, "Fulfill " << ACSMFile);␊ |
516 | ␊ |
517 | ␉// Build req file␊ |
518 | ␉pugi::xml_document fulfillReq;␊ |
519 | ␊ |
520 | ␉buildFulfillRequest(acsmDoc, fulfillReq);␊ |
521 | ␉pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill");␊ |
522 | ␉rootNode = root.node();␊ |
523 | ␊ |
524 | ␉// Remove HMAC␊ |
525 | ␉pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac");␊ |
526 | ␊ |
527 | ␉if (!xpathRes)␊ |
528 | ␉ EXCEPTION(FF_NO_HMAC_IN_ACSM_FILE, "hmac tag not found in ACSM file");␊ |
529 | ␊ |
530 | ␉pugi::xml_node hmacNode = xpathRes.node();␊ |
531 | ␉pugi::xml_node hmacParentNode = hmacNode.parent();␊ |
532 | ␉␊ |
533 | ␉hmacParentNode.remove_child(hmacNode);␊ |
534 | ␊ |
535 | ␉signNode(rootNode);␊ |
536 | ␉␊ |
537 | ␉// Add removed HMAC␊ |
538 | ␉appendTextElem(hmacParentNode, hmacNode.name(), hmacNode.first_child().value());␊ |
539 | ␊ |
540 | ␉pugi::xpath_node node = acsmDoc.select_node("//operatorURL");␊ |
541 | ␉if (!node)␊ |
542 | ␉ EXCEPTION(FF_NO_OPERATOR_URL, "OperatorURL not found in ACSM document");␊ |
543 | ␉␊ |
544 | ␉std::string operatorURL = node.node().first_child().value();␊ |
545 | ␉operatorURL = trim(operatorURL);␊ |
546 | ␉std::string fulfillURL = operatorURL + "/Fulfill";␊ |
547 | ␊ |
548 | ␉operatorAuth(fulfillURL);␊ |
549 | ␉␊ |
550 | ␉ByteArray replyData;␊ |
551 | ␊ |
552 | ␉try␊ |
553 | ␉{␊ |
554 | ␉ replyData = sendRequest(fulfillReq, fulfillURL);␊ |
555 | ␉}␊ |
556 | ␉catch (gourou::Exception& e)␊ |
557 | ␉{␊ |
558 | ␉ /*␊ |
559 | ␉ Operator requires authentication even if it's already in ␊ |
560 | ␉ our operator list␊ |
561 | ␉ */␊ |
562 | ␉ std::string errorMsg(e.what());␊ |
563 | ␉ if (e.getErrorCode() == GOUROU_ADEPT_ERROR &&␊ |
564 | ␉␉errorMsg.find("E_ADEPT_DISTRIBUTOR_AUTH") != std::string::npos)␊ |
565 | ␉ {␊ |
566 | ␉␉doOperatorAuth(fulfillURL);␊ |
567 | ␉␉replyData = sendRequest(fulfillReq, fulfillURL);␊ |
568 | ␉ }␊ |
569 | ␉ else␊ |
570 | ␉ {␊ |
571 | ␉␉throw e;␊ |
572 | ␉ }␊ |
573 | ␉}␊ |
574 | ␊ |
575 | ␉pugi::xml_document fulfillReply;␊ |
576 | ␊ |
577 | ␉fulfillReply.load_string((const char*)replyData.data());␊ |
578 | ␊ |
579 | ␉std::string licenseURL = extractTextElem(fulfillReply, "//licenseToken/licenseURL");␊ |
580 | ␉␊ |
581 | ␉fetchLicenseServiceCertificate(licenseURL, operatorURL);␊ |
582 | ␊ |
583 | ␉return new FulfillmentItem(fulfillReply, user);␊ |
584 | }␊ |
585 | ␊ |
586 | DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path, bool resume)␊ |
587 | {␊ |
588 | ␉ITEM_TYPE res = EPUB;␊ |
589 | ␉␊ |
590 | ␉if (!item)␊ |
591 | ␉ EXCEPTION(DW_NO_ITEM, "No item");␊ |
592 | ␊ |
593 | ␉std::map<std::string, std::string> headers;␊ |
594 | ␊ |
595 | ␉int fd = createNewFile(path, !resume);␊ |
596 | ␉␊ |
597 | ␉sendRequest(item->getDownloadURL(), "", 0, &headers, fd, resume);␊ |
598 | ␊ |
599 | ␉close(fd);␊ |
600 | ␊ |
601 | ␉GOUROU_LOG(INFO, "Download into " << path);␊ |
602 | ␊ |
603 | ␉ByteArray rightsStr(item->getRights());␊ |
604 | ␊ |
605 | ␉if (item->getMetadata("format").find("application/pdf") != std::string::npos)␊ |
606 | ␉ res = PDF;␊ |
607 | ␊ |
608 | ␉if (headers.count("Content-Type") &&␊ |
609 | ␉ headers["Content-Type"].find("application/pdf") != std::string::npos)␊ |
610 | ␉ res = PDF;␊ |
611 | ␉ ␊ |
612 | ␉if (res == EPUB)␊ |
613 | ␉{␊ |
614 | ␉ void* handler = client->zipOpen(path);␊ |
615 | ␉ client->zipWriteFile(handler, "META-INF/rights.xml", rightsStr);␊ |
616 | ␉ client->zipClose(handler);␊ |
617 | ␉}␊ |
618 | ␉else if (res == PDF)␊ |
619 | ␉{␊ |
620 | ␉ uPDFParser::Parser parser;␊ |
621 | ␉ bool EBXHandlerFound = false;␊ |
622 | ␉ ␊ |
623 | ␉ try␊ |
624 | ␉ {␊ |
625 | ␉␉GOUROU_LOG(DEBUG, "Parse PDF");␊ |
626 | ␉␉parser.parse(path);␊ |
627 | ␉ }␊ |
628 | ␉ catch(std::invalid_argument& e)␊ |
629 | ␉ {␊ |
630 | ␉␉GOUROU_LOG(ERROR, "Invalid PDF");␊ |
631 | ␉␉return res;␊ |
632 | ␉ }␊ |
633 | ␊ |
634 | ␉ std::vector<uPDFParser::Object*> objects = parser.objects();␊ |
635 | ␉ std::vector<uPDFParser::Object*>::reverse_iterator it;␊ |
636 | ␊ |
637 | ␉ for(it = objects.rbegin(); it != objects.rend(); it++)␊ |
638 | ␉ {␊ |
639 | ␉␉// Update EBX_HANDLER with rights␊ |
640 | ␉␉if ((*it)->hasKey("Filter") && (**it)["Filter"]->str() == "/EBX_HANDLER")␊ |
641 | ␉␉{␊ |
642 | ␉␉ EBXHandlerFound = true;␊ |
643 | ␉␉ uPDFParser::Object* ebx = (*it)->clone();␊ |
644 | ␉␉ (*ebx)["ADEPT_ID"] = new uPDFParser::String(item->getResource());␊ |
645 | ␉␉ (*ebx)["EBX_BOOKID"] = new uPDFParser::String(item->getResource());␊ |
646 | ␉␉ ByteArray zipped;␊ |
647 | ␉␉ client->deflate(rightsStr, zipped);␊ |
648 | ␉␉ (*ebx)["ADEPT_LICENSE"] = new uPDFParser::String(zipped.toBase64());␊ |
649 | ␉␉ parser.addObject(ebx);␊ |
650 | ␉␉ break;␊ |
651 | ␉␉}␊ |
652 | ␉ }␊ |
653 | ␊ |
654 | ␉ if (EBXHandlerFound)␊ |
655 | ␉␉parser.write(path, true);␊ |
656 | ␉ else␊ |
657 | ␉ {␊ |
658 | ␉␉EXCEPTION(DW_NO_EBX_HANDLER, "EBX_HANDLER not found");␊ |
659 | ␉ }␊ |
660 | ␉}␊ |
661 | ␊ |
662 | ␉return res;␊ |
663 | }␊ |
664 | ␊ |
665 | void DRMProcessor::buildSignInRequest(pugi::xml_document& signInRequest,␊ |
666 | ␉␉␉␉␉ const std::string& adobeID, const std::string& adobePassword,␊ |
667 | ␉␉␉␉␉ const std::string& authenticationCertificate)␊ |
668 | {␊ |
669 | ␉pugi::xml_node decl = signInRequest.append_child(pugi::node_declaration);␊ |
670 | ␉decl.append_attribute("version") = "1.0";␊ |
671 | ␉pugi::xml_node signIn = signInRequest.append_child("adept:signIn");␊ |
672 | ␉signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
673 | ␉std::string loginMethod = user->getLoginMethod();␊ |
674 | ␊ |
675 | ␉if (adobeID == "anonymous")␊ |
676 | ␉ signIn.append_attribute("method") = "anonymous";␊ |
677 | ␉else if (loginMethod.size())␊ |
678 | ␉ signIn.append_attribute("method") = loginMethod.c_str();␊ |
679 | ␉else␊ |
680 | ␉ signIn.append_attribute("method") = "AdobeID";␊ |
681 | ␊ |
682 | ␉unsigned char encryptedSignInData[RSA_KEY_SIZE];␊ |
683 | ␉const unsigned char* deviceKey = device->getDeviceKey();␊ |
684 | ␊ |
685 | ␉ByteArray _authenticationCertificate = ByteArray::fromBase64(authenticationCertificate);␊ |
686 | ␊ |
687 | ␉// Build buffer <deviceKey> <len username> <username> <len password> <password>␊ |
688 | ␉ByteArray ar(deviceKey, Device::DEVICE_KEY_SIZE);␊ |
689 | ␉ar.append((unsigned char)adobeID.length());␊ |
690 | ␉ar.append(adobeID);␊ |
691 | ␉ar.append((unsigned char)adobePassword.length());␊ |
692 | ␉ar.append(adobePassword);␊ |
693 | ␊ |
694 | ␉// Encrypt with authentication certificate (public part)␊ |
695 | ␉client->RSAPublicEncrypt(_authenticationCertificate.data(),␊ |
696 | ␉␉␉␉ _authenticationCertificate.length(),␊ |
697 | ␉␉␉␉ RSAInterface::RSA_KEY_X509,␊ |
698 | ␉␉␉␉ ar.data(), ar.length(), encryptedSignInData);␊ |
699 | ␊ |
700 | ␉ar = ByteArray(encryptedSignInData, sizeof(encryptedSignInData));␊ |
701 | ␉appendTextElem(signIn, "adept:signInData", ar.toBase64());␊ |
702 | ␉␊ |
703 | ␉// Generate Auth key and License Key␊ |
704 | ␉void* rsaAuth = client->generateRSAKey(RSA_KEY_SIZE_BITS);␊ |
705 | ␉void* rsaLicense = client->generateRSAKey(RSA_KEY_SIZE_BITS);␊ |
706 | ␊ |
707 | ␉std::string serializedData = serializeRSAPublicKey(rsaAuth);␊ |
708 | ␉appendTextElem(signIn, "adept:publicAuthKey", serializedData);␊ |
709 | ␉serializedData = serializeRSAPrivateKey(rsaAuth);␊ |
710 | ␉appendTextElem(signIn, "adept:encryptedPrivateAuthKey", serializedData.data());␊ |
711 | ␊ |
712 | ␉serializedData = serializeRSAPublicKey(rsaLicense);␊ |
713 | ␉appendTextElem(signIn, "adept:publicLicenseKey", serializedData.data());␊ |
714 | ␉serializedData = serializeRSAPrivateKey(rsaLicense);␊ |
715 | ␉appendTextElem(signIn, "adept:encryptedPrivateLicenseKey", serializedData.data());␊ |
716 | ␊ |
717 | ␉client->destroyRSAHandler(rsaAuth);␊ |
718 | ␉client->destroyRSAHandler(rsaLicense);␊ |
719 | }␊ |
720 | ␊ |
721 | void DRMProcessor::signIn(const std::string& adobeID, const std::string& adobePassword)␊ |
722 | {␊ |
723 | ␉pugi::xml_document signInRequest;␊ |
724 | ␉std::string authenticationCertificate = user->getAuthenticationCertificate();␊ |
725 | ␉␊ |
726 | ␉buildSignInRequest(signInRequest, adobeID, adobePassword, authenticationCertificate);␊ |
727 | ␊ |
728 | ␉GOUROU_LOG(INFO, "SignIn " << adobeID);␊ |
729 | ␉␊ |
730 | ␉std::string signInURL = user->getProperty("//adept:authURL");␊ |
731 | ␉signInURL += "/SignInDirect";␊ |
732 | ␊ |
733 | ␉ByteArray credentials = sendRequest(signInRequest, signInURL);␊ |
734 | ␉␊ |
735 | ␉pugi::xml_document credentialsDoc;␊ |
736 | ␉if (!credentialsDoc.load_buffer(credentials.data(), credentials.length()))␊ |
737 | ␉ EXCEPTION(SIGN_INVALID_CREDENTIALS, "Invalid credentials reply");␊ |
738 | ␊ |
739 | ␉struct adeptWalker: pugi::xml_tree_walker␊ |
740 | ␉{␊ |
741 | ␉ void changeName(pugi::xml_node& node)␊ |
742 | ␉ {␊ |
743 | ␉␉std::string name = std::string("adept:") + node.name();␊ |
744 | ␉␉node.set_name(name.c_str());␊ |
745 | ␉ }␊ |
746 | ␉ ␊ |
747 | ␉ bool begin(pugi::xml_node& node)␊ |
748 | ␉ {␊ |
749 | ␉␉changeName(node);␊ |
750 | ␉␉return true;␊ |
751 | ␉ }␊ |
752 | ␉ ␊ |
753 | ␉ virtual bool for_each(pugi::xml_node& node)␊ |
754 | ␉ {␊ |
755 | ␉␉if (node.type() == pugi::node_element)␊ |
756 | ␉␉ changeName(node);␊ |
757 | ␉␉return true; // continue traversal␊ |
758 | ␉ }␊ |
759 | ␉} adeptWalker;␊ |
760 | ␊ |
761 | ␉pugi::xml_node credentialsNode = credentialsDoc.first_child();␊ |
762 | ␊ |
763 | ␉if (std::string(credentialsNode.name()) != "credentials")␊ |
764 | ␉ EXCEPTION(SIGN_INVALID_CREDENTIALS, "Invalid credentials reply");␊ |
765 | ␉␊ |
766 | ␉pugi::xpath_node encryptedPrivateLicenseKey = credentialsNode.select_node("encryptedPrivateLicenseKey");␊ |
767 | ␉const char* privateKeyData = encryptedPrivateLicenseKey.node().first_child().value();␊ |
768 | ␉ByteArray privateKeyDataStr = ByteArray::fromBase64(privateKeyData);␊ |
769 | ␉ByteArray privateKey = decryptWithDeviceKey(privateKeyDataStr.data(), privateKeyDataStr.length());␊ |
770 | ␉credentialsNode.remove_child(encryptedPrivateLicenseKey.node());␊ |
771 | ␉appendTextElem(credentialsNode, "privateLicenseKey", privateKey.toBase64().data());␊ |
772 | ␊ |
773 | ␉// Add "adept:" prefix to all nodes␊ |
774 | ␉credentialsNode.remove_attribute("xmlns");␊ |
775 | ␉credentialsNode.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
776 | ␉credentialsNode.traverse(adeptWalker);␊ |
777 | ␉␊ |
778 | ␉appendTextElem(credentialsNode, "adept:authenticationCertificate", authenticationCertificate.data());␊ |
779 | ␊ |
780 | ␉pugi::xml_document activationDoc;␊ |
781 | ␉user->readActivation(activationDoc);␊ |
782 | ␉pugi::xml_node activationInfo = activationDoc.select_node("activationInfo").node();␊ |
783 | ␉activationInfo.append_copy(credentialsNode);␊ |
784 | ␊ |
785 | ␉user->updateActivationFile(activationDoc);␊ |
786 | }␊ |
787 | ␊ |
788 | void DRMProcessor::buildActivateReq(pugi::xml_document& activateReq)␊ |
789 | {␊ |
790 | ␉pugi::xml_node decl = activateReq.append_child(pugi::node_declaration);␊ |
791 | ␉decl.append_attribute("version") = "1.0";␊ |
792 | ␉␊ |
793 | ␉pugi::xml_node root = activateReq.append_child("adept:activate");␊ |
794 | ␉root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
795 | ␉root.append_attribute("requestType") = "initial";␊ |
796 | ␊ |
797 | ␉appendTextElem(root, "adept:fingerprint", (*device)["fingerprint"]);␊ |
798 | ␉appendTextElem(root, "adept:deviceType", (*device)["deviceType"]);␊ |
799 | ␉appendTextElem(root, "adept:clientOS", (*device)["clientOS"]);␊ |
800 | ␉appendTextElem(root, "adept:clientLocale", (*device)["clientLocale"]);␊ |
801 | ␉appendTextElem(root, "adept:clientVersion", (*device)["deviceClass"]);␊ |
802 | ␊ |
803 | ␉pugi::xml_node targetDevice = root.append_child("adept:targetDevice");␊ |
804 | ␉appendTextElem(targetDevice, "adept:softwareVersion", (*device)["hobbes"]);␊ |
805 | ␉appendTextElem(targetDevice, "adept:clientOS", (*device)["clientOS"]);␊ |
806 | ␉appendTextElem(targetDevice, "adept:clientLocale", (*device)["clientLocale"]);␊ |
807 | ␉appendTextElem(targetDevice, "adept:clientVersion", (*device)["deviceClass"]);␊ |
808 | ␉appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]);␊ |
809 | ␉appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]);␊ |
810 | ␊ |
811 | ␉addNonce(root);␊ |
812 | ␉␊ |
813 | ␉appendTextElem(root, "adept:user", user->getUUID());␊ |
814 | }␊ |
815 | ␊ |
816 | void DRMProcessor::activateDevice()␊ |
817 | {␊ |
818 | ␉pugi::xml_document activateReq;␊ |
819 | ␊ |
820 | ␉GOUROU_LOG(INFO, "Activate device");␊ |
821 | ␊ |
822 | ␉buildActivateReq(activateReq);␊ |
823 | ␊ |
824 | ␉pugi::xml_node root = activateReq.select_node("adept:activate").node();␊ |
825 | ␊ |
826 | ␉signNode(root);␊ |
827 | ␊ |
828 | ␉pugi::xml_document activationDoc;␊ |
829 | ␉user->readActivation(activationDoc);␊ |
830 | ␊ |
831 | ␉std::string activationURL = user->getProperty("//adept:activationURL");␊ |
832 | ␉activationURL += "/Activate";␊ |
833 | ␉␊ |
834 | ␉ByteArray reply = sendRequest(activateReq, activationURL);␊ |
835 | ␊ |
836 | ␉pugi::xml_document activationToken;␊ |
837 | ␉activationToken.load_buffer(reply.data(), reply.length());␊ |
838 | ␉␊ |
839 | ␉root = activationDoc.select_node("activationInfo").node();␊ |
840 | ␉root.append_copy(activationToken.first_child());␊ |
841 | ␉user->updateActivationFile(activationDoc);␊ |
842 | }␊ |
843 | ␊ |
844 | void DRMProcessor::buildReturnReq(pugi::xml_document& returnReq, const std::string& loanID, const std::string& operatorURL)␊ |
845 | {␊ |
846 | ␉pugi::xml_node decl = returnReq.append_child(pugi::node_declaration);␊ |
847 | ␉decl.append_attribute("version") = "1.0";␊ |
848 | ␉␊ |
849 | ␉pugi::xml_node root = returnReq.append_child("adept:loanReturn");␊ |
850 | ␉root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
851 | ␊ |
852 | ␉appendTextElem(root, "adept:user", user->getUUID());␊ |
853 | ␉appendTextElem(root, "adept:device", user->getDeviceUUID());␊ |
854 | ␉appendTextElem(root, "adept:loan", loanID);␊ |
855 | ␊ |
856 | ␉addNonce(root);␊ |
857 | ␉signNode(root);␊ |
858 | }␊ |
859 | ␊ |
860 | std::string DRMProcessor::getDefaultAdeptDir(void)␊ |
861 | {␊ |
862 | #ifndef DEFAULT_ADEPT_DIR␊ |
863 | ␉const char* user = getenv("USER");␊ |
864 | ␉␊ |
865 | ␉if (user && user[0])␊ |
866 | ␉{␊ |
867 | ␉ return std::string("/home/") + user + std::string("/.config/adept/");␊ |
868 | ␉}␊ |
869 | ␉else␊ |
870 | ␉ return LOCAL_ADEPT_DIR;␊ |
871 | #else␊ |
872 | ␉return DEFAULT_ADEPT_DIR "/";␊ |
873 | #endif␊ |
874 | }␊ |
875 | ␊ |
876 | void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL)␊ |
877 | {␊ |
878 | ␉pugi::xml_document returnReq;␊ |
879 | ␊ |
880 | ␉GOUROU_LOG(INFO, "Return loan " << loanID);␊ |
881 | ␊ |
882 | ␉buildReturnReq(returnReq, loanID, operatorURL);␊ |
883 | ␊ |
884 | ␉sendRequest(returnReq, operatorURL + "/LoanReturn");␊ |
885 | }␊ |
886 | ␊ |
887 | ByteArray DRMProcessor::encryptWithDeviceKey(const unsigned char* data, unsigned int len)␊ |
888 | {␊ |
889 | ␉const unsigned char* deviceKey = device->getDeviceKey();␊ |
890 | ␉unsigned int outLen;␊ |
891 | ␉int remain = 0;␊ |
892 | ␉if ((len % 16))␊ |
893 | ␉ remain = 16 - (len%16);␊ |
894 | ␉int encrypted_data_len = 16 + len + remain; // IV + data + pad␊ |
895 | ␉unsigned char* encrypted_data = new unsigned char[encrypted_data_len];␊ |
896 | ␉␊ |
897 | ␉// Generate IV in front␊ |
898 | ␉client->randBytes(encrypted_data, 16);␊ |
899 | ␉ ␊ |
900 | ␉client->encrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,␊ |
901 | ␉␉␉deviceKey, 16, encrypted_data, 16,␊ |
902 | ␉␉␉data, len,␊ |
903 | ␉␉␉encrypted_data+16, &outLen);␊ |
904 | ␊ |
905 | ␉ByteArray res(encrypted_data, outLen+16);␊ |
906 | ␊ |
907 | ␉delete[] encrypted_data;␊ |
908 | ␊ |
909 | ␉return res;␊ |
910 | }␊ |
911 | ␊ |
912 | /* First 16 bytes of data is IV for CBC chaining */␊ |
913 | ByteArray DRMProcessor::decryptWithDeviceKey(const unsigned char* data, unsigned int len)␊ |
914 | {␊ |
915 | ␉unsigned int outLen;␊ |
916 | ␉const unsigned char* deviceKey = device->getDeviceKey();␊ |
917 | ␉unsigned char* decrypted_data = new unsigned char[len-16];␊ |
918 | ␊ |
919 | ␉client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,␊ |
920 | ␉␉␉deviceKey, 16, data, 16,␊ |
921 | ␉␉␉data+16, len-16,␊ |
922 | ␉␉␉decrypted_data, &outLen);␊ |
923 | ␊ |
924 | ␉ByteArray res(decrypted_data, outLen);␊ |
925 | ␊ |
926 | ␉delete[] decrypted_data;␊ |
927 | ␊ |
928 | ␉return res;␊ |
929 | }␊ |
930 | ␊ |
931 | std::string DRMProcessor::serializeRSAPublicKey(void* rsa)␊ |
932 | {␊ |
933 | ␉unsigned char* data = 0;␊ |
934 | ␉unsigned int len;␊ |
935 | ␉␊ |
936 | ␉client->extractRSAPublicKey(rsa, &data, &len);␊ |
937 | ␊ |
938 | ␉ByteArray res(data, len);␊ |
939 | ␊ |
940 | ␉free(data);␊ |
941 | ␉␊ |
942 | ␉return res.toBase64();␊ |
943 | }␊ |
944 | ␊ |
945 | std::string DRMProcessor::serializeRSAPrivateKey(void* rsa)␊ |
946 | {␊ |
947 | ␉unsigned char* data = 0;␊ |
948 | ␉unsigned int len;␊ |
949 | ␉␊ |
950 | ␉client->extractRSAPrivateKey(rsa, &data, &len);␊ |
951 | ␊ |
952 | ␉ByteArray res = encryptWithDeviceKey(data, len);␊ |
953 | ␊ |
954 | ␉free(data);␊ |
955 | ␉␊ |
956 | ␉return res.toBase64();␊ |
957 | }␊ |
958 | ␊ |
959 | void DRMProcessor::exportPrivateLicenseKey(std::string path)␊ |
960 | {␊ |
961 | ␉int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU);␊ |
962 | ␉int ret;␊ |
963 | ␉␊ |
964 | ␉if (fd <= 0)␊ |
965 | ␉ EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);␊ |
966 | ␊ |
967 | ␉ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey());␊ |
968 | ␉/* In adobekey.py, we get base64 decoded data [26:] */␊ |
969 | ␉ret = write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26);␊ |
970 | ␉close(fd);␊ |
971 | ␉if (ret != (int)(privateLicenseKey.length()-26))␊ |
972 | ␉{␊ |
973 | ␉ EXCEPTION(gourou::GOUROU_FILE_ERROR, "Error writing " << path);␊ |
974 | ␉}␊ |
975 | }␊ |
976 | ␊ |
977 | int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;}␊ |
978 | void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;}␊ |
979 | ␊ |
980 | /**␊ |
981 | * RSA Key can be over encrypted with AES128-CBC if keyType attribute is set␊ |
982 | * remainder = keyType % 16␊ |
983 | * Key = SHA256(keyType)[remainder*2:remainder*2+(16-remainder)] || SHA256(keyType)[16-remainder:16]␊ |
984 | * IV = DeviceID ^ FulfillmentId ^ VoucherId␊ |
985 | *␊ |
986 | * @return Base64 encoded decrypted key␊ |
987 | */␊ |
988 | std::string DRMProcessor::encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType)␊ |
989 | {␊ |
990 | ␉unsigned char digest[32], key[16], iv[16];␊ |
991 | ␉unsigned int dataOutLength;␊ |
992 | ␉std::string id;␊ |
993 | ␉␉␊ |
994 | ␉client->digest("SHA256", (unsigned char*)keyType.c_str(), keyType.size(), digest);␊ |
995 | ␊ |
996 | ␉dumpBuffer(gourou::LG_LOG_DEBUG, "SHA of KeyType : ", digest, sizeof(digest));␊ |
997 | ␊ |
998 | ␉long nonce = std::stol(keyType);␊ |
999 | ␉int remainder = nonce % 16;␊ |
1000 | ␊ |
1001 | ␉memcpy(key, &digest[remainder*2], 16-remainder);␊ |
1002 | ␉memcpy(&key[16-remainder], &digest[remainder], remainder);␊ |
1003 | ␉␊ |
1004 | ␉id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/device");␊ |
1005 | ␉if (id == "")␊ |
1006 | ␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Device id not found in rights.xml");␊ |
1007 | ␉ByteArray deviceId = ByteArray::fromHex(extractIdFromUUID(id));␊ |
1008 | ␉unsigned char* _deviceId = deviceId.data();␊ |
1009 | ␉␉␊ |
1010 | ␉id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/fulfillment");␊ |
1011 | ␉if (id == "")␊ |
1012 | ␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Fulfillment id not found in rights.xml");␊ |
1013 | ␉ByteArray fulfillmentId = ByteArray::fromHex(extractIdFromUUID(id));␊ |
1014 | ␉unsigned char* _fulfillmentId = fulfillmentId.data();␊ |
1015 | ␉␉␊ |
1016 | ␉id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/voucher");␊ |
1017 | ␉if (id == "")␊ |
1018 | ␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Voucher id not found in rights.xml");␊ |
1019 | ␉ByteArray voucherId = ByteArray::fromHex(extractIdFromUUID(id));␊ |
1020 | ␉unsigned char* _voucherId = voucherId.data();␊ |
1021 | ␉␉␊ |
1022 | ␉if (deviceId.size() < sizeof(iv) || fulfillmentId.size() < sizeof(iv) || voucherId.size() < sizeof(iv))␊ |
1023 | ␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "One id has a bad length");␊ |
1024 | ␊ |
1025 | ␉for(unsigned int i=0; i<sizeof(iv); i++)␊ |
1026 | ␉ iv[i] = _deviceId[i] ^ _fulfillmentId[i] ^ _voucherId[i];␊ |
1027 | ␊ |
1028 | ␉dumpBuffer(gourou::LG_LOG_DEBUG, "First pass key : ", key, sizeof(key));␊ |
1029 | ␉dumpBuffer(gourou::LG_LOG_DEBUG, "First pass IV : ", iv, sizeof(iv));␊ |
1030 | ␊ |
1031 | ␉ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);␊ |
1032 | ␉unsigned char* clearRSAKey = new unsigned char[arrayEncryptedKey.size()];␊ |
1033 | ␊ |
1034 | ␉client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,␊ |
1035 | ␉␉␉(const unsigned char*)key, (unsigned int)sizeof(key),␊ |
1036 | ␉␉␉(const unsigned char*)iv, (unsigned int)sizeof(iv),␊ |
1037 | ␉␉␉(const unsigned char*)arrayEncryptedKey.data(), arrayEncryptedKey.size(),␊ |
1038 | ␉␉␉(unsigned char*)clearRSAKey, &dataOutLength);␊ |
1039 | ␊ |
1040 | ␉dumpBuffer(gourou::LG_LOG_DEBUG, "\nDecrypted key : ", clearRSAKey, dataOutLength);␊ |
1041 | ␊ |
1042 | ␉/* Last block could be 0x10*16 which is OpenSSL padding, remove it if it's the case */␊ |
1043 | ␉bool skipLastLine = true;␊ |
1044 | ␉for(unsigned int i=dataOutLength-16; i<dataOutLength; i++)␊ |
1045 | ␉{␊ |
1046 | ␉ if (clearRSAKey[i] != 0x10)␊ |
1047 | ␉ {␊ |
1048 | ␉␉skipLastLine = false;␊ |
1049 | ␉␉break;␊ |
1050 | ␉ }␊ |
1051 | ␉}␊ |
1052 | ␊ |
1053 | ␉ByteArray res(clearRSAKey, (skipLastLine)?dataOutLength-16:dataOutLength);␊ |
1054 | ␊ |
1055 | ␉delete[] clearRSAKey;␊ |
1056 | ␊ |
1057 | ␉return res.toBase64();␊ |
1058 | }␊ |
1059 | ␊ |
1060 | void DRMProcessor::decryptADEPTKey(pugi::xml_document& rightsDoc, unsigned char* decryptedKey, const unsigned char* encryptionKey, unsigned encryptionKeySize)␊ |
1061 | {␊ |
1062 | ␉unsigned char rsaKey[RSA_KEY_SIZE];␊ |
1063 | ␉␊ |
1064 | ␉std::string user = extractTextElem(rightsDoc, "/adept:rights/licenseToken/user");␊ |
1065 | ␊ |
1066 | ␉if (!encryptionKey)␊ |
1067 | ␉{␊ |
1068 | ␉ if (this->user->getUUID() != user)␊ |
1069 | ␉ {␊ |
1070 | ␉␉EXCEPTION(DRM_INVALID_USER, "This book has been downloaded for another user (" << user << ")");␊ |
1071 | ␉ }␊ |
1072 | ␊ |
1073 | ␉ std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");␊ |
1074 | ␉ std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);␊ |
1075 | ␊ |
1076 | ␉ if (keyType != "")␊ |
1077 | ␉␉encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType);␊ |
1078 | ␉ ␊ |
1079 | ␉ if (encryptedKey.size() != 172)␊ |
1080 | ␉␉EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported");␊ |
1081 | ␊ |
1082 | ␉ ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);␊ |
1083 | ␊ |
1084 | ␉ std::string privateKeyData = this->user->getPrivateLicenseKey();␊ |
1085 | ␉ ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData);␊ |
1086 | ␉␊ |
1087 | ␉ dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length());␊ |
1088 | ␊ |
1089 | ␉ client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(),␊ |
1090 | ␉␉␉␉ RSAInterface::RSA_KEY_PKCS8, "",␊ |
1091 | ␉␉␉␉ arrayEncryptedKey.data(), arrayEncryptedKey.length(), rsaKey);␊ |
1092 | ␊ |
1093 | ␉ dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", rsaKey, sizeof(rsaKey));␊ |
1094 | ␊ |
1095 | ␉ if (rsaKey[0] != 0x00 || rsaKey[1] != 0x02 ||␊ |
1096 | ␉␉rsaKey[RSA_KEY_SIZE-16-1] != 0x00)␊ |
1097 | ␉␉EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");␊ |
1098 | ␊ |
1099 | ␉ memcpy(decryptedKey, &rsaKey[sizeof(rsaKey)-16], 16);␊ |
1100 | ␉}␊ |
1101 | ␉else␊ |
1102 | ␉{␊ |
1103 | ␉ GOUROU_LOG(DEBUG, "Use provided encryption key");␊ |
1104 | ␉ if (encryptionKeySize != 16)␊ |
1105 | ␉␉EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");␊ |
1106 | ␊ |
1107 | ␉ memcpy(decryptedKey, encryptionKey, encryptionKeySize);␊ |
1108 | ␉}␊ |
1109 | }␊ |
1110 | ␊ |
1111 | void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut,␊ |
1112 | ␉␉␉␉ const unsigned char* encryptionKey, unsigned encryptionKeySize)␊ |
1113 | {␊ |
1114 | ␉ByteArray zipData;␊ |
1115 | ␉bool removeEncryptionXML = true;␊ |
1116 | ␉void* zipHandler = client->zipOpen(filenameOut);␊ |
1117 | ␊ |
1118 | ␉client->zipReadFile(zipHandler, "META-INF/rights.xml", zipData);␊ |
1119 | ␉pugi::xml_document rightsDoc;␊ |
1120 | ␉rightsDoc.load_string((const char*)zipData.data());␊ |
1121 | ␊ |
1122 | ␉unsigned char decryptedKey[16];␊ |
1123 | ␊ |
1124 | ␉decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize);␊ |
1125 | ␉␊ |
1126 | ␉client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData);␊ |
1127 | ␉pugi::xml_document encryptionDoc;␊ |
1128 | ␉encryptionDoc.load_string((const char*)zipData.data());␊ |
1129 | ␊ |
1130 | ␉pugi::xpath_node_set nodeSet = encryptionDoc.select_nodes("//EncryptedData");␊ |
1131 | ␊ |
1132 | ␉for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();␊ |
1133 | ␉ it != nodeSet.end(); ++it)␊ |
1134 | ␉{␊ |
1135 | ␉ pugi::xml_node encryptionMethod = it->node().child("EncryptionMethod");␊ |
1136 | ␉ pugi::xml_node cipherReference = it->node().child("CipherData").child("CipherReference");␊ |
1137 | ␊ |
1138 | ␉ std::string encryptionType = encryptionMethod.attribute("Algorithm").value();␊ |
1139 | ␉ std::string encryptedFile = cipherReference.attribute("URI").value();␊ |
1140 | ␉ ␊ |
1141 | ␉ if (encryptionType == "")␊ |
1142 | ␉ {␊ |
1143 | ␉␉EXCEPTION(DRM_MISSING_PARAMETER, "Missing Algorithm attribute in encryption.xml");␊ |
1144 | ␉ }␊ |
1145 | ␉ else if (encryptionType == "http://www.w3.org/2001/04/xmlenc#aes128-cbc")␊ |
1146 | ␉ {␊ |
1147 | ␉␉if (encryptedFile == "")␊ |
1148 | ␉␉{␊ |
1149 | ␉␉ EXCEPTION(DRM_MISSING_PARAMETER, "Missing URI attribute in encryption.xml");␊ |
1150 | ␉␉}␊ |
1151 | ␊ |
1152 | ␉␉GOUROU_LOG(DEBUG, "Encrypted file " << encryptedFile);␊ |
1153 | ␊ |
1154 | ␉␉client->zipReadFile(zipHandler, encryptedFile, zipData, false);␊ |
1155 | ␉ ␊ |
1156 | ␉␉unsigned char* _data = zipData.data();␊ |
1157 | ␉␉ByteArray clearData(zipData.length()-16+1, true); /* Reserve 1 byte for 'Z' */␊ |
1158 | ␉␉unsigned char* _clearData = clearData.data();␊ |
1159 | ␉␉gourou::ByteArray inflateData(true);␊ |
1160 | ␉␉unsigned int dataOutLength;␊ |
1161 | ␊ |
1162 | ␉␉client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,␊ |
1163 | ␉␉␉␉decryptedKey, sizeof(decryptedKey), /* Key */␊ |
1164 | ␉␉␉␉_data, 16, /* IV */␊ |
1165 | ␉␉␉␉&_data[16], zipData.length()-16,␊ |
1166 | ␉␉␉␉_clearData, &dataOutLength);␊ |
1167 | ␊ |
1168 | ␉␉// Add 'Z' at the end, done in ineptepub.py␊ |
1169 | ␉␉_clearData[dataOutLength] = 'Z';␊ |
1170 | ␉␉clearData.resize(dataOutLength+1);␊ |
1171 | ␊ |
1172 | ␉␉try␊ |
1173 | ␉␉{␊ |
1174 | ␉␉ client->inflate(clearData, inflateData);␊ |
1175 | ␉␉ client->zipWriteFile(zipHandler, encryptedFile, inflateData);␊ |
1176 | ␉␉}␊ |
1177 | ␉␉catch(gourou::Exception& e)␊ |
1178 | ␉␉{␊ |
1179 | ␉␉ if (e.getErrorCode() == CLIENT_ZIP_ERROR)␊ |
1180 | ␉␉ {␊ |
1181 | ␉␉␉GOUROU_LOG(ERROR, e.what() << std::endl << "Skip file " << encryptedFile);␊ |
1182 | ␉␉ }␊ |
1183 | ␉␉ else␊ |
1184 | ␉␉␉throw e;␊ |
1185 | ␉␉}␊ |
1186 | ␊ |
1187 | ␉␉it->node().parent().remove_child(it->node());␊ |
1188 | ␉ }␊ |
1189 | ␉ else␊ |
1190 | ␉ {␊ |
1191 | ␉␉GOUROU_LOG(WARN, "Unsupported encryption algorithm " << encryptionType << ", for file " << encryptedFile);␊ |
1192 | ␉␉removeEncryptionXML = false;␊ |
1193 | ␉ }␊ |
1194 | ␉}␊ |
1195 | ␉␊ |
1196 | ␉client->zipDeleteFile(zipHandler, "META-INF/rights.xml");␊ |
1197 | ␉if (removeEncryptionXML)␊ |
1198 | ␉ client->zipDeleteFile(zipHandler, "META-INF/encryption.xml");␊ |
1199 | ␉else␊ |
1200 | ␉{␊ |
1201 | ␉ StringXMLWriter xmlWriter;␊ |
1202 | ␉ encryptionDoc.save(xmlWriter, " ");␊ |
1203 | ␉ std::string xmlStr = xmlWriter.getResult();␊ |
1204 | ␉ ByteArray ba(xmlStr);␊ |
1205 | ␉ client->zipWriteFile(zipHandler, "META-INF/encryption.xml", ba);␊ |
1206 | ␉}␊ |
1207 | ␉␊ |
1208 | ␉client->zipClose(zipHandler);␊ |
1209 | }␊ |
1210 | ␊ |
1211 | void DRMProcessor::generatePDFObjectKey(int version,␊ |
1212 | ␉␉␉␉␉ const unsigned char* masterKey, unsigned int masterKeyLength,␊ |
1213 | ␉␉␉␉␉ int objectId, int objectGenerationNumber,␊ |
1214 | ␉␉␉␉␉ unsigned char* keyOut)␊ |
1215 | {␊ |
1216 | ␉switch(version)␊ |
1217 | ␉{␊ |
1218 | ␉case 4:␊ |
1219 | ␉ ByteArray toHash(masterKey, masterKeyLength);␊ |
1220 | ␉ uint32_t _objectId = objectId;␊ |
1221 | ␉ uint32_t _objectGenerationNumber = objectGenerationNumber;␊ |
1222 | ␉ toHash.append((const unsigned char*)&_objectId, 3); // Fill 3 bytes␊ |
1223 | ␉ toHash.append((const unsigned char*)&_objectGenerationNumber, 2); // Fill 2 bytes␊ |
1224 | ␊ |
1225 | ␉ client->digest("md5", toHash.data(), toHash.length(), keyOut);␊ |
1226 | ␉ break;␊ |
1227 | ␉}␊ |
1228 | }␊ |
1229 | ␊ |
1230 | void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut,␊ |
1231 | ␉␉␉␉ const unsigned char* encryptionKey, unsigned encryptionKeySize)␊ |
1232 | {␊ |
1233 | ␉uPDFParser::Parser parser;␊ |
1234 | ␉bool EBXHandlerFound = false;␊ |
1235 | ␉␉␊ |
1236 | ␉if (filenameIn == filenameOut)␊ |
1237 | ␉{␊ |
1238 | ␉ EXCEPTION(DRM_IN_OUT_EQUALS, "PDF IN must be different of PDF OUT");␊ |
1239 | ␉}␊ |
1240 | ␉␊ |
1241 | ␉try␊ |
1242 | ␉{␊ |
1243 | ␉ GOUROU_LOG(DEBUG, "Parse PDF");␊ |
1244 | ␉ parser.parse(filenameIn);␊ |
1245 | ␉}␊ |
1246 | ␉catch(std::invalid_argument& e)␊ |
1247 | ␉{␊ |
1248 | ␉ GOUROU_LOG(ERROR, "Invalid PDF");␊ |
1249 | ␉ return;␊ |
1250 | ␉}␊ |
1251 | ␊ |
1252 | ␉uPDFParser::Integer* ebxVersion;␊ |
1253 | ␉std::vector<uPDFParser::Object*> objects = parser.objects();␊ |
1254 | ␉std::vector<uPDFParser::Object*>::iterator it;␊ |
1255 | ␉std::vector<uPDFParser::Object*>::reverse_iterator rIt;␊ |
1256 | ␉unsigned char decryptedKey[16];␊ |
1257 | ␉int ebxId;␊ |
1258 | ␉␊ |
1259 | ␉for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++)␊ |
1260 | ␉{␊ |
1261 | ␉ // Update EBX_HANDLER with rights␊ |
1262 | ␉ if ((*rIt)->hasKey("Filter") && (**rIt)["Filter"]->str() == "/EBX_HANDLER")␊ |
1263 | ␉ {␊ |
1264 | ␉␉EBXHandlerFound = true;␊ |
1265 | ␉␉uPDFParser::Object* ebx = *rIt;␊ |
1266 | ␊ |
1267 | ␉␉ebxVersion = (uPDFParser::Integer*)(*ebx)["V"];␊ |
1268 | ␉␉if (ebxVersion->value() != 4)␊ |
1269 | ␉␉{␊ |
1270 | ␉␉ EXCEPTION(DRM_VERSION_NOT_SUPPORTED, "EBX encryption version not supported " << ebxVersion->value());␉␉ ␊ |
1271 | ␉␉}␊ |
1272 | ␊ |
1273 | ␉␉if (!(ebx->hasKey("ADEPT_LICENSE")))␊ |
1274 | ␉␉{␊ |
1275 | ␉␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "No ADEPT_LICENSE found");␊ |
1276 | ␉␉}␊ |
1277 | ␊ |
1278 | ␉␉uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"];␊ |
1279 | ␉␉␊ |
1280 | ␉␉std::string value = licenseObject->value();␊ |
1281 | ␉␉// Pad with '='␊ |
1282 | ␉␉while ((value.size() % 4))␊ |
1283 | ␉␉ value += "=";␊ |
1284 | ␉␉ByteArray zippedData = ByteArray::fromBase64(value);␊ |
1285 | ␊ |
1286 | ␉␉if (zippedData.size() == 0)␊ |
1287 | ␉␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Invalid ADEPT_LICENSE");␊ |
1288 | ␉␉ ␊ |
1289 | ␉␉ByteArray rightsStr;␊ |
1290 | ␉␉client->inflate(zippedData, rightsStr);␊ |
1291 | ␊ |
1292 | ␉␉pugi::xml_document rightsDoc;␊ |
1293 | ␉␉rightsDoc.load_string((const char*)rightsStr.data());␊ |
1294 | ␊ |
1295 | ␉␉decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize);␊ |
1296 | ␉␉␊ |
1297 | ␉␉ebxId = ebx->objectId();␊ |
1298 | ␊ |
1299 | ␉␉break;␊ |
1300 | ␉ }␊ |
1301 | ␉}␊ |
1302 | ␊ |
1303 | ␉if (!EBXHandlerFound)␊ |
1304 | ␉{␊ |
1305 | ␉ EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "EBX_HANDLER not found");␊ |
1306 | ␉}␊ |
1307 | ␊ |
1308 | ␉for(it = objects.begin(); it != objects.end(); it++)␊ |
1309 | ␉{␊ |
1310 | ␉ uPDFParser::Object* object = *it;␊ |
1311 | ␉ ␊ |
1312 | ␉ if (object->objectId() == ebxId)␊ |
1313 | ␉ {␊ |
1314 | ␉␉// object->deleteKey("Filter");␊ |
1315 | ␉␉continue;␊ |
1316 | ␉ }␊ |
1317 | ␊ |
1318 | ␉ // Should not decrypt XRef stream␊ |
1319 | ␉ if (object->hasKey("Type") && (*object)["Type"]->str() == "/XRef")␊ |
1320 | ␉ {␊ |
1321 | ␉␉GOUROU_LOG(DEBUG, "XRef stream at " << object->offset());␊ |
1322 | ␉␉continue;␊ |
1323 | ␉ }␊ |
1324 | ␉ ␊ |
1325 | ␉ GOUROU_LOG(DEBUG, "Obj " << object->objectId());␊ |
1326 | ␊ |
1327 | ␉ unsigned char tmpKey[sizeof(decryptedKey)];␊ |
1328 | ␊ |
1329 | ␉ generatePDFObjectKey(ebxVersion->value(),␊ |
1330 | ␉␉␉␉ decryptedKey, sizeof(decryptedKey),␊ |
1331 | ␉␉␉␉ object->objectId(), object->generationNumber(),␊ |
1332 | ␉␉␉␉ tmpKey);␊ |
1333 | ␊ |
1334 | ␉ uPDFParser::Dictionary& dictionary = object->dictionary();␊ |
1335 | ␉ std::map<std::string, uPDFParser::DataType*>& dictValues = dictionary.value();␊ |
1336 | ␉ std::map<std::string, uPDFParser::DataType*>::iterator dictIt;␊ |
1337 | ␉ std::map<std::string, uPDFParser::DataType*> decodedStrings;␊ |
1338 | ␉ std::string string;␊ |
1339 | ␉ ␊ |
1340 | ␉ /* Parse dictionary */␊ |
1341 | ␉ for (dictIt = dictValues.begin(); dictIt != dictValues.end(); dictIt++)␊ |
1342 | ␉ {␊ |
1343 | ␉␉uPDFParser::DataType* dictData = dictIt->second;␊ |
1344 | ␉␉if (dictData->type() == uPDFParser::DataType::STRING)␊ |
1345 | ␉␉{␊ |
1346 | ␉␉ string = ((uPDFParser::String*) dictData)->unescapedValue();␊ |
1347 | ␉␉ ␊ |
1348 | ␉␉ unsigned char* encryptedData = (unsigned char*)string.c_str();␊ |
1349 | ␉␉ unsigned int dataLength = string.size();␊ |
1350 | ␉␉ unsigned char* clearData = new unsigned char[dataLength];␊ |
1351 | ␉␉ unsigned int dataOutLength;␊ |
1352 | ␊ |
1353 | ␉␉ GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength);␊ |
1354 | ␊ |
1355 | ␉␉ client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,␊ |
1356 | ␉␉␉␉ tmpKey, sizeof(tmpKey), /* Key */␊ |
1357 | ␉␉␉␉ NULL, 0, /* IV */␊ |
1358 | ␉␉␉␉ encryptedData, dataLength,␊ |
1359 | ␉␉␉␉ clearData, &dataOutLength);␊ |
1360 | ␊ |
1361 | ␉␉ decodedStrings[dictIt->first] = new uPDFParser::String(␊ |
1362 | ␉␉␉std::string((const char*)clearData, dataOutLength));␊ |
1363 | ␊ |
1364 | ␉␉ delete[] clearData;␊ |
1365 | ␉␉}␊ |
1366 | ␉ }␊ |
1367 | ␉␉␊ |
1368 | ␉ for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++)␊ |
1369 | ␉␉dictionary.replace(dictIt->first, dictIt->second);␊ |
1370 | ␉ ␊ |
1371 | ␉ std::vector<uPDFParser::DataType*>::iterator datasIt;␊ |
1372 | ␉ std::vector<uPDFParser::DataType*>& datas = object->data();␊ |
1373 | ␉ uPDFParser::Stream* stream;␊ |
1374 | ␉ ␊ |
1375 | ␉ for (datasIt = datas.begin(); datasIt != datas.end(); datasIt++)␊ |
1376 | ␉ {␊ |
1377 | ␉␉if ((*datasIt)->type() != uPDFParser::DataType::STREAM)␊ |
1378 | ␉␉ continue;␊ |
1379 | ␊ |
1380 | ␉␉stream = (uPDFParser::Stream*) (*datasIt);␊ |
1381 | ␉␉unsigned char* encryptedData = stream->data();␊ |
1382 | ␉␉unsigned int dataLength = stream->dataLength();␊ |
1383 | ␉␉unsigned char* clearData = new unsigned char[dataLength];␊ |
1384 | ␉␉unsigned int dataOutLength;␊ |
1385 | ␉␉␊ |
1386 | ␉␉GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength());␊ |
1387 | ␊ |
1388 | ␉␉client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,␊ |
1389 | ␉␉␉␉tmpKey, sizeof(tmpKey), /* Key */␊ |
1390 | ␉␉␉␉NULL, 0, /* IV */␊ |
1391 | ␉␉␉␉encryptedData, dataLength,␊ |
1392 | ␉␉␉␉clearData, &dataOutLength);␊ |
1393 | ␉␉␊ |
1394 | ␉␉stream->setData(clearData, dataOutLength, true);␊ |
1395 | ␉␉if (dataOutLength != dataLength)␊ |
1396 | ␉␉ GOUROU_LOG(DEBUG, "New size " << dataOutLength);␊ |
1397 | ␉ }␊ |
1398 | ␉}␊ |
1399 | ␊ |
1400 | ␉uPDFParser::Object& trailer = parser.getTrailer();␊ |
1401 | ␉trailer.deleteKey("Encrypt");␊ |
1402 | ␊ |
1403 | ␉parser.write(filenameOut);␊ |
1404 | }␊ |
1405 | ␊ |
1406 | void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut,␊ |
1407 | ␉␉␉␉ ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize)␊ |
1408 | {␊ |
1409 | ␉if (type == PDF)␊ |
1410 | ␉ removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);␊ |
1411 | ␉else␊ |
1412 | ␉ removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);␊ |
1413 | }␊ |
1414 | }␊ |