diff --git a/include/bytearray.h b/include/bytearray.h index 0a5ed39..d5339d1 100644 --- a/include/bytearray.h +++ b/include/bytearray.h @@ -32,6 +32,7 @@ namespace gourou * * Data handled is first copied in a newly allocated buffer * and then shared between all copies until last object is destroyed + * (internal reference counter == 0) */ class ByteArray { @@ -39,8 +40,18 @@ namespace gourou /** * @brief Create an empty byte array + * + * @param useMalloc If true, use malloc() instead of new[] for allocation */ - ByteArray(); + ByteArray(bool useMalloc=false); + + /** + * @brief Create an empty byte array of length bytes + * + * @param length Length of data + * @param useMalloc If true, use malloc() instead of new[] for allocation + */ + ByteArray(unsigned int length, bool useMalloc=false); /** * @brief Initialize ByteArray with a copy of data @@ -119,14 +130,36 @@ namespace gourou void append(const std::string& str); /** - * @brief Get internal data. Must bot be modified nor freed + * @brief Get internal data. Must bot be freed */ - const unsigned char* data() {return _data;} + unsigned char* data() {return _data;} + + /** + * @brief Get internal data and increment internal reference counter. + * Must bot be freed + */ + unsigned char* takeShadowData() {addRef() ; return _data;} + + /** + * @brief Release shadow data. It can now be freed by ByteArray + */ + void releaseShadowData() {delRef();} /** * @brief Get internal data length */ - unsigned int length() {return _length;} + unsigned int length() const {return _length;} + + /** + * @brief Get internal data length + */ + unsigned int size() const {return length();} + + /** + * @brief Create a new internal buffer of length bytes + * All previous data is lost + */ + void resize(unsigned int length); ByteArray& operator=(const ByteArray& other); @@ -134,10 +167,11 @@ namespace gourou void initData(const unsigned char* data, unsigned int length); void addRef(); void delRef(); - - const unsigned char* _data; + + bool _useMalloc; + unsigned char* _data; unsigned int _length; - static std::map refCounter; + static std::map refCounter; }; } #endif diff --git a/include/drmprocessorclient.h b/include/drmprocessorclient.h index df07042..c63a539 100644 --- a/include/drmprocessorclient.h +++ b/include/drmprocessorclient.h @@ -128,6 +128,22 @@ namespace gourou const unsigned char* data, unsigned dataLength, unsigned char* res) = 0; + /** + * @brief Decrypt data with RSA private key. Data is padded using PKCS1.5 + * + * @param RSAKey RSA key in binary form + * @param RSAKeyLength RSA key length + * @param keyType Key type + * @param password Optional password for RSA PKCS12 certificate + * @param data Data to encrypt + * @param dataLength Data length + * @param res Encryption result (pre allocated buffer) + */ + virtual void RSAPrivateDecrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, + const RSA_KEY_TYPE keyType, const std::string& password, + const unsigned char* data, unsigned dataLength, + unsigned char* res) = 0; + /** * @brief Encrypt data with RSA public key. Data is padded using PKCS1.5 * @@ -331,19 +347,19 @@ namespace gourou * * @param handler ZIP file handler * @param path Internal path inside zip file - * - * @return File content + * @param result Result buffer + * @param decompress If false, don't decompress read data */ - virtual std::string zipReadFile(void* handler, const std::string& path) = 0; + virtual void zipReadFile(void* handler, const std::string& path, ByteArray& result, bool decompress=true) = 0; /** * @brief Write zip internal file * * @param handler ZIP file handler * @param path Internal path inside zip file - * @param content Internal file content + * @param content File content */ - virtual void zipWriteFile(void* handler, const std::string& path, const std::string& content) = 0; + virtual void zipWriteFile(void* handler, const std::string& path, ByteArray& content) = 0; /** * @brief Delete zip internal file @@ -367,7 +383,7 @@ namespace gourou * @param result Zipped data * @param wbits Window bits value for libz */ - virtual void inflate(std::string data, gourou::ByteArray& result, + virtual void inflate(gourou::ByteArray& data, gourou::ByteArray& result, int wbits=-15) = 0; /** @@ -378,7 +394,7 @@ namespace gourou * @param wbits Window bits value for libz * @param compressionLevel Compression level for libz */ - virtual void deflate(std::string data, gourou::ByteArray& result, + virtual void deflate(gourou::ByteArray& data, gourou::ByteArray& result, int wbits=-15, int compressionLevel=8) = 0; }; diff --git a/include/libgourou.h b/include/libgourou.h index 9e3789b..09b6fa4 100644 --- a/include/libgourou.h +++ b/include/libgourou.h @@ -40,7 +40,7 @@ #define ACS_SERVER "http://adeactivate.adobe.com/adept" #endif -#define LIBGOUROU_VERSION "0.4.4" +#define LIBGOUROU_VERSION "0.5" namespace gourou { @@ -181,6 +181,8 @@ namespace gourou */ DRMProcessorClient* getClient() { return client; } + void removeDRM(const std::string& ePubFile, ITEM_TYPE type); + private: gourou::DRMProcessorClient* client; gourou::Device* device; @@ -204,6 +206,7 @@ namespace gourou void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate); void fetchLicenseServiceCertificate(const std::string& licenseURL, const std::string& operatorURL); + void removeEPubDRM(const std::string& ePubFile); }; } diff --git a/include/libgourou_common.h b/include/libgourou_common.h index 5af33ac..a5f01a3 100644 --- a/include/libgourou_common.h +++ b/include/libgourou_common.h @@ -112,6 +112,11 @@ namespace gourou CLIENT_NETWORK_ERROR, }; + enum DRM_REMOVAL_ERROR { + CLIENT_DRM_ERR_ENCRYPTION_KEY = 0x6000, + CLIENT_DRM_FORMAT_NOT_SUPPORTED, + }; + /** * Generic exception class */ diff --git a/src/bytearray.cpp b/src/bytearray.cpp index 08d9c92..ceb7a98 100644 --- a/src/bytearray.cpp +++ b/src/bytearray.cpp @@ -24,33 +24,48 @@ namespace gourou { - std::map ByteArray::refCounter; + std::map ByteArray::refCounter; - ByteArray::ByteArray():_data(0), _length(0) + ByteArray::ByteArray(bool useMalloc):_useMalloc(useMalloc), _data(0), _length(0) {} - ByteArray::ByteArray(const unsigned char* data, unsigned int length) + ByteArray::ByteArray(unsigned int length, bool useMalloc): + _useMalloc(useMalloc) + { + initData(0, length); + } + + ByteArray::ByteArray(const unsigned char* data, unsigned int length): + _useMalloc(false) { initData(data, length); } - ByteArray::ByteArray(const char* data, int length) + ByteArray::ByteArray(const char* data, int length): + _useMalloc(false) { if (length == -1) length = strlen(data); - initData((const unsigned char*)data, (unsigned int) length); + initData((unsigned char*)data, (unsigned int) length); } - ByteArray::ByteArray(const std::string& str) + ByteArray::ByteArray(const std::string& str): + _useMalloc(false) { initData((unsigned char*)str.c_str(), (unsigned int)str.length()); } void ByteArray::initData(const unsigned char* data, unsigned int length) { - _data = new unsigned char[length]; - memcpy((void*)_data, data, length); + if (_useMalloc) + _data = (unsigned char*)malloc(length); + else + _data = new unsigned char[length]; + + if (data) + memcpy((void*)_data, data, length); + _length = length; addRef(); @@ -58,6 +73,7 @@ namespace gourou ByteArray::ByteArray(const ByteArray& other) { + this->_useMalloc = other._useMalloc; this->_data = other._data; this->_length = other._length; @@ -68,6 +84,7 @@ namespace gourou { delRef(); + this->_useMalloc = other._useMalloc; this->_data = other._data; this->_length = other._length; @@ -97,7 +114,10 @@ namespace gourou if (refCounter[_data] == 1) { - delete[] _data; + if (_useMalloc) + free(_data); + else + delete[] _data; refCounter.erase(_data); } else @@ -152,8 +172,13 @@ namespace gourou void ByteArray::append(const unsigned char* data, unsigned int length) { - const unsigned char* oldData = _data; - unsigned char* newData = new unsigned char[_length+length]; + unsigned char* oldData = _data; + unsigned char* newData; + + if (_useMalloc) + newData = (unsigned char*)malloc(_length+length); + else + newData = new unsigned char[_length+length]; memcpy(newData, oldData, _length); @@ -170,4 +195,10 @@ namespace gourou void ByteArray::append(unsigned char c) { append(&c, 1);} void ByteArray::append(const char* str) { append((const unsigned char*)str, strlen(str));} void ByteArray::append(const std::string& str) { append((const unsigned char*)str.c_str(), str.length()); } + + void ByteArray::resize(unsigned length) + { + delRef(); + initData(0, length); + } } diff --git a/src/libgourou.cpp b/src/libgourou.cpp index b0c9de7..0cc1f13 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -597,7 +597,7 @@ namespace gourou GOUROU_LOG(INFO, "Download into " << path); - std::string rightsStr = item->getRights(); + ByteArray rightsStr(item->getRights()); if (item->getMetadata("format").find("application/pdf") != std::string::npos) res = PDF; @@ -925,4 +925,80 @@ namespace gourou int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;} void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;} + + void DRMProcessor::removeEPubDRM(const std::string& ePubFile) + { + ByteArray zipData; + void* zipHandler = client->zipOpen(ePubFile); + + client->zipReadFile(zipHandler, "META-INF/rights.xml", zipData); + pugi::xml_document rightsDoc; + rightsDoc.load_string((const char*)zipData.data()); + + std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey"); + ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey); + unsigned char decryptedKey[RSA_KEY_SIZE]; + + std::string privateKeyData = user->getPrivateLicenseKey(); + ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData); + + ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE); + std::string pkcs12 = user->getPKCS12(); + + client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(), + RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(), + arrayEncryptedKey.data(), arrayEncryptedKey.length(), decryptedKey); + + if (decryptedKey[0] != 0x00 || decryptedKey[1] != 0x02 || + decryptedKey[RSA_KEY_SIZE-16-1] != 0x00) + EXCEPTION(CLIENT_DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key"); + + client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData); + pugi::xml_document encryptionDoc; + encryptionDoc.load_string((const char*)zipData.data()); + + pugi::xpath_node_set nodeSet = encryptionDoc.select_nodes("//CipherReference"); + + for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); + it != nodeSet.end(); ++it) + { + std::string encryptedFile = it->node().attribute("URI").value(); + + GOUROU_LOG(DEBUG, "Encrypted file " << encryptedFile); + + client->zipReadFile(zipHandler, encryptedFile, zipData, false); + + unsigned char* _data = zipData.data(); + ByteArray clearData(zipData.length()-16+1); /* Reserve 1 byte for 'Z' */ + unsigned char* _clearData = clearData.data(); + gourou::ByteArray inflateData(true); + unsigned int dataOutLength; + + client->AESDecrypt(CryptoInterface::CHAIN_CBC, + decryptedKey+RSA_KEY_SIZE-16, 16, /* Key */ + _data, 16, /* IV */ + &_data[16], zipData.length()-16, + _clearData, &dataOutLength); + + // Add 'Z' at the end, done in ineptepub.py + _clearData[dataOutLength] = 'Z'; + + client->inflate(clearData, inflateData); + + client->zipWriteFile(zipHandler, encryptedFile, inflateData); + } + + client->zipDeleteFile(zipHandler, "META-INF/rights.xml"); + client->zipDeleteFile(zipHandler, "META-INF/encryption.xml"); + + client->zipClose(zipHandler); + } + + void DRMProcessor::removeDRM(const std::string& ePubFile, ITEM_TYPE type) + { + if (type == EPUB) + removeEPubDRM(ePubFile); + else + EXCEPTION(CLIENT_DRM_FORMAT_NOT_SUPPORTED, "Can't remove DRM in PDF"); + } } diff --git a/utils/drmprocessorclientimpl.cpp b/utils/drmprocessorclientimpl.cpp index 9c5ec1e..6ae111b 100644 --- a/utils/drmprocessorclientimpl.cpp +++ b/utils/drmprocessorclientimpl.cpp @@ -37,8 +37,8 @@ #include #include -#include #include +#include #include #include @@ -163,13 +163,44 @@ void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsi if (gourou::logLevel >= gourou::DEBUG) { - printf("Sig : "); - for(int i=0; i<(int)sizeof(res); i++) + printf("Encrypted : "); + for(int i=0; i= gourou::DEBUG) + { + printf("Decrypted : "); + for(int i=0; i