1 | /*␊ |
2 | Copyright (c) 2021, Grégory Soutadé␊ |
3 | ␊ |
4 | All rights reserved.␊ |
5 | Redistribution and use in source and binary forms, with or without␊ |
6 | modification, are permitted provided that the following conditions are met:␊ |
7 | ␊ |
8 | * Redistributions of source code must retain the above copyright␊ |
9 | notice, this list of conditions and the following disclaimer.␊ |
10 | * Redistributions in binary form must reproduce the above copyright␊ |
11 | notice, this list of conditions and the following disclaimer in the␊ |
12 | documentation and/or other materials provided with the distribution.␊ |
13 | * Neither the name of the copyright holder nor the␊ |
14 | names of its contributors may be used to endorse or promote products␊ |
15 | derived from this software without specific prior written permission.␊ |
16 | ␊ |
17 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY␊ |
18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED␊ |
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE␊ |
20 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY␊ |
21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES␊ |
22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;␊ |
23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND␊ |
24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT␊ |
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS␊ |
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.␊ |
27 | */␊ |
28 | #include <bytearray.h>␊ |
29 | ␊ |
30 | #include <algorithm> ␊ |
31 | #include <cctype>␊ |
32 | #include <locale>␊ |
33 | ␊ |
34 | #define OPENSSL_NO_DEPRECATED 1␊ |
35 | ␊ |
36 | #include <openssl/rand.h>␊ |
37 | #include <openssl/pkcs12.h>␊ |
38 | #include <openssl/evp.h>␊ |
39 | #include <openssl/err.h>␊ |
40 | #include <openssl/rsa.h>␊ |
41 | #include <openssl/bn.h>␊ |
42 | ␊ |
43 | #include <curl/curl.h>␊ |
44 | ␊ |
45 | #include <zlib.h>␊ |
46 | #include <zip.h>␊ |
47 | ␊ |
48 | #include <libgourou_common.h>␊ |
49 | #include "drmprocessorclientimpl.h"␊ |
50 | ␊ |
51 | DRMProcessorClientImpl::DRMProcessorClientImpl():␊ |
52 | legacy(0), deflt(0)␊ |
53 | {␊ |
54 | #if OPENSSL_VERSION_MAJOR >= 3␊ |
55 | legacy = OSSL_PROVIDER_load(NULL, "legacy");␊ |
56 | if (!legacy)␊ |
57 | ␉EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL legacy provider not available");␊ |
58 | ␊ |
59 | deflt = OSSL_PROVIDER_load(NULL, "default");␊ |
60 | if (!deflt)␊ |
61 | ␉EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available");␊ |
62 | #endif␊ |
63 | }␊ |
64 | ␊ |
65 | DRMProcessorClientImpl::~DRMProcessorClientImpl()␊ |
66 | {␊ |
67 | #if OPENSSL_VERSION_MAJOR >= 3␊ |
68 | if (legacy)␊ |
69 | ␉OSSL_PROVIDER_unload(legacy);␊ |
70 | ␊ |
71 | if (deflt)␊ |
72 | ␉OSSL_PROVIDER_unload(deflt);␊ |
73 | #endif␊ |
74 | }␊ |
75 | ␊ |
76 | /* Digest interface */␊ |
77 | void* DRMProcessorClientImpl::createDigest(const std::string& digestName)␊ |
78 | {␊ |
79 | EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();␊ |
80 | const EVP_MD* md = EVP_get_digestbyname(digestName.c_str());␊ |
81 | ␊ |
82 | if (EVP_DigestInit(md_ctx, md) != 1)␊ |
83 | {␊ |
84 | ␉EVP_MD_CTX_free(md_ctx);␊ |
85 | ␉EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
86 | }␊ |
87 | ␊ |
88 | return md_ctx;␊ |
89 | }␊ |
90 | ␊ |
91 | void DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length)␊ |
92 | {␊ |
93 | if (EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length) != 1)␊ |
94 | ␉EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
95 | }␊ |
96 | ␊ |
97 | void DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut)␊ |
98 | {␊ |
99 | int res = EVP_DigestFinal((EVP_MD_CTX *)handler, digestOut, NULL);␊ |
100 | EVP_MD_CTX_free((EVP_MD_CTX *)handler);␊ |
101 | ␊ |
102 | if (res <= 0)␊ |
103 | ␉EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
104 | }␊ |
105 | ␊ |
106 | void DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut)␊ |
107 | {␊ |
108 | void* handler = createDigest(digestName);␊ |
109 | digestUpdate(handler, data, length);␊ |
110 | digestFinalize(handler, digestOut);␊ |
111 | }␊ |
112 | ␊ |
113 | /* Random interface */␊ |
114 | void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int length)␊ |
115 | {␊ |
116 | RAND_bytes(bytesOut, length);␊ |
117 | }␊ |
118 | ␊ |
119 | /* HTTP interface */␊ |
120 | #define HTTP_REQ_MAX_RETRY 5␊ |
121 | #define DISPLAY_THRESHOLD 10*1024 // Threshold to display download progression␊ |
122 | static unsigned downloadedBytes;␊ |
123 | ␊ |
124 | static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow,␊ |
125 | ␉␉␉ curl_off_t ultotal, curl_off_t ulnow)␊ |
126 | {␊ |
127 | // For "big" files only␊ |
128 | if (dltotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::LG_LOG_WARN)␊ |
129 | {␊ |
130 | ␉int percent = 0;␊ |
131 | ␉if (dltotal)␊ |
132 | ␉ percent = (dlnow * 100) / dltotal;␊ |
133 | ␊ |
134 | ␉std::cout << "\rDownload " << percent << "%" << std::flush;␊ |
135 | }␊ |
136 | ␊ |
137 | return 0;␊ |
138 | }␊ |
139 | ␊ |
140 | static size_t curlRead(void *data, size_t size, size_t nmemb, void *userp)␊ |
141 | {␊ |
142 | gourou::ByteArray* replyData = (gourou::ByteArray*) userp;␊ |
143 | ␊ |
144 | replyData->append((unsigned char*)data, size*nmemb);␊ |
145 | ␊ |
146 | return size*nmemb;␊ |
147 | }␊ |
148 | ␊ |
149 | static size_t curlReadFd(void *data, size_t size, size_t nmemb, void *userp)␊ |
150 | {␊ |
151 | int fd = *(int*) userp;␊ |
152 | ␊ |
153 | size_t res = write(fd, data, size*nmemb);␊ |
154 | ␊ |
155 | downloadedBytes += res;␊ |
156 | ␊ |
157 | return res;␊ |
158 | }␊ |
159 | ␊ |
160 | static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userdata)␊ |
161 | {␊ |
162 | std::map<std::string, std::string>* responseHeaders = (std::map<std::string, std::string>*)userdata;␊ |
163 | std::string::size_type pos = 0;␊ |
164 | std::string buf(buffer, size*nitems);␊ |
165 | ␊ |
166 | pos = buf.find(":", pos);␊ |
167 | ␊ |
168 | if (pos != std::string::npos)␊ |
169 | {␊ |
170 | ␉std::string key = std::string(buffer, pos);␊ |
171 | ␉std::string value = std::string(&buffer[pos+1], (size*nitems)-(pos+1));␊ |
172 | ␊ |
173 | ␉key = gourou::trim(key);␊ |
174 | ␉value = gourou::trim(value);␊ |
175 | ␊ |
176 | ␉(*responseHeaders)[key] = value;␊ |
177 | ␊ |
178 | ␉if (gourou::logLevel >= gourou::LG_LOG_DEBUG)␊ |
179 | ␉ std::cout << key << " : " << value << std::endl;␊ |
180 | }␊ |
181 | ␊ |
182 | return size*nitems;␊ |
183 | }␊ |
184 | ␊ |
185 | std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders, int fd, bool resume)␊ |
186 | {␊ |
187 | gourou::ByteArray replyData;␊ |
188 | std::map<std::string, std::string> localHeaders;␊ |
189 | ␊ |
190 | if (!responseHeaders)␊ |
191 | ␉responseHeaders = &localHeaders;␊ |
192 | ␊ |
193 | GOUROU_LOG(INFO, "Send request to " << URL);␊ |
194 | if (POSTData.size())␊ |
195 | {␊ |
196 | ␉GOUROU_LOG(DEBUG, "<<< " << std::endl << POSTData);␊ |
197 | }␊ |
198 | ␊ |
199 | unsigned prevDownloadedBytes;␊ |
200 | downloadedBytes = 0;␊ |
201 | if (fd && resume)␊ |
202 | {␊ |
203 | ␉struct stat _stat;␊ |
204 | ␉if (!fstat(fd, &_stat))␊ |
205 | ␉{␊ |
206 | ␉ GOUROU_LOG(WARN, "Resume download @ " << _stat.st_size << " bytes");␊ |
207 | ␉ downloadedBytes = _stat.st_size;␊ |
208 | ␉}␊ |
209 | ␉else␊ |
210 | ␉ GOUROU_LOG(WARN, "Want to resume, but fstat failed");␊ |
211 | }␊ |
212 | ␊ |
213 | CURL *curl = curl_easy_init();␊ |
214 | CURLcode res;␊ |
215 | curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());␊ |
216 | curl_easy_setopt(curl, CURLOPT_USERAGENT, "book2png");␊ |
217 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);␊ |
218 | ␊ |
219 | ␊ |
220 | struct curl_slist *list = NULL;␊ |
221 | list = curl_slist_append(list, "Accept: */*");␊ |
222 | std::string _contentType;␊ |
223 | if (contentType.size())␊ |
224 | {␊ |
225 | ␉_contentType = "Content-Type: " + contentType;␊ |
226 | ␉list = curl_slist_append(list, _contentType.c_str());␊ |
227 | }␊ |
228 | ␊ |
229 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);␊ |
230 | ␊ |
231 | if (POSTData.size())␊ |
232 | {␊ |
233 | ␉curl_easy_setopt(curl, CURLOPT_POST, 1L);␊ |
234 | ␉curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, POSTData.size());␊ |
235 | ␉curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTData.data());␊ |
236 | }␊ |
237 | ␊ |
238 | if (fd)␊ |
239 | {␊ |
240 | ␉curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlReadFd);␊ |
241 | ␉curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&fd);␊ |
242 | }␊ |
243 | else␊ |
244 | {␊ |
245 | ␉curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlRead);␊ |
246 | ␉curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&replyData);␊ |
247 | }␊ |
248 | ␊ |
249 | curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlHeaders);␊ |
250 | curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)responseHeaders);␊ |
251 | ␊ |
252 | curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress);␊ |
253 | curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);␊ |
254 | ␊ |
255 | for (int i=0; i<HTTP_REQ_MAX_RETRY; i++)␊ |
256 | {␊ |
257 | ␉prevDownloadedBytes = downloadedBytes;␊ |
258 | ␉if (downloadedBytes)␊ |
259 | ␉ curl_easy_setopt(curl, CURLOPT_RESUME_FROM, downloadedBytes);␊ |
260 | ␉ ␊ |
261 | ␉res = curl_easy_perform(curl);␊ |
262 | ␊ |
263 | ␉// Connexion failed, wait & retry␊ |
264 | ␉if (res == CURLE_COULDNT_CONNECT)␊ |
265 | ␉{␊ |
266 | ␉ GOUROU_LOG(WARN, "\nConnection failed, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);␉ ␊ |
267 | ␉}␊ |
268 | ␉// Transfer failed but some data has been received␊ |
269 | ␉// --> try again without incrementing tries␊ |
270 | ␉else if (res == CURLE_RECV_ERROR)␊ |
271 | ␉{␊ |
272 | ␉ if (prevDownloadedBytes != downloadedBytes)␊ |
273 | ␉ {␊ |
274 | ␉␉GOUROU_LOG(WARN, "\nConnection broken, but data received, try again");␉ ␊ |
275 | ␉␉i--;␊ |
276 | ␉ }␊ |
277 | ␉ else␊ |
278 | ␉␉GOUROU_LOG(WARN, "\nConnection broken and no data received, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);␊ |
279 | ␉}␊ |
280 | ␉// Other error --> fail␊ |
281 | ␉else␊ |
282 | ␉ break;␊ |
283 | ␊ |
284 | ␉// Wait a little bit (250ms * i)␊ |
285 | ␉usleep((250 * 1000) * (i+1));␊ |
286 | }␊ |
287 | ␊ |
288 | curl_slist_free_all(list);␊ |
289 | curl_easy_cleanup(curl);␊ |
290 | ␊ |
291 | if (res != CURLE_OK)␊ |
292 | ␉EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));␊ |
293 | ␊ |
294 | if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&␊ |
295 | ␉gourou::logLevel >= gourou::LG_LOG_WARN)␊ |
296 | ␉std::cout << std::endl;␊ |
297 | ␊ |
298 | if ((*responseHeaders)["Content-Type"] == "application/vnd.adobe.adept+xml")␊ |
299 | {␊ |
300 | ␉GOUROU_LOG(DEBUG, ">>> " << std::endl << replyData.data());␊ |
301 | }␊ |
302 | ␉␊ |
303 | return std::string((char*)replyData.data(), replyData.length());␊ |
304 | }␊ |
305 | ␊ |
306 | void DRMProcessorClientImpl::padWithPKCS1(unsigned char* out, unsigned int outLength,␊ |
307 | ␉␉␉␉␉ const unsigned char* in, unsigned int inLength)␊ |
308 | {␊ |
309 | if (outLength < (inLength + 3))␊ |
310 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding");␊ |
311 | ␊ |
312 | /*␊ |
313 | PKCS1v5 Padding is :␊ |
314 | 0x00 0x01 0xff * n 0x00 dataIn␊ |
315 | */␊ |
316 | ␊ |
317 | memset(out, 0xFF, outLength);␊ |
318 | ␊ |
319 | out[0] = 0x0;␊ |
320 | out[1] = 0x1;␊ |
321 | out[outLength - inLength - 1] = 0x00;␊ |
322 | memcpy(&out[outLength - inLength], in, inLength);␊ |
323 | }␊ |
324 | ␊ |
325 | ␊ |
326 | void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,␊ |
327 | ␉␉␉␉␉ const RSA_KEY_TYPE keyType, const std::string& password,␊ |
328 | ␉␉␉␉␉ const unsigned char* data, unsigned dataLength,␊ |
329 | ␉␉␉␉␉ unsigned char* res)␊ |
330 | {␊ |
331 | PKCS12 * pkcs12;␊ |
332 | EVP_PKEY_CTX *ctx;␊ |
333 | EVP_PKEY* pkey = NULL;␊ |
334 | size_t outlen;␊ |
335 | unsigned char* tmp;␊ |
336 | int ret;␊ |
337 | ␊ |
338 | pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);␊ |
339 | if (!pkcs12)␊ |
340 | ␉EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));␊ |
341 | ␊ |
342 | if (PKCS12_parse(pkcs12, password.c_str(), &pkey, NULL, NULL) <= 0)␊ |
343 | ␉EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));␊ |
344 | ␊ |
345 | outlen = EVP_PKEY_get_size(pkey);␊ |
346 | ␊ |
347 | ctx = EVP_PKEY_CTX_new(pkey, NULL);␊ |
348 | ␊ |
349 | /* Use RSA private key */␊ |
350 | if (EVP_PKEY_decrypt_init(ctx) <= 0)␊ |
351 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
352 | ␊ |
353 | if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)␊ |
354 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
355 | ␊ |
356 | tmp = (unsigned char*)malloc(outlen);␊ |
357 | ␊ |
358 | /* PKCS1 functions are no more exported */␊ |
359 | padWithPKCS1(tmp, outlen, data, dataLength);␊ |
360 | ␊ |
361 | ret = EVP_PKEY_decrypt(ctx, res, &outlen, tmp, outlen);␊ |
362 | ␊ |
363 | EVP_PKEY_CTX_free(ctx);␊ |
364 | free(tmp);␊ |
365 | ␊ |
366 | if (ret <= 0)␊ |
367 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
368 | }␊ |
369 | ␊ |
370 | void DRMProcessorClientImpl::RSAPrivateDecrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,␊ |
371 | ␉␉␉␉␉ const RSA_KEY_TYPE keyType, const std::string& password,␊ |
372 | ␉␉␉␉␉ const unsigned char* data, unsigned dataLength,␊ |
373 | ␉␉␉␉␉ unsigned char* res)␊ |
374 | {␊ |
375 | BIO* mem = BIO_new_mem_buf(RSAKey, RSAKeyLength);␊ |
376 | PKCS8_PRIV_KEY_INFO* p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(mem, NULL);␊ |
377 | ␊ |
378 | if (!p8inf)␊ |
379 | ␉EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));␊ |
380 | ␊ |
381 | EVP_PKEY_CTX *ctx;␊ |
382 | EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);␊ |
383 | size_t outlen = dataLength;␊ |
384 | int ret;␊ |
385 | ␊ |
386 | if (!pkey)␊ |
387 | ␉EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));␊ |
388 | ␊ |
389 | ctx = EVP_PKEY_CTX_new(pkey, NULL);␊ |
390 | ␊ |
391 | if (EVP_PKEY_decrypt_init(ctx) <= 0)␊ |
392 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
393 | ␊ |
394 | if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)␊ |
395 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
396 | ␊ |
397 | ret = EVP_PKEY_decrypt(ctx, res, &outlen, data, dataLength);␊ |
398 | ␊ |
399 | PKCS8_PRIV_KEY_INFO_free(p8inf);␊ |
400 | EVP_PKEY_CTX_free(ctx);␊ |
401 | BIO_free(mem);␊ |
402 | ␊ |
403 | if (ret <= 0)␊ |
404 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
405 | }␊ |
406 | ␊ |
407 | void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,␊ |
408 | ␉␉␉␉␉ const RSA_KEY_TYPE keyType,␊ |
409 | ␉␉␉␉␉ const unsigned char* data, unsigned dataLength,␊ |
410 | ␉␉␉␉␉ unsigned char* res)␊ |
411 | {␊ |
412 | size_t outlen;␊ |
413 | ␊ |
414 | X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength);␊ |
415 | if (!x509)␊ |
416 | ␉EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate");␊ |
417 | ␉␊ |
418 | EVP_PKEY_CTX *ctx;␊ |
419 | EVP_PKEY * evpKey = X509_get_pubkey(x509);␊ |
420 | ␊ |
421 | if (!evpKey)␊ |
422 | ␉EXCEPTION(gourou::CLIENT_NO_PUB_KEY, "No public key in certificate");␊ |
423 | ␊ |
424 | ctx = EVP_PKEY_CTX_new(evpKey, NULL);␊ |
425 | ␊ |
426 | if (EVP_PKEY_encrypt_init(ctx) <= 0)␊ |
427 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
428 | ␊ |
429 | if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)␊ |
430 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
431 | ␊ |
432 | int ret = EVP_PKEY_encrypt(ctx, res, &outlen, data, dataLength);␊ |
433 | ␊ |
434 | EVP_PKEY_CTX_free(ctx);␊ |
435 | ␊ |
436 | if (ret < 0)␊ |
437 | ␉EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
438 | ␊ |
439 | EVP_PKEY_free(evpKey);␊ |
440 | }␊ |
441 | ␊ |
442 | void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits)␊ |
443 | {␊ |
444 | BIGNUM * bn = BN_new();␊ |
445 | EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);␊ |
446 | EVP_PKEY *key = NULL;␊ |
447 | ␊ |
448 | BN_set_word(bn, 0x10001);␊ |
449 | ␊ |
450 | EVP_PKEY_keygen_init(ctx);␊ |
451 | ␊ |
452 | EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keyLengthBits);␊ |
453 | EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);␊ |
454 | EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, bn);␊ |
455 | EVP_PKEY_keygen(ctx, &key);␊ |
456 | ␊ |
457 | EVP_PKEY_CTX_free(ctx);␊ |
458 | BN_free(bn);␊ |
459 | ␊ |
460 | return key;␊ |
461 | }␊ |
462 | ␊ |
463 | void DRMProcessorClientImpl::destroyRSAHandler(void* handler)␊ |
464 | {␊ |
465 | free(handler);␊ |
466 | }␊ |
467 | ␊ |
468 | void DRMProcessorClientImpl::extractRSAPublicKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)␊ |
469 | {␊ |
470 | X509_PUBKEY *x509_pubkey = 0;␊ |
471 | X509_PUBKEY_set(&x509_pubkey, (EVP_PKEY*)handler);␊ |
472 | ␊ |
473 | *keyOutLength = i2d_X509_PUBKEY(x509_pubkey, keyOut);␊ |
474 | ␊ |
475 | X509_PUBKEY_free(x509_pubkey);␊ |
476 | }␊ |
477 | ␊ |
478 | void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)␊ |
479 | {␊ |
480 | PKCS8_PRIV_KEY_INFO * privKey = EVP_PKEY2PKCS8((EVP_PKEY*)handler);␊ |
481 | ␊ |
482 | *keyOutLength = i2d_PKCS8_PRIV_KEY_INFO(privKey, keyOut);␊ |
483 | ␊ |
484 | PKCS8_PRIV_KEY_INFO_free(privKey);␊ |
485 | }␊ |
486 | ␉␉␉␉ ␊ |
487 | void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,␊ |
488 | ␉␉␉␉␉␉const RSA_KEY_TYPE keyType, const std::string& password,␊ |
489 | ␉␉␉␉␉␉unsigned char** certOut, unsigned int* certOutLength)␊ |
490 | {␊ |
491 | PKCS12 * pkcs12;␊ |
492 | EVP_PKEY* pkey = 0;␊ |
493 | X509* cert = 0;␊ |
494 | ␊ |
495 | pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);␊ |
496 | if (!pkcs12)␊ |
497 | ␉EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));␊ |
498 | PKCS12_parse(pkcs12, password.c_str(), &pkey, &cert, NULL);␊ |
499 | ␊ |
500 | if (!cert)␊ |
501 | ␉EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));␊ |
502 | ␊ |
503 | *certOutLength = i2d_X509(cert, certOut);␊ |
504 | ␊ |
505 | EVP_PKEY_free(pkey);␊ |
506 | }␊ |
507 | ␊ |
508 | /* Crypto interface */␊ |
509 | void DRMProcessorClientImpl::encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,␊ |
510 | ␉␉␉␉ const unsigned char* key, unsigned int keyLength,␊ |
511 | ␉␉␉␉ const unsigned char* iv, unsigned int ivLength,␊ |
512 | ␉␉␉␉ const unsigned char* dataIn, unsigned int dataInLength,␊ |
513 | ␉␉␉␉ unsigned char* dataOut, unsigned int* dataOutLength)␊ |
514 | {␊ |
515 | void* handler = encryptInit(algo, chaining, key, keyLength, iv, ivLength);␊ |
516 | encryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);␊ |
517 | encryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);␊ |
518 | }␊ |
519 | ␊ |
520 | void* DRMProcessorClientImpl::encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,␊ |
521 | ␉␉␉␉␉ const unsigned char* key, unsigned int keyLength,␊ |
522 | ␉␉␉␉␉ const unsigned char* iv, unsigned int ivLength)␊ |
523 | {␊ |
524 | EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();␊ |
525 | int ret = 0;␊ |
526 | ␊ |
527 | switch (algo)␊ |
528 | {␊ |
529 | case ALGO_AES:␊ |
530 | {␊ |
531 | ␉switch(keyLength)␊ |
532 | ␉{␊ |
533 | ␉case 16:␊ |
534 | ␉ switch(chaining)␊ |
535 | ␉ {␊ |
536 | ␉ case CHAIN_ECB:␊ |
537 | ␉␉ret = EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, iv);␊ |
538 | ␉␉break;␊ |
539 | ␉ case CHAIN_CBC:␊ |
540 | ␉␉ret = EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv);␊ |
541 | ␉␉break;␊ |
542 | ␉ default:␊ |
543 | ␉␉EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);␊ |
544 | ␉ }␊ |
545 | ␉ break;␊ |
546 | ␉default:␊ |
547 | ␉ EVP_CIPHER_CTX_free(ctx);␊ |
548 | ␉ EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);␊ |
549 | ␉}␊ |
550 | ␉break;␊ |
551 | }␊ |
552 | case ALGO_RC4:␊ |
553 | {␊ |
554 | ␉if (keyLength != 16)␊ |
555 | ␉{␊ |
556 | ␉ EVP_CIPHER_CTX_free(ctx);␊ |
557 | ␉ EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);␊ |
558 | ␉}␊ |
559 | ␉ret = EVP_DecryptInit(ctx, EVP_rc4(), key, iv);␊ |
560 | ␉break;␊ |
561 | }␊ |
562 | }␊ |
563 | ␊ |
564 | if (ret <= 0)␊ |
565 | {␊ |
566 | ␉EVP_CIPHER_CTX_free(ctx);␊ |
567 | ␉EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
568 | }␊ |
569 | ␊ |
570 | return ctx;␊ |
571 | }␊ |
572 | ␊ |
573 | void* DRMProcessorClientImpl::decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,␊ |
574 | ␉␉␉␉␉ const unsigned char* key, unsigned int keyLength,␊ |
575 | ␉␉␉␉␉ const unsigned char* iv, unsigned int ivLength)␊ |
576 | {␊ |
577 | EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();␊ |
578 | int ret = 0;␊ |
579 | ␊ |
580 | switch(algo)␊ |
581 | {␊ |
582 | case ALGO_AES:␊ |
583 | {␊ |
584 | ␉switch(keyLength)␊ |
585 | ␉{␊ |
586 | ␉case 16:␊ |
587 | ␉ switch(chaining)␊ |
588 | ␉ {␊ |
589 | ␉ case CHAIN_ECB:␊ |
590 | ␉␉ret = EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, iv);␊ |
591 | ␉␉break;␊ |
592 | ␉ case CHAIN_CBC:␊ |
593 | ␉␉ret = EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);␊ |
594 | ␉␉break;␊ |
595 | ␉ default:␊ |
596 | ␉␉EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);␊ |
597 | ␉ }␊ |
598 | ␉ break;␊ |
599 | ␉default:␊ |
600 | ␉ EVP_CIPHER_CTX_free(ctx);␊ |
601 | ␉ EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);␊ |
602 | ␉}␊ |
603 | ␉break;␊ |
604 | }␊ |
605 | case ALGO_RC4:␊ |
606 | {␊ |
607 | ␉if (keyLength != 16)␊ |
608 | ␉{␊ |
609 | ␉ EVP_CIPHER_CTX_free(ctx);␊ |
610 | ␉ EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);␊ |
611 | ␉}␊ |
612 | ␉ret = EVP_DecryptInit(ctx, EVP_rc4(), key, iv);␊ |
613 | ␉break;␊ |
614 | }␊ |
615 | }␊ |
616 | ␊ |
617 | if (ret <= 0)␊ |
618 | {␊ |
619 | ␉EVP_CIPHER_CTX_free(ctx);␊ |
620 | ␉EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
621 | }␊ |
622 | ␊ |
623 | return ctx;␊ |
624 | }␊ |
625 | ␊ |
626 | void DRMProcessorClientImpl::encryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,␊ |
627 | ␉␉␉␉␉ unsigned char* dataOut, unsigned int* dataOutLength)␊ |
628 | {␊ |
629 | int ret = EVP_EncryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);␊ |
630 | ␊ |
631 | if (ret <= 0)␊ |
632 | EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
633 | }␊ |
634 | ␊ |
635 | void DRMProcessorClientImpl::encryptFinalize(void* handler,␊ |
636 | ␉␉␉␉␉ unsigned char* dataOut, unsigned int* dataOutLength)␊ |
637 | {␊ |
638 | int len, ret;␊ |
639 | ␊ |
640 | ret = EVP_EncryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);␊ |
641 | *dataOutLength += len;␊ |
642 | EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);␊ |
643 | ␊ |
644 | if (ret <= 0)␊ |
645 | EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
646 | }␊ |
647 | ␊ |
648 | void DRMProcessorClientImpl::decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,␊ |
649 | ␉␉␉␉ const unsigned char* key, unsigned int keyLength,␊ |
650 | ␉␉␉␉ const unsigned char* iv, unsigned int ivLength,␊ |
651 | ␉␉␉␉ const unsigned char* dataIn, unsigned int dataInLength,␊ |
652 | ␉␉␉␉ unsigned char* dataOut, unsigned int* dataOutLength)␊ |
653 | {␊ |
654 | void* handler = decryptInit(algo, chaining, key, keyLength, iv, ivLength);␊ |
655 | decryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);␊ |
656 | decryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);␊ |
657 | }␊ |
658 | ␊ |
659 | void DRMProcessorClientImpl::decryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,␊ |
660 | ␉␉␉␉␉ unsigned char* dataOut, unsigned int* dataOutLength)␊ |
661 | {␊ |
662 | int ret = EVP_DecryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);␊ |
663 | ␊ |
664 | if (ret <= 0)␊ |
665 | EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
666 | }␊ |
667 | ␊ |
668 | void DRMProcessorClientImpl::decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength)␊ |
669 | {␊ |
670 | int len, ret;␊ |
671 | ␊ |
672 | ret = EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);␊ |
673 | *dataOutLength += len;␊ |
674 | EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);␊ |
675 | ␊ |
676 | if (ret <= 0)␊ |
677 | EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));␊ |
678 | }␊ |
679 | ␊ |
680 | void* DRMProcessorClientImpl::zipOpen(const std::string& path)␊ |
681 | {␊ |
682 | zip_t* handler = zip_open(path.c_str(), 0, 0);␊ |
683 | ␊ |
684 | if (!handler)␊ |
685 | ␉EXCEPTION(gourou::CLIENT_BAD_ZIP_FILE, "Invalid zip file " << path);␊ |
686 | ␊ |
687 | return handler;␊ |
688 | }␊ |
689 | ␊ |
690 | void DRMProcessorClientImpl::zipReadFile(void* handler, const std::string& path, gourou::ByteArray& result, bool decompress)␊ |
691 | {␊ |
692 | std::string res;␊ |
693 | zip_stat_t sb;␊ |
694 | ␊ |
695 | if (zip_stat((zip_t *)handler, path.c_str(), 0, &sb) < 0)␊ |
696 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error, no file " << path << ", " << zip_strerror((zip_t *)handler));␊ |
697 | ␊ |
698 | if (!(sb.valid & (ZIP_STAT_INDEX|ZIP_STAT_SIZE)))␊ |
699 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Required fields missing");␊ |
700 | ␊ |
701 | result.resize(sb.size);␊ |
702 | ␊ |
703 | zip_file_t *f = zip_fopen_index((zip_t *)handler, sb.index, (decompress)?0:ZIP_FL_COMPRESSED);␊ |
704 | zip_fread(f, result.data(), sb.size);␊ |
705 | zip_fclose(f);␊ |
706 | }␊ |
707 | ␊ |
708 | void DRMProcessorClientImpl::zipWriteFile(void* handler, const std::string& path, gourou::ByteArray& content)␊ |
709 | {␊ |
710 | zip_int64_t ret;␊ |
711 | ␊ |
712 | zip_source_t* s = zip_source_buffer((zip_t*)handler, content.takeShadowData(), content.length(), 1);␊ |
713 | ␊ |
714 | zip_int64_t idx = zip_name_locate((zip_t*)handler, path.c_str(), 0);␊ |
715 | ␊ |
716 | // File doesn't exists␊ |
717 | if (idx == -1)␊ |
718 | ␉ret = zip_file_add((zip_t*)handler, path.c_str(), s, 0);␊ |
719 | else␊ |
720 | ␉ret = zip_file_replace((zip_t*)handler, idx, s, ZIP_FL_OVERWRITE);␊ |
721 | ␊ |
722 | if (ret < 0)␊ |
723 | {␊ |
724 | ␉zip_source_free(s);␊ |
725 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));␊ |
726 | }␊ |
727 | }␊ |
728 | ␊ |
729 | void DRMProcessorClientImpl::zipDeleteFile(void* handler, const std::string& path)␊ |
730 | {␊ |
731 | zip_int64_t idx = zip_name_locate((zip_t*)handler, path.c_str(), 0);␊ |
732 | ␊ |
733 | if (idx < 0)␊ |
734 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "No such file " << path.c_str());␊ |
735 | ␊ |
736 | if (zip_delete((zip_t*)handler, idx))␊ |
737 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));␊ |
738 | }␊ |
739 | ␊ |
740 | void DRMProcessorClientImpl::zipClose(void* handler)␊ |
741 | {␊ |
742 | zip_close((zip_t*)handler);␊ |
743 | }␊ |
744 | ␊ |
745 | void DRMProcessorClientImpl::inflate(gourou::ByteArray& data, gourou::ByteArray& result,␊ |
746 | ␉␉␉␉ int wbits)␊ |
747 | {␊ |
748 | unsigned int dataSize = data.size()*2;␊ |
749 | unsigned char* buffer = new unsigned char[dataSize];␊ |
750 | ␊ |
751 | z_stream infstream;␊ |
752 | ␊ |
753 | infstream.zalloc = Z_NULL;␊ |
754 | infstream.zfree = Z_NULL;␊ |
755 | infstream.opaque = Z_NULL;␊ |
756 | ␊ |
757 | infstream.avail_in = (uInt)data.size();␊ |
758 | infstream.next_in = (Bytef *)data.data(); // input char array␊ |
759 | infstream.avail_out = (uInt)dataSize; // size of output␊ |
760 | infstream.next_out = (Bytef *)buffer; // output char array␊ |
761 | ␊ |
762 | int ret = inflateInit2(&infstream, wbits);␊ |
763 | ␊ |
764 | if (ret != Z_OK)␊ |
765 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Inflate error, code " << zError(ret) << ", msg " << infstream.msg);␊ |
766 | ␊ |
767 | ret = ::inflate(&infstream, Z_FINISH);␊ |
768 | while (ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR)␊ |
769 | {␊ |
770 | ␉// Real error␊ |
771 | ␉if (ret == Z_BUF_ERROR && infstream.avail_out == (uInt)dataSize)␊ |
772 | ␉ break;␊ |
773 | ␊ |
774 | ␉result.append(buffer, dataSize-infstream.avail_out);␊ |
775 | ␉ ␊ |
776 | ␉if ((ret == Z_OK && infstream.avail_out != 0) || ret == Z_STREAM_END)␊ |
777 | ␉ break;␊ |
778 | ␉infstream.avail_out = (uInt)dataSize; // size of output␊ |
779 | ␉infstream.next_out = (Bytef *)buffer; // output char array␊ |
780 | ␉ret = ::inflate(&infstream, Z_FINISH);␊ |
781 | }␊ |
782 | ␊ |
783 | if (ret == Z_STREAM_END)␊ |
784 | ␉ret = inflateEnd(&infstream);␊ |
785 | ␊ |
786 | delete[] buffer;␊ |
787 | ␊ |
788 | if (ret != Z_OK && ret != Z_STREAM_END)␊ |
789 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Inflate error, code " << zError(ret) << ", msg " << infstream.msg);␊ |
790 | }␊ |
791 | ␉␊ |
792 | void DRMProcessorClientImpl::deflate(gourou::ByteArray& data, gourou::ByteArray& result,␊ |
793 | ␉␉␉␉ int wbits, int compressionLevel)␊ |
794 | {␊ |
795 | unsigned int dataSize = data.size();␊ |
796 | unsigned char* buffer = new unsigned char[dataSize];␊ |
797 | ␊ |
798 | z_stream defstream;␊ |
799 | ␊ |
800 | defstream.zalloc = Z_NULL;␊ |
801 | defstream.zfree = Z_NULL;␊ |
802 | defstream.opaque = Z_NULL;␊ |
803 | ␊ |
804 | defstream.avail_in = (uInt)dataSize;␊ |
805 | defstream.next_in = (Bytef *)data.data(); // input char array␊ |
806 | defstream.avail_out = (uInt)dataSize; // size of output␊ |
807 | defstream.next_out = (Bytef *)buffer; // output char array␊ |
808 | ␊ |
809 | int ret = deflateInit2(&defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits,␊ |
810 | ␉␉␉ compressionLevel, Z_DEFAULT_STRATEGY);␊ |
811 | ␊ |
812 | if (ret != Z_OK)␊ |
813 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Deflate error, code " << zError(ret) << ", msg " << defstream.msg);␊ |
814 | ␊ |
815 | ret = ::deflate(&defstream, Z_FINISH);␊ |
816 | while (ret == Z_OK || ret == Z_STREAM_END)␊ |
817 | {␊ |
818 | ␉result.append(buffer, dataSize-defstream.avail_out);␊ |
819 | ␉if ((ret == Z_OK && defstream.avail_out != 0) || ret == Z_STREAM_END)␊ |
820 | ␉ break;␊ |
821 | ␉defstream.avail_out = (uInt)dataSize; // size of output␊ |
822 | ␉defstream.next_out = (Bytef *)buffer; // output char array␊ |
823 | ␉ret = ::deflate(&defstream, Z_FINISH);␊ |
824 | }␊ |
825 | ␊ |
826 | if (ret == Z_STREAM_END)␊ |
827 | ␉ret = deflateEnd(&defstream);␊ |
828 | ␊ |
829 | delete[] buffer;␊ |
830 | ␊ |
831 | if (ret != Z_OK && ret != Z_STREAM_END)␊ |
832 | ␉EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Deflate error, code " << zError(ret) << ", msg " << defstream.msg);␊ |
833 | }␊ |