Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16a13eed89 | ||
|
|
479869b7f2 | ||
|
|
41f1a1e980 | ||
|
|
9648157bf7 | ||
|
|
a97a915bc8 | ||
|
|
a623a3d796 | ||
|
|
7d161133c3 | ||
|
|
7d93817e49 |
10
README.md
10
README.md
@@ -70,10 +70,10 @@ BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_S
|
|||||||
Utils
|
Utils
|
||||||
-----
|
-----
|
||||||
|
|
||||||
You can import configuration from your eReader or create a new one with utils/activate :
|
You can import configuration from your eReader or create a new one with _utils/adept\_activate_ :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
./utils/activate -u <AdobeID USERNAME>
|
./utils/adept_activate -u <AdobeID USERNAME>
|
||||||
|
|
||||||
Then a _./.adept_ directory is created with all configuration file
|
Then a _./.adept_ directory is created with all configuration file
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ To download an ePub/PDF :
|
|||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
./utils/acsmdownloader -f <ACSM_FILE>
|
./utils/acsmdownloader -f <ACSM_FILE>
|
||||||
|
|
||||||
To export your private key :
|
To export your private key (for DeDRM software) :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
|
./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
|
||||||
@@ -92,6 +92,9 @@ To remove ADEPT DRM :
|
|||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
./utils/adept_remove -f <encryptedFile>
|
./utils/adept_remove -f <encryptedFile>
|
||||||
|
|
||||||
|
You can get utils full options description with -h or --help switch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
---------
|
---------
|
||||||
@@ -113,3 +116,4 @@ Special thanks
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
* _Jens_ for all test samples and utils testing
|
* _Jens_ for all test samples and utils testing
|
||||||
|
* _Milian_ for debug & code
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
For libgourou :
|
|
||||||
|
|
||||||
* None
|
|
||||||
|
|
||||||
For utils :
|
|
||||||
|
|
||||||
* QT5Core
|
|
||||||
* QT5Network
|
|
||||||
* OpenSSL
|
|
||||||
* libzip
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Utils
|
|
||||||
-----
|
|
||||||
|
|
||||||
You can import configuration from your eReader or create a new one with utils/activate :
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./activate -u <AdobeID USERNAME>
|
|
||||||
|
|
||||||
Then a _./.adept_ directory is created with all configuration file
|
|
||||||
|
|
||||||
To download an ePub/PDF :
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./acsmdownloader -f <ACSM_FILE>
|
|
||||||
|
|
||||||
To export your private key :
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./acsmdownloader --export-private-key [-o adobekey_1.der]
|
|
||||||
|
|
||||||
To remove ADEPT DRM :
|
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./adept_remove -f <encryptedFile>
|
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
|
||||||
---------
|
|
||||||
|
|
||||||
Grégory Soutadé
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
libgourou : LGPL v3 or later
|
|
||||||
|
|
||||||
utils : BSD
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Special thanks
|
|
||||||
--------------
|
|
||||||
|
|
||||||
* _Jens_ for all test samples and utils testing
|
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LIBGOUROU_VERSION "0.5.1"
|
#define LIBGOUROU_VERSION "0.5.2"
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ namespace gourou
|
|||||||
CLIENT_ZIP_ERROR,
|
CLIENT_ZIP_ERROR,
|
||||||
CLIENT_GENERIC_EXCEPTION,
|
CLIENT_GENERIC_EXCEPTION,
|
||||||
CLIENT_NETWORK_ERROR,
|
CLIENT_NETWORK_ERROR,
|
||||||
|
CLIENT_INVALID_PKCS8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DRM_REMOVAL_ERROR {
|
enum DRM_REMOVAL_ERROR {
|
||||||
@@ -118,7 +119,8 @@ namespace gourou
|
|||||||
DRM_FILE_ERROR,
|
DRM_FILE_ERROR,
|
||||||
DRM_FORMAT_NOT_SUPPORTED,
|
DRM_FORMAT_NOT_SUPPORTED,
|
||||||
DRM_IN_OUT_EQUALS,
|
DRM_IN_OUT_EQUALS,
|
||||||
DRM_MISSING_PARAMETER
|
DRM_MISSING_PARAMETER,
|
||||||
|
DRM_INVALID_KEY_SIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -927,8 +927,12 @@ namespace gourou
|
|||||||
|
|
||||||
void DRMProcessor::decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey)
|
void DRMProcessor::decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey)
|
||||||
{
|
{
|
||||||
|
if (encryptedKey.size() != 172)
|
||||||
|
EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported");
|
||||||
|
|
||||||
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
||||||
|
|
||||||
|
|
||||||
std::string privateKeyData = user->getPrivateLicenseKey();
|
std::string privateKeyData = user->getPrivateLicenseKey();
|
||||||
ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData);
|
ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData);
|
||||||
|
|
||||||
@@ -1006,9 +1010,20 @@ namespace gourou
|
|||||||
_clearData[dataOutLength] = 'Z';
|
_clearData[dataOutLength] = 'Z';
|
||||||
clearData.resize(dataOutLength+1);
|
clearData.resize(dataOutLength+1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
client->inflate(clearData, inflateData);
|
client->inflate(clearData, inflateData);
|
||||||
|
|
||||||
client->zipWriteFile(zipHandler, encryptedFile, inflateData);
|
client->zipWriteFile(zipHandler, encryptedFile, inflateData);
|
||||||
|
}
|
||||||
|
catch(gourou::Exception& e)
|
||||||
|
{
|
||||||
|
if (e.getErrorCode() == CLIENT_ZIP_ERROR)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(ERROR, e.what() << std::endl << "Skip file " << encryptedFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
it->node().parent().remove_child(it->node());
|
it->node().parent().remove_child(it->node());
|
||||||
}
|
}
|
||||||
@@ -1190,14 +1205,14 @@ namespace gourou
|
|||||||
if ((*datasIt)->type() != uPDFParser::DataType::STREAM)
|
if ((*datasIt)->type() != uPDFParser::DataType::STREAM)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId());
|
|
||||||
|
|
||||||
stream = (uPDFParser::Stream*) (*datasIt);
|
stream = (uPDFParser::Stream*) (*datasIt);
|
||||||
unsigned char* encryptedData = stream->data();
|
unsigned char* encryptedData = stream->data();
|
||||||
unsigned int dataLength = stream->dataLength();
|
unsigned int dataLength = stream->dataLength();
|
||||||
unsigned char* clearData = new unsigned char[dataLength];
|
unsigned char* clearData = new unsigned char[dataLength];
|
||||||
unsigned int dataOutLength;
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength());
|
||||||
|
|
||||||
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||||
tmpKey, 16, /* Key */
|
tmpKey, 16, /* Key */
|
||||||
NULL, 0, /* IV */
|
NULL, 0, /* IV */
|
||||||
|
|||||||
@@ -282,17 +282,19 @@ int main(int argc, char** argv)
|
|||||||
goto end;
|
goto end;
|
||||||
if (key == 'y' || key == 'Y')
|
if (key == 'y' || key == 'Y')
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Clean STDIN buf
|
// Clean STDIN buf
|
||||||
while ((key = getchar()) != '\n')
|
while ((key = getchar()) != '\n')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
std::string pass;
|
||||||
if (!password)
|
if (!password)
|
||||||
{
|
{
|
||||||
char prompt[128];
|
char prompt[128];
|
||||||
std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username);
|
std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username);
|
||||||
std::string pass = getpass((const char*)prompt, false);
|
pass = getpass((const char*)prompt, false);
|
||||||
password = pass.c_str();
|
password = pass.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,20 @@ void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int len
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* HTTP interface */
|
/* HTTP interface */
|
||||||
|
#define DISPLAY_THRESHOLD 10*1024 // Threshold to display download progression
|
||||||
|
|
||||||
|
static void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
|
// For "big" files only
|
||||||
|
if (bytesTotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN)
|
||||||
|
{
|
||||||
|
int percent = 0;
|
||||||
|
if (bytesTotal)
|
||||||
|
percent = (bytesReceived * 100) / bytesTotal;
|
||||||
|
|
||||||
|
std::cout << "\rDownload " << percent << "%" << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders)
|
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders)
|
||||||
{
|
{
|
||||||
QNetworkRequest request(QUrl(URL.c_str()));
|
QNetworkRequest request(QUrl(URL.c_str()));
|
||||||
@@ -112,10 +126,14 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
else
|
else
|
||||||
reply = networkManager.get(request);
|
reply = networkManager.get(request);
|
||||||
|
|
||||||
QCoreApplication* app = QCoreApplication::instance();
|
QEventLoop loop;
|
||||||
networkManager.moveToThread(app->thread());
|
|
||||||
while (!reply->isFinished())
|
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
app->processEvents();
|
// Handled just below
|
||||||
|
QObject::connect(reply, &QNetworkReply::errorOccurred, &loop, &QEventLoop::quit);
|
||||||
|
QObject::connect(reply, &QNetworkReply::downloadProgress, &loop, downloadProgress);
|
||||||
|
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
QByteArray location = reply->rawHeader("Location");
|
QByteArray location = reply->rawHeader("Location");
|
||||||
if (location.size() != 0)
|
if (location.size() != 0)
|
||||||
@@ -136,6 +154,8 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
replyData = reply->readAll();
|
replyData = reply->readAll();
|
||||||
|
if (replyData.size() >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN)
|
||||||
|
std::cout << std::endl;
|
||||||
if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
|
if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
|
||||||
{
|
{
|
||||||
GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data());
|
GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data());
|
||||||
@@ -184,7 +204,7 @@ void DRMProcessorClientImpl::RSAPrivateDecrypt(const unsigned char* RSAKey, unsi
|
|||||||
PKCS8_PRIV_KEY_INFO* p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(mem, NULL);
|
PKCS8_PRIV_KEY_INFO* p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(mem, NULL);
|
||||||
|
|
||||||
if (!p8inf)
|
if (!p8inf)
|
||||||
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
|
EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
|
||||||
EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);
|
EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);
|
||||||
RSA * rsa;
|
RSA * rsa;
|
||||||
@@ -510,7 +530,12 @@ void DRMProcessorClientImpl::inflate(gourou::ByteArray& data, gourou::ByteArray&
|
|||||||
ret = ::inflate(&infstream, Z_FINISH);
|
ret = ::inflate(&infstream, Z_FINISH);
|
||||||
while (ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR)
|
while (ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR)
|
||||||
{
|
{
|
||||||
|
// Real error
|
||||||
|
if (ret == Z_BUF_ERROR && infstream.avail_out == (uInt)dataSize)
|
||||||
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Inflate error, code " << zError(ret) << ", msg " << infstream.msg);
|
||||||
|
|
||||||
result.append(buffer, dataSize-infstream.avail_out);
|
result.append(buffer, dataSize-infstream.avail_out);
|
||||||
|
|
||||||
if ((ret == Z_OK && infstream.avail_out != 0) || ret == Z_STREAM_END)
|
if ((ret == Z_OK && infstream.avail_out != 0) || ret == Z_STREAM_END)
|
||||||
break;
|
break;
|
||||||
infstream.avail_out = (uInt)dataSize; // size of output
|
infstream.avail_out = (uInt)dataSize; // size of output
|
||||||
@@ -518,7 +543,6 @@ void DRMProcessorClientImpl::inflate(gourou::ByteArray& data, gourou::ByteArray&
|
|||||||
ret = ::inflate(&infstream, Z_FINISH);
|
ret = ::inflate(&infstream, Z_FINISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ret == Z_STREAM_END)
|
if (ret == Z_STREAM_END)
|
||||||
ret = inflateEnd(&infstream);
|
ret = inflateEnd(&infstream);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user