Compare commits

..

13 Commits

10 changed files with 108 additions and 52 deletions
+19 -5
View File
@@ -1,16 +1,16 @@
Introduction Introduction
------------ ------------
libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub/PDF files. It overcome the lacks of Adobe support for Linux platforms. libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub/PDF files. It overcomes the lack of Adobe support for Linux platforms.
Architecture Architecture
------------ ------------
Like RMSDK, libgourou has a client/server scheme. All platform specific functions (crypto, network...) has to be implemented in a client class (that derives from DRMProcessorClient) while server implements ADEPT protocol. Like RMSDK, libgourou has a client/server scheme. All platform specific functions (crypto, network...) have to be implemented in a client class (that derives from DRMProcessorClient) while server implements ADEPT protocol.
A reference implementation using cURL, OpenSSL and libzip is provided (in _utils_ directory). A reference implementation using cURL, OpenSSL and libzip is provided (in _utils_ directory).
Main fucntions to use from gourou::DRMProcessor are : Main functions to use from gourou::DRMProcessor are:
* Get an ePub from an ACSM file : _fulfill()_ and _download()_ * Get an ePub from an ACSM file : _fulfill()_ and _download()_
* Create a new device : _createDRMProcessor()_ * Create a new device : _createDRMProcessor()_
@@ -23,11 +23,11 @@ You can import configuration from (at least) :
* Kobo device : .adept/device.xml, .adept/devicesalt and .adept/activation.xml * Kobo device : .adept/device.xml, .adept/devicesalt and .adept/activation.xml
* Bookeen device : .adobe-digital-editions/device.xml, root/devkey.bin and .adobe-digital-editions/activation.xml * Bookeen device : .adobe-digital-editions/device.xml, root/devkey.bin and .adobe-digital-editions/activation.xml
Or create a new one. Be careful : there is a limited number of devices that can be created bye one account. Or create a new one. Be careful: there is a limited number of devices that can be created by one account.
ePub are encrypted using a shared key: one account / multiple devices, so you can create and register a device into your computer and read downloaded (and encrypted) ePub file with your eReader configured using the same AdobeID account. ePub are encrypted using a shared key: one account / multiple devices, so you can create and register a device into your computer and read downloaded (and encrypted) ePub file with your eReader configured using the same AdobeID account.
For those who wants to remove DRM without adept_remove, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin. For those who want to remove DRM without adept_remove, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin.
Dependencies Dependencies
@@ -150,3 +150,17 @@ Special thanks
* _Jens_ for all test samples and utils testing * _Jens_ for all test samples and utils testing
* _Milian_ for debug & code * _Milian_ for debug & code
* _Berwyn H_ for all test samples, feedbacks, patches and kind donation * _Berwyn H_ for all test samples, feedbacks, patches and kind donation
Donation
--------
https://www.paypal.com/donate/?hosted_button_id=JD3U6XMZCPHKN
Donators
--------
* _Berwyn H_
* _bwitt_
* _Ismail_
+1 -1
View File
@@ -37,7 +37,7 @@
#define ACS_SERVER "http://adeactivate.adobe.com/adept" #define ACS_SERVER "http://adeactivate.adobe.com/adept"
#endif #endif
#define LIBGOUROU_VERSION "0.8.3" #define LIBGOUROU_VERSION "0.8.6"
namespace gourou namespace gourou
{ {
+1
View File
@@ -120,6 +120,7 @@ namespace gourou
CLIENT_OSSL_ERROR, CLIENT_OSSL_ERROR,
CLIENT_CRYPT_ERROR, CLIENT_CRYPT_ERROR,
CLIENT_DIGEST_ERROR, CLIENT_DIGEST_ERROR,
CLIENT_HTTP_ERROR
}; };
enum DRM_REMOVAL_ERROR { enum DRM_REMOVAL_ERROR {
+9
View File
@@ -7,3 +7,12 @@ if [ ! -d lib/updfparser ] ; then
make BUILD_STATIC=1 BUILD_SHARED=0 make BUILD_STATIC=1 BUILD_SHARED=0
popd popd
fi fi
# OpenSSL legacy
if [ ! -d lib/openssl ] ; then
git clone --branch openssl-3.3 https://github.com/openssl/openssl.git lib/openssl
pushd lib/openssl
./Configure disable-apps disable-shared enable-legacy
make -j4
popd
fi
+1 -1
View File
@@ -3,7 +3,7 @@
if [ ! -d lib/updfparser ] ; then if [ ! -d lib/updfparser ] ; then
echo "Some libraries are missing" echo "Some libraries are missing"
echo "You must run this script at the top of libgourou working direcotry." echo "You must run this script at the top of libgourou working direcotry."
echo "./lib/setup.sh must be called first (make all)" echo "./scripts/setup.sh must be called first (make all)"
exit 1 exit 1
fi fi
+23 -25
View File
@@ -399,30 +399,6 @@ namespace gourou
} }
doOperatorAuth(operatorURL); doOperatorAuth(operatorURL);
// Add new operatorURL to list
pugi::xml_document activationDoc;
user->readActivation(activationDoc);
pugi::xml_node root;
pugi::xpath_node xpathRes = activationDoc.select_node("//adept:operatorURLList");
// Create adept:operatorURLList if it doesn't exists
if (!xpathRes)
{
xpathRes = activationDoc.select_node("/activationInfo");
root = xpathRes.node();
root = root.append_child("adept:operatorURLList");
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
appendTextElem(root, "adept:user", user->getUUID());
}
else
root = xpathRes.node();
appendTextElem(root, "adept:operatorURL", operatorURL);
user->updateActivationFile(activationDoc);
} }
void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq) void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)
@@ -492,6 +468,24 @@ namespace gourou
appendTextElem(root, "adept:licenseURL", licenseURL); appendTextElem(root, "adept:licenseURL", licenseURL);
appendTextElem(root, "adept:certificate", certificate); appendTextElem(root, "adept:certificate", certificate);
// Add new operatorURL to list
xpathRes = activationDoc.select_node("//adept:operatorURLList");
// Create adept:operatorURLList if it doesn't exists
if (!xpathRes)
{
xpathRes = activationDoc.select_node("/activationInfo");
root = xpathRes.node();
root = root.append_child("adept:operatorURLList");
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
appendTextElem(root, "adept:user", user->getUUID());
}
else
root = xpathRes.node();
appendTextElem(root, "adept:operatorURL", operatorURL);
user->updateActivationFile(activationDoc); user->updateActivationFile(activationDoc);
} }
@@ -1323,6 +1317,7 @@ namespace gourou
std::vector<uPDFParser::Object*> objects = parser.objects(); std::vector<uPDFParser::Object*> objects = parser.objects();
std::vector<uPDFParser::Object*>::iterator it; std::vector<uPDFParser::Object*>::iterator it;
std::vector<uPDFParser::Object*>::reverse_iterator rIt; std::vector<uPDFParser::Object*>::reverse_iterator rIt;
std::vector<uPDFParser::Object*> ebxObjects;
unsigned char decryptedKey[16]; unsigned char decryptedKey[16];
int ebxId; int ebxId;
@@ -1381,7 +1376,7 @@ namespace gourou
if (object->objectId() == ebxId) if (object->objectId() == ebxId)
{ {
// object->deleteKey("Filter"); ebxObjects.push_back(object);
continue; continue;
} }
@@ -1491,6 +1486,9 @@ namespace gourou
} }
} }
for(it = ebxObjects.begin(); it != ebxObjects.end(); it++)
parser.removeObject(*it);
uPDFParser::Object& trailer = parser.getTrailer(); uPDFParser::Object& trailer = parser.getTrailer();
trailer.deleteKey("Encrypt"); trailer.deleteKey("Encrypt");
+1
View File
@@ -10,6 +10,7 @@ CXXFLAGS=-Wall -fPIC -I$(ROOT)/include
STATIC_DEP= STATIC_DEP=
LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl -lpugixml LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl -lpugixml
LDFLAGS += -L$(ROOT) $(ROOT)/lib/openssl/libcrypto.a -lzip -lz -lcurl -lpugixml
ifneq ($(STATIC_UTILS),) ifneq ($(STATIC_UTILS),)
STATIC_DEP = $(ROOT)/libgourou.a STATIC_DEP = $(ROOT)/libgourou.a
+15 -3
View File
@@ -183,8 +183,13 @@ private:
loan->bookName = node.first_child().value(); loan->bookName = node.first_child().value();
struct tm tm; struct tm tm;
#ifdef __ANDROID__
res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%z", &tm);
#else
res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm); res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm);
if (*res == 0) #endif
if (res != NULL && *res == 0)
{ {
if (mktime(&tm) <= time(NULL)) if (mktime(&tm) <= time(NULL))
loan->validity = " (Expired)"; loan->validity = " (Expired)";
@@ -224,7 +229,12 @@ private:
maxSizeBookName = loan->bookName.size(); maxSizeBookName = loan->bookName.size();
} }
if (maxSizeBookName > MAX_SIZE_BOOK_NAME) /* Manage empty names */
if (maxSizeBookName == 0)
maxSizeBookName = sizeof("No name ")-1;
else if (maxSizeBookName < 4)
maxSizeBookName = 4;
else if (maxSizeBookName > MAX_SIZE_BOOK_NAME)
maxSizeBookName = MAX_SIZE_BOOK_NAME; maxSizeBookName = MAX_SIZE_BOOK_NAME;
else if ((maxSizeBookName % 2)) else if ((maxSizeBookName % 2))
maxSizeBookName++; maxSizeBookName++;
@@ -271,7 +281,9 @@ private:
std::cout << kv.first; std::cout << kv.first;
std::cout << " "; std::cout << " ";
if (loan->bookName.size() > MAX_SIZE_BOOK_NAME) if (loan->bookName.size() == 0)
bookName = std::string("No name ");
else if (loan->bookName.size() > MAX_SIZE_BOOK_NAME)
bookName = std::string(loan->bookName.c_str(), MAX_SIZE_BOOK_NAME); bookName = std::string(loan->bookName.c_str(), MAX_SIZE_BOOK_NAME);
else else
bookName = loan->bookName; bookName = loan->bookName;
+19
View File
@@ -30,6 +30,7 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <locale> #include <locale>
#include <stdlib.h>
#define OPENSSL_NO_DEPRECATED 1 #define OPENSSL_NO_DEPRECATED 1
@@ -60,6 +61,14 @@ DRMProcessorClientImpl::DRMProcessorClientImpl():
if (!deflt) if (!deflt)
EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available"); EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available");
#endif #endif
#ifdef WIN32
strcpy(cookiejar, "C:\\temp\\libgourou_cookie_jar_XXXXXX");
#else
strcpy(cookiejar, "/tmp/libgourou_cookie_jar_XXXXXX");
#endif
mkstemp(cookiejar);
} }
DRMProcessorClientImpl::~DRMProcessorClientImpl() DRMProcessorClientImpl::~DRMProcessorClientImpl()
@@ -71,6 +80,8 @@ DRMProcessorClientImpl::~DRMProcessorClientImpl()
if (deflt) if (deflt)
OSSL_PROVIDER_unload(deflt); OSSL_PROVIDER_unload(deflt);
#endif #endif
unlink(cookiejar);
} }
/* Digest interface */ /* Digest interface */
@@ -227,6 +238,7 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
} }
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookiejar);
if (POSTData.size()) if (POSTData.size())
{ {
@@ -286,11 +298,18 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
} }
curl_slist_free_all(list); curl_slist_free_all(list);
long http_code = 400;
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
if (res != CURLE_OK) if (res != CURLE_OK)
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res)); EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));
if (http_code >= 400)
EXCEPTION(gourou::CLIENT_HTTP_ERROR, "HTTP Error code " << http_code);
if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) && if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&
gourou::logLevel >= gourou::LG_LOG_WARN) gourou::logLevel >= gourou::LG_LOG_WARN)
std::cout << std::endl; std::cout << std::endl;
+2
View File
@@ -136,6 +136,8 @@ private:
#else #else
void *legacy, *deflt; void *legacy, *deflt;
#endif #endif
char cookiejar[64];
}; };
#endif #endif