Compare commits

...

18 Commits

Author SHA1 Message Date
soutade 6bff559806 Fix mkstemp unused result warning 2026-01-12 21:48:41 +01:00
soutade 8a86bafbdc Add PKCS1 Type 2 padding for Signing request 2026-01-12 21:47:34 +01:00
soutade 28aefba6d6 Update README.md 2025-11-17 22:29:35 +01:00
soutade e0e2bc7430 Update uPDFParser git address 2025-08-25 20:37:52 +02:00
soutade d3c90f03bb Update README.md 2025-04-21 08:59:51 +02:00
soutade 469d378f9a Update version to 0.8.7 2025-02-16 21:32:51 +01:00
Philipp Pagel 98b531a232 Fix setup.sh
fix path for updfparser
2025-02-11 18:12:36 +01:00
soutade 956bad3068 Update README.md 2024-12-31 13:21:14 +01:00
ismail d9a920b062 Use $HOME variable if it exists
Signed-off-by: Ismail Dönmez <ismail@i10z.com>
2024-09-22 10:55:54 +02:00
soutade 204500117d Typo fix 2024-09-19 08:41:15 +02:00
soutade ce2cf4192a Update README.md 2024-08-31 08:27:53 +02:00
soutade 68bf48df27 Update README.md 2024-08-27 21:48:39 +02:00
soutade 81faf1f9be adept_loan_mgt: manage cases were name is empty 2024-04-15 07:37:28 +02:00
nicokosi f60abf04d8 Fix typos in README.md 2024-04-11 06:05:23 +02:00
soutade 0d77cf55e1 Update version 2024-03-28 21:58:07 +01:00
soutade 86a79cc381 Remove whole EBX objects for PDF when removing DRM 2024-03-28 21:54:23 +01:00
soutade 68bf982b6f Fix use after free in DRMProcessorClientImpl::sendHTTPRequest() 2024-01-24 19:13:22 +01:00
soutade ef8c2644ca Update version 2024-01-16 11:09:31 +01:00
8 changed files with 133 additions and 50 deletions
+30 -7
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
@@ -46,12 +46,14 @@ _internals_ :
For utils: For utils:
* libcurl * libcurl
* OpenSSL * openssl
* libzip * libzip
* libpugixml * libpugixml
Internal libraries are automatically fetched and statically compiled during the first run. External & utils dependencies has to be installed by your package manager (_apt_ for example).
Use _-dev_ flavours to get needed headers.
Internal libraries are automatically fetched and statically compiled during the first compilation.
When you update libgourou's repository, **don't forget to update internal libraries** with: When you update libgourou's repository, **don't forget to update internal libraries** with:
make update_lib make update_lib
@@ -124,6 +126,12 @@ To return a loaned book :
You can get utils full options description with -h or --help switch You can get utils full options description with -h or --help switch
Binary packages
---------------
Compiled version (and AppImage) of libgourou and utils are available in [Release page](https://forge.soutade.fr/soutade/libgourou/releases)
Docker Docker
------ ------
@@ -150,3 +158,18 @@ 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_
* _Radon_
+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.7"
namespace gourou namespace gourou
{ {
+1 -1
View File
@@ -2,7 +2,7 @@
# uPDFParser # uPDFParser
if [ ! -d lib/updfparser ] ; then if [ ! -d lib/updfparser ] ; then
git clone git://soutade.fr/updfparser.git lib/updfparser git clone https://forge.soutade.fr/soutade/uPDFParser.git lib/updfparser
pushd lib/updfparser pushd lib/updfparser
make BUILD_STATIC=1 BUILD_SHARED=0 make BUILD_STATIC=1 BUILD_SHARED=0
popd popd
+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
+12 -1
View File
@@ -859,6 +859,12 @@ namespace gourou
std::string DRMProcessor::getDefaultAdeptDir(void) std::string DRMProcessor::getDefaultAdeptDir(void)
{ {
#ifndef DEFAULT_ADEPT_DIR #ifndef DEFAULT_ADEPT_DIR
const char* home = getenv("HOME");
if (home)
return home + std::string("/.config/adept/");
else
{
const char* user = getenv("USER"); const char* user = getenv("USER");
if (user && user[0]) if (user && user[0])
@@ -867,6 +873,7 @@ namespace gourou
} }
else else
return LOCAL_ADEPT_DIR; return LOCAL_ADEPT_DIR;
}
#else #else
return DEFAULT_ADEPT_DIR "/"; return DEFAULT_ADEPT_DIR "/";
#endif #endif
@@ -1317,6 +1324,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;
@@ -1375,7 +1383,7 @@ namespace gourou
if (object->objectId() == ebxId) if (object->objectId() == ebxId)
{ {
// object->deleteKey("Filter"); ebxObjects.push_back(object);
continue; continue;
} }
@@ -1485,6 +1493,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");
+9 -2
View File
@@ -229,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++;
@@ -276,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;
+53 -13
View File
@@ -68,7 +68,13 @@ DRMProcessorClientImpl::DRMProcessorClientImpl():
strcpy(cookiejar, "/tmp/libgourou_cookie_jar_XXXXXX"); strcpy(cookiejar, "/tmp/libgourou_cookie_jar_XXXXXX");
#endif #endif
mkstemp(cookiejar); int fd = mkstemp(cookiejar);
if (fd >= 0)
close(fd);
else
{
EXCEPTION(gourou::CLIENT_FILE_ERROR, "mkstemp error");
}
} }
DRMProcessorClientImpl::~DRMProcessorClientImpl() DRMProcessorClientImpl::~DRMProcessorClientImpl()
@@ -298,14 +304,15 @@ 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));
long http_code = 400;
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code >= 400) if (http_code >= 400)
EXCEPTION(gourou::CLIENT_HTTP_ERROR, "HTTP Error code " << http_code); EXCEPTION(gourou::CLIENT_HTTP_ERROR, "HTTP Error code " << http_code);
@@ -332,11 +339,33 @@ void DRMProcessorClientImpl::padWithPKCS1(unsigned char* out, unsigned int outLe
0x00 0x01 0xff * n 0x00 dataIn 0x00 0x01 0xff * n 0x00 dataIn
*/ */
memset(out, 0xFF, outLength); memset(out, 0xFF, outLength - inLength - 1);
out[0] = 0x0; out[0] = 0x0;
out[1] = 0x1; out[1] = 0x1;
out[outLength - inLength - 1] = 0x00; out[outLength - inLength - 1] = 0x00;
memcpy(&out[outLength - inLength], in, inLength);
}
void DRMProcessorClientImpl::padWithPKCS1Type2(unsigned char* out, unsigned int outLength,
const unsigned char* in, unsigned int inLength)
{
if (outLength < (inLength + 3))
EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding");
/*
PKCS1v5 type 2 Padding is :
0x00 0x02 0xXX * n 0x00 dataIn
XX is random non zero data
*/
RAND_bytes(&out[2], outLength - inLength - 1);
out[0] = 0x0;
out[1] = 0x2;
out[outLength - inLength - 1] = 0x00;
memcpy(&out[outLength - inLength], in, inLength); memcpy(&out[outLength - inLength], in, inLength);
} }
@@ -428,33 +457,45 @@ void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsig
unsigned char* res) unsigned char* res)
{ {
size_t outlen; size_t outlen;
unsigned char* tmp;
X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength); X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength);
if (!x509) if (!x509)
EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate"); EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate");
EVP_PKEY_CTX *ctx; EVP_PKEY_CTX *ctx;
EVP_PKEY * evpKey = X509_get_pubkey(x509); EVP_PKEY * pkey = X509_get_pubkey(x509);
if (!evpKey) if (!pkey)
EXCEPTION(gourou::CLIENT_NO_PUB_KEY, "No public key in certificate"); EXCEPTION(gourou::CLIENT_NO_PUB_KEY, "No public key in certificate");
ctx = EVP_PKEY_CTX_new(evpKey, NULL); ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (EVP_PKEY_encrypt_init(ctx) <= 0) if (EVP_PKEY_encrypt_init(ctx) <= 0)
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
int ret = EVP_PKEY_encrypt(ctx, res, &outlen, data, dataLength); outlen = EVP_PKEY_get_size(pkey);
tmp = (unsigned char*)malloc(outlen);
/*
PKCS1 functions are no more exported.
Some OpenSSL libraries still use type 1
*/
padWithPKCS1Type2(tmp, outlen, data, dataLength);
int ret = EVP_PKEY_encrypt(ctx, res, &outlen, tmp, outlen);
EVP_PKEY_CTX_free(ctx); EVP_PKEY_CTX_free(ctx);
free(tmp);
EVP_PKEY_free(pkey);
if (ret < 0) if (ret < 0)
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_free(evpKey);
} }
void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits) void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits)
@@ -468,7 +509,6 @@ void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits)
EVP_PKEY_keygen_init(ctx); EVP_PKEY_keygen_init(ctx);
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keyLengthBits); EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keyLengthBits);
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);
EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, bn); EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, bn);
EVP_PKEY_keygen(ctx, &key); EVP_PKEY_keygen(ctx, &key);
+2
View File
@@ -130,6 +130,8 @@ private:
void padWithPKCS1(unsigned char* out, unsigned int outLength, void padWithPKCS1(unsigned char* out, unsigned int outLength,
const unsigned char* in, unsigned int inLength); const unsigned char* in, unsigned int inLength);
void padWithPKCS1Type2(unsigned char* out, unsigned int outLength,
const unsigned char* in, unsigned int inLength);
#if OPENSSL_VERSION_MAJOR >= 3 #if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER *legacy, *deflt; OSSL_PROVIDER *legacy, *deflt;