Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f475423c0 | ||
|
|
9b946a62b4 | ||
|
|
432eb6f6cb | ||
|
|
85b65f8d61 | ||
|
|
25f5049ab9 | ||
|
|
16a13eed89 | ||
|
|
479869b7f2 | ||
|
|
41f1a1e980 | ||
|
|
9648157bf7 | ||
|
|
a97a915bc8 | ||
|
|
a623a3d796 | ||
|
|
7d161133c3 | ||
|
|
7d93817e49 | ||
|
|
ef62fb921a | ||
|
|
e4c05bd6b3 | ||
|
|
f33891ef1c | ||
|
|
9f62cf3447 | ||
|
|
36553cdd2c | ||
|
|
ad6da2f8ab | ||
|
|
b8a4ca222e | ||
|
|
f0ff97f7d7 | ||
|
|
4fe846f78e | ||
|
|
19aacf98a2 | ||
|
|
a751327dab | ||
|
|
a79bdd1e21 | ||
|
|
55ab41613e | ||
|
|
8129ec4423 | ||
|
|
9ab66ddba9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,4 +5,5 @@ lib
|
|||||||
*~
|
*~
|
||||||
utils/acsmdownloader
|
utils/acsmdownloader
|
||||||
utils/adept_activate
|
utils/adept_activate
|
||||||
|
utils/adept_remove
|
||||||
.adept
|
.adept
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -53,8 +53,8 @@ $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
|||||||
|
|
||||||
libgourou: libgourou.a libgourou.so
|
libgourou: libgourou.a libgourou.so
|
||||||
|
|
||||||
libgourou.a: $(OBJECTS)
|
libgourou.a: $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(AR) crs $@ obj/*.o ./lib/updfparser/obj/*.o
|
$(AR) crs $@ obj/*.o $(UPDFPARSERLIB)
|
||||||
|
|
||||||
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB)
|
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -15,6 +15,7 @@ Main fucntions 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()_
|
||||||
* Register a new device : _signIn()_ and _activateDevice()_
|
* Register a new device : _signIn()_ and _activateDevice()_
|
||||||
|
* Remove DRM : _removeDRM()_
|
||||||
|
|
||||||
|
|
||||||
You can import configuration from (at least) :
|
You can import configuration from (at least) :
|
||||||
@@ -26,7 +27,7 @@ Or create a new one. Be careful : there is a limited number of devices that can
|
|||||||
|
|
||||||
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, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin.
|
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.
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
@@ -65,13 +66,14 @@ BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_S
|
|||||||
|
|
||||||
* Default value
|
* Default value
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -80,11 +82,19 @@ 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]
|
||||||
|
|
||||||
|
To remove ADEPT DRM :
|
||||||
|
|
||||||
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
|
./utils/adept_remove -f <encryptedFile>
|
||||||
|
|
||||||
|
You can get utils full options description with -h or --help switch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
---------
|
---------
|
||||||
@@ -106,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,72 +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]
|
|
||||||
|
|
||||||
|
|
||||||
Sources
|
|
||||||
-------
|
|
||||||
|
|
||||||
http://indefero.soutade.fr/p/libgourou
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
|
||||||
---------
|
|
||||||
|
|
||||||
Grégory Soutadé
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
libgourou : LGPL v3 or later
|
|
||||||
|
|
||||||
utils : BSD
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Special thanks
|
|
||||||
--------------
|
|
||||||
|
|
||||||
* _Jens_ for all test samples and utils testing
|
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ namespace gourou
|
|||||||
*
|
*
|
||||||
* Data handled is first copied in a newly allocated buffer
|
* Data handled is first copied in a newly allocated buffer
|
||||||
* and then shared between all copies until last object is destroyed
|
* and then shared between all copies until last object is destroyed
|
||||||
|
* (internal reference counter == 0)
|
||||||
*/
|
*/
|
||||||
class ByteArray
|
class ByteArray
|
||||||
{
|
{
|
||||||
@@ -39,8 +40,18 @@ namespace gourou
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create an empty byte array
|
* @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
|
* @brief Initialize ByteArray with a copy of data
|
||||||
@@ -119,14 +130,38 @@ namespace gourou
|
|||||||
void append(const std::string& str);
|
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
|
* @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 Increase or decrease internal buffer
|
||||||
|
* @param length New length of internal buffer
|
||||||
|
* @param keepData If true copy old data on new buffer, if false,
|
||||||
|
* create a new buffer with random data
|
||||||
|
*/
|
||||||
|
void resize(unsigned int length, bool keepData=true);
|
||||||
|
|
||||||
ByteArray& operator=(const ByteArray& other);
|
ByteArray& operator=(const ByteArray& other);
|
||||||
|
|
||||||
@@ -135,9 +170,10 @@ namespace gourou
|
|||||||
void addRef();
|
void addRef();
|
||||||
void delRef();
|
void delRef();
|
||||||
|
|
||||||
const unsigned char* _data;
|
bool _useMalloc;
|
||||||
|
unsigned char* _data;
|
||||||
unsigned int _length;
|
unsigned int _length;
|
||||||
static std::map<const unsigned char*, int> refCounter;
|
static std::map<unsigned char*, int> refCounter;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace gourou
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Create a digest handler (for now only SHA1 is used)
|
* @brief Create a digest handler
|
||||||
*
|
*
|
||||||
* @param digestName Digest name to instanciate
|
* @param digestName Digest name to instanciate
|
||||||
*/
|
*/
|
||||||
@@ -128,6 +128,22 @@ namespace gourou
|
|||||||
const unsigned char* data, unsigned dataLength,
|
const unsigned char* data, unsigned dataLength,
|
||||||
unsigned char* res) = 0;
|
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
|
* @brief Encrypt data with RSA public key. Data is padded using PKCS1.5
|
||||||
*
|
*
|
||||||
@@ -196,14 +212,20 @@ namespace gourou
|
|||||||
class CryptoInterface
|
class CryptoInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum CRYPTO_ALGO {
|
||||||
|
ALGO_AES=0,
|
||||||
|
ALGO_RC4
|
||||||
|
};
|
||||||
|
|
||||||
enum CHAINING_MODE {
|
enum CHAINING_MODE {
|
||||||
CHAIN_ECB=0,
|
CHAIN_ECB=0,
|
||||||
CHAIN_CBC
|
CHAIN_CBC
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Do AES encryption. If length of data is not multiple of 16, PKCS#5 padding is done
|
* @brief Do encryption. If length of data is not multiple of block size, PKCS#5 padding is done
|
||||||
*
|
*
|
||||||
|
* @param algo Algorithm to use
|
||||||
* @param chaining Chaining mode
|
* @param chaining Chaining mode
|
||||||
* @param key AES key
|
* @param key AES key
|
||||||
* @param keyLength AES key length
|
* @param keyLength AES key length
|
||||||
@@ -214,52 +236,53 @@ namespace gourou
|
|||||||
* @param dataOut Encrypted data
|
* @param dataOut Encrypted data
|
||||||
* @param dataOutLength Length of encrypted data
|
* @param dataOutLength Length of encrypted data
|
||||||
*/
|
*/
|
||||||
virtual void AESEncrypt(CHAINING_MODE chaining,
|
virtual void Encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength,
|
const unsigned char* iv, unsigned int ivLength,
|
||||||
const unsigned char* dataIn, unsigned int dataInLength,
|
const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Init AES CBC encryption
|
* @brief Init encryption
|
||||||
*
|
*
|
||||||
* @param chaining Chaining mode
|
* @param chaining Chaining mode
|
||||||
* @param key AES key
|
* @param key Key
|
||||||
* @param keyLength AES key length
|
* @param keyLength Key length
|
||||||
* @param iv IV key
|
* @param iv Optional IV key
|
||||||
* @param ivLength IV key length
|
* @param ivLength Optional IV key length
|
||||||
*
|
*
|
||||||
* @return AES handler
|
* @return AES handler
|
||||||
*/
|
*/
|
||||||
virtual void* AESEncryptInit(CHAINING_MODE chaining,
|
virtual void* EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv=0, unsigned int ivLength=0) = 0;
|
const unsigned char* iv=0, unsigned int ivLength=0) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Encrypt data
|
* @brief Encrypt data
|
||||||
*
|
*
|
||||||
* @param handler AES handler
|
* @param handler Crypto handler
|
||||||
* @param dataIn Data to encrypt
|
* @param dataIn Data to encrypt
|
||||||
* @param dataInLength Data length
|
* @param dataInLength Data length
|
||||||
* @param dataOut Encrypted data
|
* @param dataOut Encrypted data
|
||||||
* @param dataOutLength Length of encrypted data
|
* @param dataOutLength Length of encrypted data
|
||||||
*/
|
*/
|
||||||
virtual void AESEncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
virtual void EncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Finalize AES encryption (pad and encrypt last block if needed)
|
* @brief Finalizeencryption (pad and encrypt last block if needed)
|
||||||
* Destroy handler at the end
|
* Destroy handler at the end
|
||||||
*
|
*
|
||||||
* @param handler AES handler
|
* @param handler Crypto handler
|
||||||
* @param dataOut Last block of encrypted data
|
* @param dataOut Last block of encrypted data
|
||||||
* @param dataOutLength Length of encrypted data
|
* @param dataOutLength Length of encrypted data
|
||||||
*/
|
*/
|
||||||
virtual void AESEncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
virtual void EncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Do AES decryption. If length of data is not multiple of 16, PKCS#5 padding is done
|
* @brief Do decryption. If length of data is not multiple of block size, PKCS#5 padding is done
|
||||||
*
|
*
|
||||||
|
* @param algo Algorithm to use
|
||||||
* @param chaining Chaining mode
|
* @param chaining Chaining mode
|
||||||
* @param key AES key
|
* @param key AES key
|
||||||
* @param keyLength AES key length
|
* @param keyLength AES key length
|
||||||
@@ -270,47 +293,47 @@ namespace gourou
|
|||||||
* @param dataOut Encrypted data
|
* @param dataOut Encrypted data
|
||||||
* @param dataOutLength Length of encrypted data
|
* @param dataOutLength Length of encrypted data
|
||||||
*/
|
*/
|
||||||
virtual void AESDecrypt(CHAINING_MODE chaining,
|
virtual void Decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength,
|
const unsigned char* iv, unsigned int ivLength,
|
||||||
const unsigned char* dataIn, unsigned int dataInLength,
|
const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Init AES decryption
|
* @brief Init decryption
|
||||||
*
|
*
|
||||||
* @param chaining Chaining mode
|
* @param chaining Chaining mode
|
||||||
* @param key AES key
|
* @param key Key
|
||||||
* @param keyLength AES key length
|
* @param keyLength Key length
|
||||||
* @param iv IV key
|
* @param iv IV key
|
||||||
* @param ivLength IV key length
|
* @param ivLength IV key length
|
||||||
*
|
*
|
||||||
* @return AES handler
|
* @return AES handler
|
||||||
*/
|
*/
|
||||||
virtual void* AESDecryptInit(CHAINING_MODE chaining,
|
virtual void* DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv=0, unsigned int ivLength=0) = 0;
|
const unsigned char* iv=0, unsigned int ivLength=0) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Decrypt data
|
* @brief Decrypt data
|
||||||
*
|
*
|
||||||
* @param handler AES handler
|
* @param handler Crypto handler
|
||||||
* @param dataIn Data to decrypt
|
* @param dataIn Data to decrypt
|
||||||
* @param dataInLength Data length
|
* @param dataInLength Data length
|
||||||
* @param dataOut Decrypted data
|
* @param dataOut Decrypted data
|
||||||
* @param dataOutLength Length of decrypted data
|
* @param dataOutLength Length of decrypted data
|
||||||
*/
|
*/
|
||||||
virtual void AESDecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
virtual void DecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||||
/**
|
/**
|
||||||
* @brief Finalize AES decryption (decrypt last block and remove padding if it is set).
|
* @brief Finalize decryption (decrypt last block and remove padding if it is set).
|
||||||
* Destroy handler at the end
|
* Destroy handler at the end
|
||||||
*
|
*
|
||||||
* @param handler AES handler
|
* @param handler Crypto handler
|
||||||
* @param dataOut Last block decrypted data
|
* @param dataOut Last block decrypted data
|
||||||
* @param dataOutLength Length of decrypted data
|
* @param dataOutLength Length of decrypted data
|
||||||
*/
|
*/
|
||||||
virtual void AESDecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
virtual void DecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -331,19 +354,19 @@ namespace gourou
|
|||||||
*
|
*
|
||||||
* @param handler ZIP file handler
|
* @param handler ZIP file handler
|
||||||
* @param path Internal path inside zip file
|
* @param path Internal path inside zip file
|
||||||
*
|
* @param result Result buffer
|
||||||
* @return File content
|
* @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
|
* @brief Write zip internal file
|
||||||
*
|
*
|
||||||
* @param handler ZIP file handler
|
* @param handler ZIP file handler
|
||||||
* @param path Internal path inside zip file
|
* @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
|
* @brief Delete zip internal file
|
||||||
@@ -367,7 +390,7 @@ namespace gourou
|
|||||||
* @param result Zipped data
|
* @param result Zipped data
|
||||||
* @param wbits Window bits value for libz
|
* @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;
|
int wbits=-15) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -378,7 +401,7 @@ namespace gourou
|
|||||||
* @param wbits Window bits value for libz
|
* @param wbits Window bits value for libz
|
||||||
* @param compressionLevel Compression level 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;
|
int wbits=-15, int compressionLevel=8) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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.4.3"
|
#define LIBGOUROU_VERSION "0.5.3"
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
@@ -164,6 +164,9 @@ namespace gourou
|
|||||||
*/
|
*/
|
||||||
std::string serializeRSAPrivateKey(void* rsa);
|
std::string serializeRSAPrivateKey(void* rsa);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Export clear private license key into path
|
||||||
|
*/
|
||||||
void exportPrivateLicenseKey(std::string path);
|
void exportPrivateLicenseKey(std::string path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,6 +184,18 @@ namespace gourou
|
|||||||
*/
|
*/
|
||||||
DRMProcessorClient* getClient() { return client; }
|
DRMProcessorClient* getClient() { return client; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove ADEPT DRM
|
||||||
|
* Warning: for PDF format, filenameIn must be different than filenameOut
|
||||||
|
*
|
||||||
|
* @param filenameIn Input file (with ADEPT DRM)
|
||||||
|
* @param filenameOut Output file (without ADEPT DRM)
|
||||||
|
* @param type Type of file (ePub or PDF)
|
||||||
|
* @param encryptionKey Optional encryption key, do not try to decrypt the one inside input file
|
||||||
|
* @param encryptionKeySize Size of encryption key (if provided)
|
||||||
|
*/
|
||||||
|
void removeDRM(const std::string& filenameIn, const std::string& filenameOut, ITEM_TYPE type, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
gourou::DRMProcessorClient* client;
|
gourou::DRMProcessorClient* client;
|
||||||
gourou::Device* device;
|
gourou::Device* device;
|
||||||
@@ -204,6 +219,13 @@ namespace gourou
|
|||||||
void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate);
|
void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate);
|
||||||
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
||||||
const std::string& operatorURL);
|
const std::string& operatorURL);
|
||||||
|
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey);
|
||||||
|
void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
|
||||||
|
void generatePDFObjectKey(int version,
|
||||||
|
const unsigned char* masterKey, unsigned int masterKeyLength,
|
||||||
|
int objectId, int objectGenerationNumber,
|
||||||
|
unsigned char* keyOut);
|
||||||
|
void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ namespace gourou
|
|||||||
USER_INVALID_ACTIVATION_FILE,
|
USER_INVALID_ACTIVATION_FILE,
|
||||||
USER_NO_AUTHENTICATION_URL,
|
USER_NO_AUTHENTICATION_URL,
|
||||||
USER_NO_PROPERTY,
|
USER_NO_PROPERTY,
|
||||||
|
USER_INVALID_INPUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FULFILL_ITEM_ERROR {
|
enum FULFILL_ITEM_ERROR {
|
||||||
@@ -110,6 +111,17 @@ 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 {
|
||||||
|
DRM_ERR_ENCRYPTION_KEY = 0x6000,
|
||||||
|
DRM_VERSION_NOT_SUPPORTED,
|
||||||
|
DRM_FILE_ERROR,
|
||||||
|
DRM_FORMAT_NOT_SUPPORTED,
|
||||||
|
DRM_IN_OUT_EQUALS,
|
||||||
|
DRM_MISSING_PARAMETER,
|
||||||
|
DRM_INVALID_KEY_SIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,33 +24,48 @@
|
|||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
std::map<const unsigned char*, int> ByteArray::refCounter;
|
std::map<unsigned char*, int> 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);
|
initData(data, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArray::ByteArray(const char* data, int length)
|
ByteArray::ByteArray(const char* data, int length):
|
||||||
|
_useMalloc(false)
|
||||||
{
|
{
|
||||||
if (length == -1)
|
if (length == -1)
|
||||||
length = strlen(data);
|
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());
|
initData((unsigned char*)str.c_str(), (unsigned int)str.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ByteArray::initData(const unsigned char* data, unsigned int length)
|
void ByteArray::initData(const unsigned char* data, unsigned int length)
|
||||||
{
|
{
|
||||||
|
if (_useMalloc)
|
||||||
|
_data = (unsigned char*)malloc(length);
|
||||||
|
else
|
||||||
_data = new unsigned char[length];
|
_data = new unsigned char[length];
|
||||||
|
|
||||||
|
if (data)
|
||||||
memcpy((void*)_data, data, length);
|
memcpy((void*)_data, data, length);
|
||||||
|
|
||||||
_length = length;
|
_length = length;
|
||||||
|
|
||||||
addRef();
|
addRef();
|
||||||
@@ -58,6 +73,7 @@ namespace gourou
|
|||||||
|
|
||||||
ByteArray::ByteArray(const ByteArray& other)
|
ByteArray::ByteArray(const ByteArray& other)
|
||||||
{
|
{
|
||||||
|
this->_useMalloc = other._useMalloc;
|
||||||
this->_data = other._data;
|
this->_data = other._data;
|
||||||
this->_length = other._length;
|
this->_length = other._length;
|
||||||
|
|
||||||
@@ -68,6 +84,7 @@ namespace gourou
|
|||||||
{
|
{
|
||||||
delRef();
|
delRef();
|
||||||
|
|
||||||
|
this->_useMalloc = other._useMalloc;
|
||||||
this->_data = other._data;
|
this->_data = other._data;
|
||||||
this->_length = other._length;
|
this->_length = other._length;
|
||||||
|
|
||||||
@@ -97,6 +114,9 @@ namespace gourou
|
|||||||
|
|
||||||
if (refCounter[_data] == 1)
|
if (refCounter[_data] == 1)
|
||||||
{
|
{
|
||||||
|
if (_useMalloc)
|
||||||
|
free(_data);
|
||||||
|
else
|
||||||
delete[] _data;
|
delete[] _data;
|
||||||
refCounter.erase(_data);
|
refCounter.erase(_data);
|
||||||
}
|
}
|
||||||
@@ -152,22 +172,44 @@ namespace gourou
|
|||||||
|
|
||||||
void ByteArray::append(const unsigned char* data, unsigned int length)
|
void ByteArray::append(const unsigned char* data, unsigned int length)
|
||||||
{
|
{
|
||||||
const unsigned char* oldData = _data;
|
if (!length)
|
||||||
unsigned char* newData = new unsigned char[_length+length];
|
return;
|
||||||
|
|
||||||
memcpy(newData, oldData, _length);
|
unsigned int oldLength = _length;
|
||||||
|
|
||||||
delRef();
|
resize(_length+length, true);
|
||||||
|
|
||||||
memcpy(&newData[_length], data, length);
|
memcpy(&_data[oldLength], data, length);
|
||||||
_length += length;
|
|
||||||
|
|
||||||
_data = newData;
|
|
||||||
|
|
||||||
addRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ByteArray::append(unsigned char c) { append(&c, 1);}
|
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 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::append(const std::string& str) { append((const unsigned char*)str.c_str(), str.length()); }
|
||||||
|
|
||||||
|
void ByteArray::resize(unsigned length, bool keepData)
|
||||||
|
{
|
||||||
|
if (length == _length)
|
||||||
|
return;
|
||||||
|
else if (length < _length)
|
||||||
|
_length = length ; // Don't touch data
|
||||||
|
else // New size >
|
||||||
|
{
|
||||||
|
unsigned char* newData;
|
||||||
|
|
||||||
|
if (_useMalloc)
|
||||||
|
newData = (unsigned char*)malloc(_length+length);
|
||||||
|
else
|
||||||
|
newData = new unsigned char[_length+length];
|
||||||
|
|
||||||
|
if (keepData)
|
||||||
|
memcpy(newData, _data, _length);
|
||||||
|
|
||||||
|
delRef();
|
||||||
|
|
||||||
|
_length = length;
|
||||||
|
_data = newData;
|
||||||
|
|
||||||
|
addRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
#define ASN_TEXT 0x04
|
#define ASN_TEXT 0x04
|
||||||
#define ASN_ATTRIBUTE 0x05
|
#define ASN_ATTRIBUTE 0x05
|
||||||
|
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
GOUROU_LOG_LEVEL logLevel = WARN;
|
GOUROU_LOG_LEVEL logLevel = WARN;
|
||||||
@@ -597,7 +596,7 @@ namespace gourou
|
|||||||
|
|
||||||
GOUROU_LOG(INFO, "Download into " << path);
|
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)
|
if (item->getMetadata("format").find("application/pdf") != std::string::npos)
|
||||||
res = PDF;
|
res = PDF;
|
||||||
@@ -851,7 +850,7 @@ namespace gourou
|
|||||||
// Generate IV in front
|
// Generate IV in front
|
||||||
client->randBytes(encrypted_data, 16);
|
client->randBytes(encrypted_data, 16);
|
||||||
|
|
||||||
client->AESEncrypt(CryptoInterface::CHAIN_CBC,
|
client->Encrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||||
deviceKey, 16, encrypted_data, 16,
|
deviceKey, 16, encrypted_data, 16,
|
||||||
data, len,
|
data, len,
|
||||||
encrypted_data+16, &outLen);
|
encrypted_data+16, &outLen);
|
||||||
@@ -870,7 +869,7 @@ namespace gourou
|
|||||||
const unsigned char* deviceKey = device->getDeviceKey();
|
const unsigned char* deviceKey = device->getDeviceKey();
|
||||||
unsigned char* decrypted_data = new unsigned char[len-16];
|
unsigned char* decrypted_data = new unsigned char[len-16];
|
||||||
|
|
||||||
client->AESDecrypt(CryptoInterface::CHAIN_CBC,
|
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||||
deviceKey, 16, data, 16,
|
deviceKey, 16, data, 16,
|
||||||
data+16, len-16,
|
data+16, len-16,
|
||||||
decrypted_data, &outLen);
|
decrypted_data, &outLen);
|
||||||
@@ -925,4 +924,353 @@ namespace gourou
|
|||||||
|
|
||||||
int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;}
|
int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;}
|
||||||
void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;}
|
void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
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(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
|
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
|
{
|
||||||
|
ByteArray zipData;
|
||||||
|
bool removeEncryptionXML = true;
|
||||||
|
void* zipHandler = client->zipOpen(filenameOut);
|
||||||
|
|
||||||
|
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");
|
||||||
|
unsigned char decryptedKey[RSA_KEY_SIZE];
|
||||||
|
|
||||||
|
if (!encryptionKey)
|
||||||
|
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
||||||
|
if (encryptionKeySize != 16)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
|
||||||
|
|
||||||
|
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
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("//EncryptedData");
|
||||||
|
|
||||||
|
for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();
|
||||||
|
it != nodeSet.end(); ++it)
|
||||||
|
{
|
||||||
|
pugi::xml_node encryptionMethod = it->node().child("EncryptionMethod");
|
||||||
|
pugi::xml_node cipherReference = it->node().child("CipherData").child("CipherReference");
|
||||||
|
|
||||||
|
std::string encryptionType = encryptionMethod.attribute("Algorithm").value();
|
||||||
|
std::string encryptedFile = cipherReference.attribute("URI").value();
|
||||||
|
|
||||||
|
if (encryptionType == "")
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_MISSING_PARAMETER, "Missing Algorithm attribute in encryption.xml");
|
||||||
|
}
|
||||||
|
else if (encryptionType == "http://www.w3.org/2001/04/xmlenc#aes128-cbc")
|
||||||
|
{
|
||||||
|
if (encryptedFile == "")
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_MISSING_PARAMETER, "Missing URI attribute in encryption.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Encrypted file " << encryptedFile);
|
||||||
|
|
||||||
|
client->zipReadFile(zipHandler, encryptedFile, zipData, false);
|
||||||
|
|
||||||
|
unsigned char* _data = zipData.data();
|
||||||
|
ByteArray clearData(zipData.length()-16+1, true); /* Reserve 1 byte for 'Z' */
|
||||||
|
unsigned char* _clearData = clearData.data();
|
||||||
|
gourou::ByteArray inflateData(true);
|
||||||
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
|
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||||
|
decryptedKey+sizeof(decryptedKey)-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';
|
||||||
|
clearData.resize(dataOutLength+1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client->inflate(clearData, 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());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GOUROU_LOG(WARN, "Unsupported encryption algorithm " << encryptionType << ", for file " << encryptedFile);
|
||||||
|
removeEncryptionXML = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client->zipDeleteFile(zipHandler, "META-INF/rights.xml");
|
||||||
|
if (removeEncryptionXML)
|
||||||
|
client->zipDeleteFile(zipHandler, "META-INF/encryption.xml");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringXMLWriter xmlWriter;
|
||||||
|
encryptionDoc.save(xmlWriter, " ");
|
||||||
|
std::string xmlStr = xmlWriter.getResult();
|
||||||
|
ByteArray ba(xmlStr);
|
||||||
|
client->zipWriteFile(zipHandler, "META-INF/encryption.xml", ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
client->zipClose(zipHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::generatePDFObjectKey(int version,
|
||||||
|
const unsigned char* masterKey, unsigned int masterKeyLength,
|
||||||
|
int objectId, int objectGenerationNumber,
|
||||||
|
unsigned char* keyOut)
|
||||||
|
{
|
||||||
|
switch(version)
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
ByteArray toHash(masterKey, masterKeyLength);
|
||||||
|
uint32_t _objectId = objectId;
|
||||||
|
uint32_t _objectGenerationNumber = objectGenerationNumber;
|
||||||
|
toHash.append((const unsigned char*)&_objectId, 3); // Fill 3 bytes
|
||||||
|
toHash.append((const unsigned char*)&_objectGenerationNumber, 2); // Fill 2 bytes
|
||||||
|
|
||||||
|
client->digest("md5", toHash.data(), toHash.length(), keyOut);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
|
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
|
{
|
||||||
|
uPDFParser::Parser parser;
|
||||||
|
bool EBXHandlerFound = false;
|
||||||
|
|
||||||
|
if (filenameIn == filenameOut)
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_IN_OUT_EQUALS, "PDF IN must be different of PDF OUT");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Parse PDF");
|
||||||
|
parser.parse(filenameIn);
|
||||||
|
}
|
||||||
|
catch(std::invalid_argument& e)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(ERROR, "Invalid PDF");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uPDFParser::Integer* ebxVersion;
|
||||||
|
std::vector<uPDFParser::Object*> objects = parser.objects();
|
||||||
|
std::vector<uPDFParser::Object*>::iterator it;
|
||||||
|
std::vector<uPDFParser::Object*>::reverse_iterator rIt;
|
||||||
|
unsigned char decryptedKey[RSA_KEY_SIZE];
|
||||||
|
int ebxId;
|
||||||
|
|
||||||
|
for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++)
|
||||||
|
{
|
||||||
|
// Update EBX_HANDLER with rights
|
||||||
|
if ((*rIt)->hasKey("Filter") && (**rIt)["Filter"]->str() == "/EBX_HANDLER")
|
||||||
|
{
|
||||||
|
EBXHandlerFound = true;
|
||||||
|
uPDFParser::Object* ebx = *rIt;
|
||||||
|
|
||||||
|
ebxVersion = (uPDFParser::Integer*)(*ebx)["V"];
|
||||||
|
if (ebxVersion->value() != 4)
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_VERSION_NOT_SUPPORTED, "EBX encryption version not supported " << ebxVersion->value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ebx->hasKey("ADEPT_LICENSE")))
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "No ADEPT_LICENSE found");
|
||||||
|
}
|
||||||
|
|
||||||
|
uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"];
|
||||||
|
|
||||||
|
std::string value = licenseObject->value();
|
||||||
|
// Pad with '='
|
||||||
|
while ((value.size() % 4))
|
||||||
|
value += "=";
|
||||||
|
ByteArray zippedData = ByteArray::fromBase64(value);
|
||||||
|
|
||||||
|
if (zippedData.size() == 0)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Invalid ADEPT_LICENSE");
|
||||||
|
|
||||||
|
ByteArray rightsStr;
|
||||||
|
client->inflate(zippedData, rightsStr);
|
||||||
|
|
||||||
|
pugi::xml_document rightsDoc;
|
||||||
|
rightsDoc.load_string((const char*)rightsStr.data());
|
||||||
|
|
||||||
|
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||||
|
|
||||||
|
if (!encryptionKey)
|
||||||
|
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
||||||
|
if (encryptionKeySize != 16)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
|
||||||
|
|
||||||
|
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ebxId = ebx->objectId();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EBXHandlerFound)
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "EBX_HANDLER not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(it = objects.begin(); it != objects.end(); it++)
|
||||||
|
{
|
||||||
|
uPDFParser::Object* object = *it;
|
||||||
|
|
||||||
|
if (object->objectId() == ebxId)
|
||||||
|
{
|
||||||
|
// object->deleteKey("Filter");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not decrypt XRef stream
|
||||||
|
if (object->hasKey("Type") && (*object)["Type"]->str() == "/XRef")
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "XRef stream at " << object->offset());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Obj " << object->objectId());
|
||||||
|
|
||||||
|
unsigned char tmpKey[16];
|
||||||
|
|
||||||
|
generatePDFObjectKey(ebxVersion->value(),
|
||||||
|
decryptedKey+sizeof(decryptedKey)-16, 16,
|
||||||
|
object->objectId(), object->generationNumber(),
|
||||||
|
tmpKey);
|
||||||
|
|
||||||
|
uPDFParser::Dictionary& dictionary = object->dictionary();
|
||||||
|
std::map<std::string, uPDFParser::DataType*>& dictValues = dictionary.value();
|
||||||
|
std::map<std::string, uPDFParser::DataType*>::iterator dictIt;
|
||||||
|
std::map<std::string, uPDFParser::DataType*> decodedStrings;
|
||||||
|
std::string string;
|
||||||
|
|
||||||
|
/* Parse dictionary */
|
||||||
|
for (dictIt = dictValues.begin(); dictIt != dictValues.end(); dictIt++)
|
||||||
|
{
|
||||||
|
uPDFParser::DataType* dictData = dictIt->second;
|
||||||
|
if (dictData->type() == uPDFParser::DataType::STRING)
|
||||||
|
{
|
||||||
|
string = ((uPDFParser::String*) dictData)->unescapedValue();
|
||||||
|
|
||||||
|
unsigned char* encryptedData = (unsigned char*)string.c_str();
|
||||||
|
unsigned int dataLength = string.size();
|
||||||
|
unsigned char* clearData = new unsigned char[dataLength];
|
||||||
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength);
|
||||||
|
|
||||||
|
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||||
|
tmpKey, 16, /* Key */
|
||||||
|
NULL, 0, /* IV */
|
||||||
|
encryptedData, dataLength,
|
||||||
|
clearData, &dataOutLength);
|
||||||
|
|
||||||
|
decodedStrings[dictIt->first] = new uPDFParser::String(
|
||||||
|
std::string((const char*)clearData, dataOutLength));
|
||||||
|
|
||||||
|
delete[] clearData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++)
|
||||||
|
dictionary.replace(dictIt->first, dictIt->second);
|
||||||
|
|
||||||
|
std::vector<uPDFParser::DataType*>::iterator datasIt;
|
||||||
|
std::vector<uPDFParser::DataType*>& datas = object->data();
|
||||||
|
uPDFParser::Stream* stream;
|
||||||
|
|
||||||
|
for (datasIt = datas.begin(); datasIt != datas.end(); datasIt++)
|
||||||
|
{
|
||||||
|
if ((*datasIt)->type() != uPDFParser::DataType::STREAM)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stream = (uPDFParser::Stream*) (*datasIt);
|
||||||
|
unsigned char* encryptedData = stream->data();
|
||||||
|
unsigned int dataLength = stream->dataLength();
|
||||||
|
unsigned char* clearData = new unsigned char[dataLength];
|
||||||
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength());
|
||||||
|
|
||||||
|
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||||
|
tmpKey, 16, /* Key */
|
||||||
|
NULL, 0, /* IV */
|
||||||
|
encryptedData, dataLength,
|
||||||
|
clearData, &dataOutLength);
|
||||||
|
|
||||||
|
stream->setData(clearData, dataOutLength, true);
|
||||||
|
if (dataOutLength != dataLength)
|
||||||
|
GOUROU_LOG(DEBUG, "New size " << dataOutLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uPDFParser::Object& trailer = parser.getTrailer();
|
||||||
|
trailer.deleteKey("Encrypt");
|
||||||
|
|
||||||
|
parser.write(filenameOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
|
ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
|
{
|
||||||
|
if (type == PDF)
|
||||||
|
removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);
|
||||||
|
else
|
||||||
|
removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ namespace gourou {
|
|||||||
deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull);
|
deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull);
|
||||||
authenticationCertificate = gourou::extractTextElem(activationDoc, "//adept:authenticationCertificate", throwOnNull);
|
authenticationCertificate = gourou::extractTextElem(activationDoc, "//adept:authenticationCertificate", throwOnNull);
|
||||||
privateLicenseKey = gourou::extractTextElem(activationDoc, "//adept:privateLicenseKey", throwOnNull);
|
privateLicenseKey = gourou::extractTextElem(activationDoc, "//adept:privateLicenseKey", throwOnNull);
|
||||||
username = gourou::extractTextElem(activationDoc, "//adept:username", throwOnNull);
|
|
||||||
|
|
||||||
pugi::xpath_node xpath_node = activationDoc.select_node("//adept:username");
|
pugi::xpath_node xpath_node = activationDoc.select_node("//adept:username");
|
||||||
if (xpath_node)
|
if (xpath_node)
|
||||||
@@ -61,6 +60,11 @@ namespace gourou {
|
|||||||
EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");
|
EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loginMethod == "anonymous")
|
||||||
|
username = "anonymous";
|
||||||
|
else
|
||||||
|
username = gourou::extractTextElem(activationDoc, "//adept:username", throwOnNull);
|
||||||
|
|
||||||
pugi::xpath_node_set nodeSet = activationDoc.select_nodes("//adept:licenseServices/adept:licenseServiceInfo");
|
pugi::xpath_node_set nodeSet = activationDoc.select_nodes("//adept:licenseServices/adept:licenseServiceInfo");
|
||||||
for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();
|
for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();
|
||||||
it != nodeSet.end(); ++it)
|
it != nodeSet.end(); ++it)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
TARGETS=acsmdownloader adept_activate
|
TARGETS=acsmdownloader adept_activate adept_remove
|
||||||
|
|
||||||
CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
|
CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
|
||||||
|
|
||||||
@@ -18,12 +18,17 @@ else
|
|||||||
CXXFLAGS += -O2
|
CXXFLAGS += -O2
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
COMMON_DEPS = drmprocessorclientimpl.cpp $(STATIC_DEP)
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
acsmdownloader: drmprocessorclientimpl.cpp acsmdownloader.cpp $(STATIC_DEP)
|
acsmdownloader: acsmdownloader.cpp $(COMMON_DEPS)
|
||||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
adept_activate: drmprocessorclientimpl.cpp adept_activate.cpp $(STATIC_DEP)
|
adept_activate: adept_activate.cpp $(COMMON_DEPS)
|
||||||
|
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
adept_remove: adept_remove.cpp $(COMMON_DEPS)
|
||||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -144,8 +144,9 @@ static void usage(const char* cmd)
|
|||||||
{
|
{
|
||||||
std::cout << "Create new device files used by ADEPT DRM" << std::endl;
|
std::cout << "Create new device files used by ADEPT DRM" << std::endl;
|
||||||
|
|
||||||
std::cout << "Usage: " << cmd << " (-u|--username) username [(-p|--password) password] [(-O|--output-dir) dir] [(-r|--random-serial)] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
|
std::cout << "Usage: " << cmd << " (-a|--anonymous) | ( (-u|--username) username [(-p|--password) password] ) [(-O|--output-dir) dir] [(-r|--random-serial)] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
|
||||||
|
|
||||||
|
std::cout << " " << "-a|--anonymous" << "\t" << "Anonymous account, no need for username/password (Use it only with a DRM removal software)" << std::endl;
|
||||||
std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl;
|
std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl;
|
||||||
std::cout << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl;
|
std::cout << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl;
|
||||||
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./.adept). This directory must not already exists" << std::endl;
|
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./.adept). This directory must not already exists" << std::endl;
|
||||||
@@ -174,10 +175,12 @@ int main(int argc, char** argv)
|
|||||||
int c, ret = -1;
|
int c, ret = -1;
|
||||||
const char* _outputDir = outputDir;
|
const char* _outputDir = outputDir;
|
||||||
int verbose = gourou::DRMProcessor::getLogLevel();
|
int verbose = gourou::DRMProcessor::getLogLevel();
|
||||||
|
bool anonymous = false;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
|
{"anonymous", no_argument , 0, 'a' },
|
||||||
{"username", required_argument, 0, 'u' },
|
{"username", required_argument, 0, 'u' },
|
||||||
{"password", required_argument, 0, 'p' },
|
{"password", required_argument, 0, 'p' },
|
||||||
{"output-dir", required_argument, 0, 'O' },
|
{"output-dir", required_argument, 0, 'O' },
|
||||||
@@ -189,12 +192,15 @@ int main(int argc, char** argv)
|
|||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "u:p:O:H:rvVh",
|
c = getopt_long(argc, argv, "au:p:O:H:rvVh",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case 'a':
|
||||||
|
anonymous = true;
|
||||||
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
username = optarg;
|
username = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -227,15 +233,22 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
gourou::DRMProcessor::setLogLevel(verbose);
|
gourou::DRMProcessor::setLogLevel(verbose);
|
||||||
|
|
||||||
if (!username)
|
if ((!username && !anonymous) ||
|
||||||
|
(username && anonymous))
|
||||||
{
|
{
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (anonymous)
|
||||||
|
{
|
||||||
|
username = "anonymous";
|
||||||
|
password = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (!_outputDir || _outputDir[0] == 0)
|
if (!_outputDir || _outputDir[0] == 0)
|
||||||
{
|
{
|
||||||
outputDir = abspath(DEFAULT_ADEPT_DIR);
|
outputDir = strdup(abspath(DEFAULT_ADEPT_DIR));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -245,29 +258,53 @@ int main(int argc, char** argv)
|
|||||||
QFile file(_outputDir);
|
QFile file(_outputDir);
|
||||||
// realpath doesn't works if file/dir doesn't exists
|
// realpath doesn't works if file/dir doesn't exists
|
||||||
if (file.exists())
|
if (file.exists())
|
||||||
outputDir = realpath(_outputDir, 0);
|
outputDir = strdup(realpath(_outputDir, 0));
|
||||||
else
|
else
|
||||||
outputDir = abspath(_outputDir);
|
outputDir = strdup(abspath(_outputDir));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
outputDir = strdup(_outputDir);
|
outputDir = strdup(_outputDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
QFile file(outputDir);
|
||||||
|
if (file.exists())
|
||||||
|
{
|
||||||
|
int key;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::cout << "!! Warning !! : " << outputDir << " already exists." << std::endl;
|
||||||
|
std::cout << "All your data will be overwrite. Would you like to continue ? [y/N] " << std::flush ;
|
||||||
|
key = getchar();
|
||||||
|
if (key == 'n' || key == 'N' || key == '\n' || key == '\r')
|
||||||
|
goto end;
|
||||||
|
if (key == 'y' || key == 'Y')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean STDIN buf
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
|
||||||
|
|
||||||
ADEPTActivate activate(&app);
|
ADEPTActivate activate(&app);
|
||||||
QThreadPool::globalInstance()->start(&activate);
|
QThreadPool::globalInstance()->start(&activate);
|
||||||
|
|
||||||
ret = app.exec();
|
ret = app.exec();
|
||||||
|
|
||||||
|
end:
|
||||||
|
|
||||||
free((void*)outputDir);
|
free((void*)outputDir);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
353
utils/adept_remove.cpp
Normal file
353
utils/adept_remove.cpp
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2021, Grégory Soutadé
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QRunnable>
|
||||||
|
#include <QThreadPool>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include <libgourou.h>
|
||||||
|
#include <libgourou_common.h>
|
||||||
|
#include "drmprocessorclientimpl.h"
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
|
||||||
|
|
||||||
|
static const char* deviceFile = "device.xml";
|
||||||
|
static const char* activationFile = "activation.xml";
|
||||||
|
static const char* devicekeyFile = "devicesalt";
|
||||||
|
static const char* inputFile = 0;
|
||||||
|
static const char* outputFile = 0;
|
||||||
|
static const char* outputDir = 0;
|
||||||
|
static const char* defaultDirs[] = {
|
||||||
|
".adept/",
|
||||||
|
"./adobe-digital-editions/",
|
||||||
|
"./.adobe-digital-editions/"
|
||||||
|
};
|
||||||
|
static char* encryptionKeyUser = 0;
|
||||||
|
static unsigned char* encryptionKey = 0;
|
||||||
|
static unsigned encryptionKeySize = 0;
|
||||||
|
|
||||||
|
static inline unsigned char htoi(unsigned char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
c -= '0';
|
||||||
|
else if (c >= 'a' && c <= 'f')
|
||||||
|
c -= 'a' - 10;
|
||||||
|
else if (c >= 'A' && c <= 'F')
|
||||||
|
c -= 'A' - 10;
|
||||||
|
else
|
||||||
|
EXCEPTION(gourou::USER_INVALID_INPUT, "Invalid character " << c << " in encryption key");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool endsWith(const std::string& s, const std::string& suffix)
|
||||||
|
{
|
||||||
|
return s.rfind(suffix) == std::abs((int)(s.size()-suffix.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ADEPTRemove: public QRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ADEPTRemove(QCoreApplication* app):
|
||||||
|
app(app)
|
||||||
|
{
|
||||||
|
setAutoDelete(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gourou::DRMProcessor::ITEM_TYPE type;
|
||||||
|
DRMProcessorClientImpl client;
|
||||||
|
gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
|
||||||
|
|
||||||
|
std::string filename;
|
||||||
|
if (!outputFile)
|
||||||
|
filename = std::string(inputFile);
|
||||||
|
else
|
||||||
|
filename = outputFile;
|
||||||
|
|
||||||
|
if (outputDir)
|
||||||
|
{
|
||||||
|
QDir dir(outputDir);
|
||||||
|
if (!dir.exists(outputDir))
|
||||||
|
dir.mkpath(outputDir);
|
||||||
|
|
||||||
|
filename = std::string(outputDir) + "/" + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endsWith(filename, ".epub"))
|
||||||
|
type = gourou::DRMProcessor::ITEM_TYPE::EPUB;
|
||||||
|
else if (endsWith(filename, ".pdf"))
|
||||||
|
type = gourou::DRMProcessor::ITEM_TYPE::PDF;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXCEPTION(gourou::DRM_FORMAT_NOT_SUPPORTED, "Unsupported file format of " << filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputFile != filename)
|
||||||
|
{
|
||||||
|
QFile::remove(filename.c_str());
|
||||||
|
if (!QFile::copy(inputFile, filename.c_str()))
|
||||||
|
{
|
||||||
|
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << inputFile << " into " << filename);
|
||||||
|
}
|
||||||
|
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||||
|
std::cout << "DRM removed into new file " << filename << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use temp file for PDF
|
||||||
|
if (type == gourou::DRMProcessor::ITEM_TYPE::PDF)
|
||||||
|
{
|
||||||
|
QTemporaryFile tempFile;
|
||||||
|
tempFile.open();
|
||||||
|
tempFile.setAutoRemove(false); // In case of failure
|
||||||
|
processor.removeDRM(inputFile, tempFile.fileName().toStdString(), type, encryptionKey, encryptionKeySize);
|
||||||
|
/* Original file must be removed before doing a copy... */
|
||||||
|
QFile origFile(inputFile);
|
||||||
|
origFile.remove();
|
||||||
|
if (!QFile::copy(tempFile.fileName(), filename.c_str()))
|
||||||
|
{
|
||||||
|
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile.fileName().toStdString() << " into " << filename);
|
||||||
|
}
|
||||||
|
tempFile.setAutoRemove(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||||
|
std::cout << "DRM removed from " << filename << std::endl;
|
||||||
|
}
|
||||||
|
} catch(std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << std::endl;
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->app->exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QCoreApplication* app;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* findFile(const char* filename, bool inDefaultDirs=true)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
|
||||||
|
if (file.exists())
|
||||||
|
return strdup(filename);
|
||||||
|
|
||||||
|
if (!inDefaultDirs) return 0;
|
||||||
|
|
||||||
|
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
|
||||||
|
{
|
||||||
|
QString path = QString(defaultDirs[i]) + QString(filename);
|
||||||
|
file.setFileName(path);
|
||||||
|
if (file.exists())
|
||||||
|
return strdup(path.toStdString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void version(void)
|
||||||
|
{
|
||||||
|
std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(const char* cmd)
|
||||||
|
{
|
||||||
|
std::cout << "Remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-v|--verbose)] [(-h|--help)] (-f|--input-file) file(.epub|pdf)" << std::endl << std::endl;
|
||||||
|
|
||||||
|
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
|
||||||
|
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
|
||||||
|
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;
|
||||||
|
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
|
||||||
|
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl;
|
||||||
|
std::cout << " " << "-f|--input-file" << "\t" << "EPUB/PDF file to process" << std::endl;
|
||||||
|
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
||||||
|
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
||||||
|
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
|
||||||
|
std::cout << " * Current directory" << std::endl;
|
||||||
|
std::cout << " * .adept" << std::endl;
|
||||||
|
std::cout << " * adobe-digital-editions directory" << std::endl;
|
||||||
|
std::cout << " * .adobe-digital-editions directory" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int c, ret = -1;
|
||||||
|
|
||||||
|
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
|
||||||
|
int verbose = gourou::DRMProcessor::getLogLevel();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int option_index = 0;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"device-file", required_argument, 0, 'd' },
|
||||||
|
{"activation-file", required_argument, 0, 'a' },
|
||||||
|
{"device-key-file", required_argument, 0, 'k' },
|
||||||
|
{"output-dir", required_argument, 0, 'O' },
|
||||||
|
{"output-file", required_argument, 0, 'o' },
|
||||||
|
{"input-file", required_argument, 0, 'f' },
|
||||||
|
{"encryption-key", required_argument, 0, 'K' }, // Private option
|
||||||
|
{"verbose", no_argument, 0, 'v' },
|
||||||
|
{"version", no_argument, 0, 'V' },
|
||||||
|
{"help", no_argument, 0, 'h' },
|
||||||
|
{0, 0, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "d:a:k:O:o:f:K:vVh",
|
||||||
|
long_options, &option_index);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
deviceFile = optarg;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
activationFile = optarg;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
devicekeyFile = optarg;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
inputFile = optarg;
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
outputDir = optarg;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
outputFile = optarg;
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
encryptionKeyUser = optarg;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose++;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
version();
|
||||||
|
return 0;
|
||||||
|
case 'h':
|
||||||
|
usage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gourou::DRMProcessor::setLogLevel(verbose);
|
||||||
|
|
||||||
|
if (!inputFile || (outputDir && !outputDir[0]) ||
|
||||||
|
(outputFile && !outputFile[0]))
|
||||||
|
{
|
||||||
|
usage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
ADEPTRemove remover(&app);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
bool hasErrors = false;
|
||||||
|
const char* orig;
|
||||||
|
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
||||||
|
{
|
||||||
|
orig = *files[i];
|
||||||
|
*files[i] = findFile(*files[i]);
|
||||||
|
if (!*files[i])
|
||||||
|
{
|
||||||
|
std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl;
|
||||||
|
ret = -1;
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyUser)
|
||||||
|
{
|
||||||
|
int size = std::string(encryptionKeyUser).size();
|
||||||
|
if ((size % 2))
|
||||||
|
{
|
||||||
|
std::cout << "Error : Encryption key must be odd length" << std::endl;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyUser[0] == '0' && encryptionKeyUser[1] == 'x')
|
||||||
|
{
|
||||||
|
encryptionKeyUser += 2;
|
||||||
|
size -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKey = new unsigned char[size/2];
|
||||||
|
|
||||||
|
for(i=0; i<size; i+=2)
|
||||||
|
{
|
||||||
|
encryptionKey[i/2] = htoi(encryptionKeyUser[i]) << 4;
|
||||||
|
encryptionKey[i/2] |= htoi(encryptionKeyUser[i+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKeySize = size/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasErrors)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
QThreadPool::globalInstance()->start(&remover);
|
||||||
|
|
||||||
|
ret = app.exec();
|
||||||
|
|
||||||
|
end:
|
||||||
|
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
||||||
|
{
|
||||||
|
if (*files[i])
|
||||||
|
free((void*)*files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKey)
|
||||||
|
free(encryptionKey);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include <zip.h>
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#include <zip.h>
|
||||||
|
|
||||||
#include <libgourou_common.h>
|
#include <libgourou_common.h>
|
||||||
#include <libgourou_log.h>
|
#include <libgourou_log.h>
|
||||||
@@ -47,23 +47,28 @@
|
|||||||
/* Digest interface */
|
/* Digest interface */
|
||||||
void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
|
void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
|
||||||
{
|
{
|
||||||
EVP_MD_CTX *sha_ctx = EVP_MD_CTX_new();
|
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||||
const EVP_MD* md = EVP_get_digestbyname(digestName.c_str());
|
const EVP_MD* md = EVP_get_digestbyname(digestName.c_str());
|
||||||
EVP_DigestInit(sha_ctx, md);
|
|
||||||
|
|
||||||
return sha_ctx;
|
if (EVP_DigestInit(md_ctx, md) != 1)
|
||||||
|
{
|
||||||
|
EVP_MD_CTX_free(md_ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return md_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length)
|
int DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length)
|
||||||
{
|
{
|
||||||
return EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length);
|
return (EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length)) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut)
|
int DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut)
|
||||||
{
|
{
|
||||||
int res = EVP_DigestFinal((EVP_MD_CTX *)handler, digestOut, NULL);
|
int res = EVP_DigestFinal((EVP_MD_CTX *)handler, digestOut, NULL);
|
||||||
EVP_MD_CTX_free((EVP_MD_CTX *)handler);
|
EVP_MD_CTX_free((EVP_MD_CTX *)handler);
|
||||||
return res;
|
return (res == 1) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut)
|
int DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut)
|
||||||
@@ -83,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()));
|
||||||
@@ -107,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)
|
||||||
@@ -131,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());
|
||||||
@@ -163,8 +188,39 @@ void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsi
|
|||||||
|
|
||||||
if (gourou::logLevel >= gourou::DEBUG)
|
if (gourou::logLevel >= gourou::DEBUG)
|
||||||
{
|
{
|
||||||
printf("Sig : ");
|
printf("Encrypted : ");
|
||||||
for(int i=0; i<(int)sizeof(res); i++)
|
for(int i=0; i<ret; i++)
|
||||||
|
printf("%02x ", res[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessorClientImpl::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)
|
||||||
|
{
|
||||||
|
BIO* mem=BIO_new_mem_buf(RSAKey, RSAKeyLength);
|
||||||
|
PKCS8_PRIV_KEY_INFO* p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(mem, NULL);
|
||||||
|
|
||||||
|
if (!p8inf)
|
||||||
|
EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
|
||||||
|
EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);
|
||||||
|
RSA * rsa;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||||
|
|
||||||
|
ret = RSA_private_decrypt(dataLength, data, res, rsa, RSA_NO_PADDING);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
|
||||||
|
if (gourou::logLevel >= gourou::DEBUG)
|
||||||
|
{
|
||||||
|
printf("Decrypted : ");
|
||||||
|
for(int i=0; i<ret; i++)
|
||||||
printf("%02x ", res[i]);
|
printf("%02x ", res[i]);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
@@ -252,33 +308,35 @@ void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, uns
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Crypto interface */
|
/* Crypto interface */
|
||||||
void DRMProcessorClientImpl::AESEncrypt(CHAINING_MODE chaining,
|
void DRMProcessorClientImpl::Encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength,
|
const unsigned char* iv, unsigned int ivLength,
|
||||||
const unsigned char* dataIn, unsigned int dataInLength,
|
const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||||
{
|
{
|
||||||
void* handler = AESEncryptInit(chaining, key, keyLength, iv, ivLength);
|
void* handler = EncryptInit(algo, chaining, key, keyLength, iv, ivLength);
|
||||||
AESEncryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
EncryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
||||||
AESEncryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
EncryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* DRMProcessorClientImpl::AESEncryptInit(CHAINING_MODE chaining,
|
void* DRMProcessorClientImpl::EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength)
|
const unsigned char* iv, unsigned int ivLength)
|
||||||
{
|
{
|
||||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
|
|
||||||
|
if (algo == ALGO_AES)
|
||||||
|
{
|
||||||
switch(keyLength)
|
switch(keyLength)
|
||||||
{
|
{
|
||||||
case 16:
|
case 16:
|
||||||
switch(chaining)
|
switch(chaining)
|
||||||
{
|
{
|
||||||
case CHAIN_ECB:
|
case CHAIN_ECB:
|
||||||
EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
|
EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, iv);
|
||||||
break;
|
break;
|
||||||
case CHAIN_CBC:
|
case CHAIN_CBC:
|
||||||
EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
|
EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
||||||
@@ -288,26 +346,37 @@ void* DRMProcessorClientImpl::AESEncryptInit(CHAINING_MODE chaining,
|
|||||||
EVP_CIPHER_CTX_free(ctx);
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (algo == ALGO_RC4)
|
||||||
|
{
|
||||||
|
if (keyLength != 16)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
||||||
|
}
|
||||||
|
EVP_DecryptInit(ctx, EVP_rc4(), key, iv);
|
||||||
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* DRMProcessorClientImpl::AESDecryptInit(CHAINING_MODE chaining,
|
void* DRMProcessorClientImpl::DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength)
|
const unsigned char* iv, unsigned int ivLength)
|
||||||
{
|
{
|
||||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
|
|
||||||
|
if (algo == ALGO_AES)
|
||||||
|
{
|
||||||
switch(keyLength)
|
switch(keyLength)
|
||||||
{
|
{
|
||||||
case 16:
|
case 16:
|
||||||
switch(chaining)
|
switch(chaining)
|
||||||
{
|
{
|
||||||
case CHAIN_ECB:
|
case CHAIN_ECB:
|
||||||
EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
|
EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, iv);
|
||||||
break;
|
break;
|
||||||
case CHAIN_CBC:
|
case CHAIN_CBC:
|
||||||
EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
|
EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
||||||
@@ -317,17 +386,27 @@ void* DRMProcessorClientImpl::AESDecryptInit(CHAINING_MODE chaining,
|
|||||||
EVP_CIPHER_CTX_free(ctx);
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (algo == ALGO_RC4)
|
||||||
|
{
|
||||||
|
if (keyLength != 16)
|
||||||
|
{
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
||||||
|
}
|
||||||
|
EVP_DecryptInit(ctx, EVP_rc4(), key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::AESEncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
void DRMProcessorClientImpl::EncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||||
{
|
{
|
||||||
EVP_EncryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
EVP_EncryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::AESEncryptFinalize(void* handler,
|
void DRMProcessorClientImpl::EncryptFinalize(void* handler,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
@@ -336,24 +415,24 @@ void DRMProcessorClientImpl::AESEncryptFinalize(void* handler,
|
|||||||
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);
|
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::AESDecrypt(CHAINING_MODE chaining,
|
void DRMProcessorClientImpl::Decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength,
|
const unsigned char* iv, unsigned int ivLength,
|
||||||
const unsigned char* dataIn, unsigned int dataInLength,
|
const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||||
{
|
{
|
||||||
void* handler = AESDecryptInit(chaining, key, keyLength, iv, ivLength);
|
void* handler = DecryptInit(algo, chaining, key, keyLength, iv, ivLength);
|
||||||
AESDecryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
DecryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
||||||
AESDecryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
DecryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::AESDecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
void DRMProcessorClientImpl::DecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||||
{
|
{
|
||||||
EVP_DecryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
EVP_DecryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::AESDecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength)
|
void DRMProcessorClientImpl::DecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
|
EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
|
||||||
@@ -371,35 +450,39 @@ void* DRMProcessorClientImpl::zipOpen(const std::string& path)
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DRMProcessorClientImpl::zipReadFile(void* handler, const std::string& path)
|
void DRMProcessorClientImpl::zipReadFile(void* handler, const std::string& path, gourou::ByteArray& result, bool decompress)
|
||||||
{
|
{
|
||||||
std::string res;
|
std::string res;
|
||||||
unsigned char* buffer;
|
|
||||||
zip_stat_t sb;
|
zip_stat_t sb;
|
||||||
|
|
||||||
if (zip_stat((zip_t *)handler, path.c_str(), 0, &sb) < 0)
|
if (zip_stat((zip_t *)handler, path.c_str(), 0, &sb) < 0)
|
||||||
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error, no file " << path << ", " << zip_strerror((zip_t *)handler));
|
||||||
|
|
||||||
if (!(sb.valid & (ZIP_STAT_INDEX|ZIP_STAT_SIZE)))
|
if (!(sb.valid & (ZIP_STAT_INDEX|ZIP_STAT_SIZE)))
|
||||||
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Required fields missing");
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Required fields missing");
|
||||||
|
|
||||||
buffer = new unsigned char[sb.size];
|
result.resize(sb.size);
|
||||||
|
|
||||||
zip_file_t *f = zip_fopen_index((zip_t *)handler, sb.index, ZIP_FL_COMPRESSED);
|
zip_file_t *f = zip_fopen_index((zip_t *)handler, sb.index, (decompress)?0:ZIP_FL_COMPRESSED);
|
||||||
|
zip_fread(f, result.data(), sb.size);
|
||||||
zip_fread(f, buffer, sb.size);
|
|
||||||
zip_fclose(f);
|
zip_fclose(f);
|
||||||
|
|
||||||
res = std::string((char*)buffer, sb.size);
|
|
||||||
delete[] buffer;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::zipWriteFile(void* handler, const std::string& path, const std::string& content)
|
void DRMProcessorClientImpl::zipWriteFile(void* handler, const std::string& path, gourou::ByteArray& content)
|
||||||
{
|
{
|
||||||
zip_source_t* s = zip_source_buffer((zip_t*)handler, content.c_str(), content.length(), 0);
|
zip_int64_t ret;
|
||||||
if (zip_file_add((zip_t*)handler, path.c_str(), s, ZIP_FL_OVERWRITE|ZIP_FL_ENC_UTF_8) < 0)
|
|
||||||
|
zip_source_t* s = zip_source_buffer((zip_t*)handler, content.takeShadowData(), content.length(), 1);
|
||||||
|
|
||||||
|
zip_int64_t idx = zip_name_locate((zip_t*)handler, path.c_str(), 0);
|
||||||
|
|
||||||
|
// File doesn't exists
|
||||||
|
if (idx == -1)
|
||||||
|
ret = zip_file_add((zip_t*)handler, path.c_str(), s, 0);
|
||||||
|
else
|
||||||
|
ret = zip_file_replace((zip_t*)handler, idx, s, ZIP_FL_OVERWRITE);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
zip_source_free(s);
|
zip_source_free(s);
|
||||||
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));
|
||||||
@@ -422,7 +505,7 @@ void DRMProcessorClientImpl::zipClose(void* handler)
|
|||||||
zip_close((zip_t*)handler);
|
zip_close((zip_t*)handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::inflate(std::string data, gourou::ByteArray& result,
|
void DRMProcessorClientImpl::inflate(gourou::ByteArray& data, gourou::ByteArray& result,
|
||||||
int wbits)
|
int wbits)
|
||||||
{
|
{
|
||||||
unsigned int dataSize = data.size()*2;
|
unsigned int dataSize = data.size()*2;
|
||||||
@@ -435,31 +518,41 @@ void DRMProcessorClientImpl::inflate(std::string data, gourou::ByteArray& result
|
|||||||
infstream.opaque = Z_NULL;
|
infstream.opaque = Z_NULL;
|
||||||
|
|
||||||
infstream.avail_in = (uInt)data.size();
|
infstream.avail_in = (uInt)data.size();
|
||||||
infstream.next_in = (Bytef *)data.c_str(); // input char array
|
infstream.next_in = (Bytef *)data.data(); // input char array
|
||||||
infstream.avail_out = (uInt)dataSize; // size of output
|
infstream.avail_out = (uInt)dataSize; // size of output
|
||||||
infstream.next_out = (Bytef *)buffer; // output char array
|
infstream.next_out = (Bytef *)buffer; // output char array
|
||||||
|
|
||||||
int ret = inflateInit2(&infstream, wbits);
|
int ret = inflateInit2(&infstream, wbits);
|
||||||
|
|
||||||
ret = ::inflate(&infstream, Z_SYNC_FLUSH);
|
if (ret != Z_OK)
|
||||||
while (ret == Z_OK || ret == Z_STREAM_END)
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Inflate error, code " << zError(ret) << ", msg " << infstream.msg);
|
||||||
|
|
||||||
|
ret = ::inflate(&infstream, Z_FINISH);
|
||||||
|
while (ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR)
|
||||||
{
|
{
|
||||||
|
// Real error
|
||||||
|
if (ret == Z_BUF_ERROR && infstream.avail_out == (uInt)dataSize)
|
||||||
|
break;
|
||||||
|
|
||||||
result.append(buffer, dataSize-infstream.avail_out);
|
result.append(buffer, dataSize-infstream.avail_out);
|
||||||
if (ret == Z_STREAM_END) break;
|
|
||||||
|
if ((ret == Z_OK && infstream.avail_out != 0) || ret == Z_STREAM_END)
|
||||||
|
break;
|
||||||
infstream.avail_out = (uInt)dataSize; // size of output
|
infstream.avail_out = (uInt)dataSize; // size of output
|
||||||
infstream.next_out = (Bytef *)buffer; // output char array
|
infstream.next_out = (Bytef *)buffer; // output char array
|
||||||
ret = ::inflate(&infstream, Z_SYNC_FLUSH);
|
ret = ::inflate(&infstream, Z_FINISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
inflateEnd(&infstream);
|
if (ret == Z_STREAM_END)
|
||||||
|
ret = inflateEnd(&infstream);
|
||||||
|
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
|
||||||
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR)
|
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||||
EXCEPTION(gourou::CLIENT_ZIP_ERROR, zError(ret));
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Inflate error, code " << zError(ret) << ", msg " << infstream.msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::deflate(std::string data, gourou::ByteArray& result,
|
void DRMProcessorClientImpl::deflate(gourou::ByteArray& data, gourou::ByteArray& result,
|
||||||
int wbits, int compressionLevel)
|
int wbits, int compressionLevel)
|
||||||
{
|
{
|
||||||
unsigned int dataSize = data.size();
|
unsigned int dataSize = data.size();
|
||||||
@@ -471,28 +564,33 @@ void DRMProcessorClientImpl::deflate(std::string data, gourou::ByteArray& result
|
|||||||
defstream.zfree = Z_NULL;
|
defstream.zfree = Z_NULL;
|
||||||
defstream.opaque = Z_NULL;
|
defstream.opaque = Z_NULL;
|
||||||
|
|
||||||
defstream.avail_in = (uInt)data.size();
|
defstream.avail_in = (uInt)dataSize;
|
||||||
defstream.next_in = (Bytef *)data.c_str(); // input char array
|
defstream.next_in = (Bytef *)data.data(); // input char array
|
||||||
defstream.avail_out = (uInt)dataSize; // size of output
|
defstream.avail_out = (uInt)dataSize; // size of output
|
||||||
defstream.next_out = (Bytef *)buffer; // output char array
|
defstream.next_out = (Bytef *)buffer; // output char array
|
||||||
|
|
||||||
int ret = deflateInit2(&defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits,
|
int ret = deflateInit2(&defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits,
|
||||||
compressionLevel, Z_DEFAULT_STRATEGY);
|
compressionLevel, Z_DEFAULT_STRATEGY);
|
||||||
|
|
||||||
ret = ::deflate(&defstream, Z_SYNC_FLUSH);
|
if (ret != Z_OK)
|
||||||
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Deflate error, code " << zError(ret) << ", msg " << defstream.msg);
|
||||||
|
|
||||||
|
ret = ::deflate(&defstream, Z_FINISH);
|
||||||
while (ret == Z_OK || ret == Z_STREAM_END)
|
while (ret == Z_OK || ret == Z_STREAM_END)
|
||||||
{
|
{
|
||||||
result.append(buffer, dataSize-defstream.avail_out);
|
result.append(buffer, dataSize-defstream.avail_out);
|
||||||
if (ret == Z_STREAM_END) break;
|
if ((ret == Z_OK && defstream.avail_out != 0) || ret == Z_STREAM_END)
|
||||||
|
break;
|
||||||
defstream.avail_out = (uInt)dataSize; // size of output
|
defstream.avail_out = (uInt)dataSize; // size of output
|
||||||
defstream.next_out = (Bytef *)buffer; // output char array
|
defstream.next_out = (Bytef *)buffer; // output char array
|
||||||
ret = ::deflate(&defstream, Z_SYNC_FLUSH);
|
ret = ::deflate(&defstream, Z_FINISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
deflateEnd(&defstream);
|
if (ret == Z_STREAM_END)
|
||||||
|
ret = deflateEnd(&defstream);
|
||||||
|
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
|
||||||
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR)
|
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||||
EXCEPTION(gourou::CLIENT_ZIP_ERROR, zError(ret));
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Deflate error, code " << zError(ret) << ", msg " << defstream.msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ class DRMProcessorClientImpl : public gourou::DRMProcessorClient
|
|||||||
const unsigned char* data, unsigned dataLength,
|
const unsigned char* data, unsigned dataLength,
|
||||||
unsigned char* res);
|
unsigned char* res);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
virtual void RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
virtual void RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
const RSA_KEY_TYPE keyType,
|
const RSA_KEY_TYPE keyType,
|
||||||
const unsigned char* data, unsigned dataLength,
|
const unsigned char* data, unsigned dataLength,
|
||||||
@@ -68,49 +73,50 @@ class DRMProcessorClientImpl : public gourou::DRMProcessorClient
|
|||||||
unsigned char** certOut, unsigned int* certOutLength);
|
unsigned char** certOut, unsigned int* certOutLength);
|
||||||
|
|
||||||
/* Crypto interface */
|
/* Crypto interface */
|
||||||
virtual void AESEncrypt(CHAINING_MODE chaining,
|
virtual void Encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength,
|
const unsigned char* iv, unsigned int ivLength,
|
||||||
const unsigned char* dataIn, unsigned int dataInLength,
|
const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength);
|
unsigned char* dataOut, unsigned int* dataOutLength);
|
||||||
|
|
||||||
virtual void* AESEncryptInit(CHAINING_MODE chaining,
|
virtual void* EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv=0, unsigned int ivLength=0);
|
const unsigned char* iv=0, unsigned int ivLength=0);
|
||||||
|
|
||||||
|
|
||||||
virtual void AESEncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
virtual void EncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength);
|
unsigned char* dataOut, unsigned int* dataOutLength);
|
||||||
virtual void AESEncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
virtual void EncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
||||||
|
|
||||||
virtual void AESDecrypt(CHAINING_MODE chaining,
|
virtual void Decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv, unsigned int ivLength,
|
const unsigned char* iv, unsigned int ivLength,
|
||||||
const unsigned char* dataIn, unsigned int dataInLength,
|
const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength);
|
unsigned char* dataOut, unsigned int* dataOutLength);
|
||||||
|
|
||||||
virtual void* AESDecryptInit(CHAINING_MODE chaining,
|
virtual void* DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
const unsigned char* iv=0, unsigned int ivLength=0);
|
const unsigned char* iv=0, unsigned int ivLength=0);
|
||||||
|
|
||||||
virtual void AESDecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
virtual void DecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
|
||||||
unsigned char* dataOut, unsigned int* dataOutLength);
|
unsigned char* dataOut, unsigned int* dataOutLength);
|
||||||
virtual void AESDecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
virtual void DecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
||||||
|
|
||||||
/* ZIP Interface */
|
/* ZIP Interface */
|
||||||
virtual void* zipOpen(const std::string& path);
|
virtual void* zipOpen(const std::string& path);
|
||||||
|
|
||||||
virtual std::string zipReadFile(void* handler, const std::string& path);
|
virtual void zipReadFile(void* handler, const std::string& path, gourou::ByteArray& result, bool decompress=true);
|
||||||
|
|
||||||
virtual void zipWriteFile(void* handler, const std::string& path, const std::string& content);
|
virtual void zipWriteFile(void* handler, const std::string& path, gourou::ByteArray& content);
|
||||||
|
|
||||||
virtual void zipDeleteFile(void* handler, const std::string& path);
|
virtual void zipDeleteFile(void* handler, const std::string& path);
|
||||||
|
|
||||||
virtual void zipClose(void* handler);
|
virtual void zipClose(void* handler);
|
||||||
|
|
||||||
virtual void inflate(std::string data, gourou::ByteArray& result, int wbits=-15);
|
virtual void inflate(gourou::ByteArray& data, gourou::ByteArray& result,
|
||||||
|
int wbits=-15);
|
||||||
|
|
||||||
virtual void deflate(std::string data, gourou::ByteArray& result,
|
virtual void deflate(gourou::ByteArray& data, gourou::ByteArray& result,
|
||||||
int wbits=-15, int compressionLevel=8);
|
int wbits=-15, int compressionLevel=8);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user