Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f01bf9e837 | ||
|
|
c57aba8061 | ||
|
|
33ea9e7d65 | ||
|
|
dc15fc7197 | ||
|
|
47a38b1ebc | ||
|
|
d8333531d2 | ||
|
|
cb4a26289e | ||
|
|
fb812964e0 | ||
|
|
8a2b22ca9b | ||
|
|
2ac917619e | ||
|
|
3d9e343734 | ||
|
|
8bc346d139 | ||
|
|
7f5a4900e0 | ||
|
|
d8f882a277 | ||
|
|
bdaceba2e0 | ||
|
|
b5eb0efd31 | ||
|
|
20b4e69c3e | ||
|
|
878e4e7453 | ||
|
|
1eebc88913 | ||
|
|
460f452783 | ||
|
|
8881a58c95 | ||
|
|
0e90b89382 | ||
|
|
7adc6a0fc1 | ||
|
|
429fe34e8e | ||
|
|
49a74cdd6f | ||
|
|
c85db73583 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,5 +4,5 @@ lib
|
|||||||
*.so
|
*.so
|
||||||
*~
|
*~
|
||||||
utils/acsmdownloader
|
utils/acsmdownloader
|
||||||
utils/activate
|
utils/adept_activate
|
||||||
.adept
|
.adept
|
||||||
|
|||||||
34
Makefile
34
Makefile
@@ -2,8 +2,26 @@
|
|||||||
AR ?= $(CROSS)ar
|
AR ?= $(CROSS)ar
|
||||||
CXX ?= $(CROSS)g++
|
CXX ?= $(CROSS)g++
|
||||||
|
|
||||||
CXXFLAGS=-Wall -fPIC -I./include -I./lib -I./lib/pugixml/src/
|
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
|
||||||
LDFLAGS=
|
|
||||||
|
CXXFLAGS=-Wall -fPIC -I./include -I./lib -I./lib/pugixml/src/ -I./lib/updfparser/include
|
||||||
|
LDFLAGS = $(UPDFPARSERLIB)
|
||||||
|
|
||||||
|
BUILD_STATIC ?= 0
|
||||||
|
BUILD_SHARED ?= 1
|
||||||
|
BUILD_UTILS ?= 1
|
||||||
|
|
||||||
|
TARGETS =
|
||||||
|
ifneq ($(BUILD_STATIC), 0)
|
||||||
|
TARGETS += libgourou.a
|
||||||
|
endif
|
||||||
|
ifneq ($(BUILD_SHARED), 0)
|
||||||
|
TARGETS += libgourou.so
|
||||||
|
endif
|
||||||
|
ifneq ($(BUILD_UTILS), 0)
|
||||||
|
TARGETS += build_utils
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifneq ($(DEBUG),)
|
ifneq ($(DEBUG),)
|
||||||
CXXFLAGS += -ggdb -O0
|
CXXFLAGS += -ggdb -O0
|
||||||
@@ -18,12 +36,10 @@ TARGETDIR := bin
|
|||||||
SRCEXT := cpp
|
SRCEXT := cpp
|
||||||
OBJEXT := o
|
OBJEXT := o
|
||||||
|
|
||||||
SOURCES=src/libgourou.cpp src/user.cpp src/device.cpp src/fulfillment_item.cpp src/bytearray.cpp src/pugixml.cpp
|
SOURCES = src/libgourou.cpp src/user.cpp src/device.cpp src/fulfillment_item.cpp src/bytearray.cpp src/pugixml.cpp
|
||||||
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||||
|
|
||||||
.PHONY: utils
|
all: lib obj $(TARGETS)
|
||||||
|
|
||||||
all: lib obj libgourou utils
|
|
||||||
|
|
||||||
lib:
|
lib:
|
||||||
mkdir lib
|
mkdir lib
|
||||||
@@ -38,12 +54,12 @@ $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
|||||||
libgourou: libgourou.a libgourou.so
|
libgourou: libgourou.a libgourou.so
|
||||||
|
|
||||||
libgourou.a: $(OBJECTS)
|
libgourou.a: $(OBJECTS)
|
||||||
$(AR) crs $@ obj/*.o
|
$(AR) crs $@ obj/*.o $(LDFLAGS)
|
||||||
|
|
||||||
libgourou.so: libgourou.a
|
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
||||||
|
|
||||||
utils:
|
build_utils:
|
||||||
make -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS)
|
make -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -1,7 +1,7 @@
|
|||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub files. It overcome the lacks of Adobe support for Linux platforms.
|
libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub/PDF files. It overcome the lacks of Adobe support for Linux platforms.
|
||||||
|
|
||||||
|
|
||||||
Architecture
|
Architecture
|
||||||
@@ -26,6 +26,8 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
------------
|
------------
|
||||||
@@ -47,14 +49,21 @@ Compilation
|
|||||||
|
|
||||||
Use _make_ command
|
Use _make_ command
|
||||||
|
|
||||||
make [CROSS=XXX] [DEBUG=1] [STATIC_UTILS=1]
|
make [CROSS=XXX] [DEBUG=(0*|1)] [STATIC_UTILS=(0*|1)] [BUILD_UTILS=(0|1*)] [BUILD_STATIC=(0*|1)] [BUILD_SHARED=(0|1*)]
|
||||||
|
|
||||||
CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-)
|
CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-)
|
||||||
|
|
||||||
DEBUG can be set to compile in DEBUG mode
|
DEBUG can be set to compile in DEBUG mode
|
||||||
|
|
||||||
|
BUILD_UTILS to build utils or not
|
||||||
|
|
||||||
STATIC_UTILS to build utils with static library (libgourou.a) instead of default dynamic one (libgourou.so)
|
STATIC_UTILS to build utils with static library (libgourou.a) instead of default dynamic one (libgourou.so)
|
||||||
|
|
||||||
|
BUILD_STATIC build libgourou.a if 1, nothing if 0, can be combined with BUILD_SHARED
|
||||||
|
|
||||||
|
BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_STATIC
|
||||||
|
|
||||||
|
* Default value
|
||||||
|
|
||||||
Utils
|
Utils
|
||||||
-----
|
-----
|
||||||
@@ -62,15 +71,20 @@ 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/activate :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
./utils/activate -u <USERNAME>
|
./utils/activate -u <AdobeID USERNAME>
|
||||||
|
|
||||||
Then a _./.adept_ directory is created with all configuration file
|
Then a _./.adept_ directory is created with all configuration file
|
||||||
|
|
||||||
To download an ePub :
|
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 :
|
||||||
|
|
||||||
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
|
./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
---------
|
---------
|
||||||
@@ -85,3 +99,10 @@ License
|
|||||||
libgourou : LGPL v3 or later
|
libgourou : LGPL v3 or later
|
||||||
|
|
||||||
utils : BSD
|
utils : BSD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Special thanks
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* _Jens_ for all test samples and utils testing
|
||||||
|
|||||||
72
README_package.md
Normal file
72
README_package.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
|
./utils/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
|
||||||
|
./utils/acsmdownloader -f <ACSM_FILE>
|
||||||
|
|
||||||
|
To export your private key :
|
||||||
|
|
||||||
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
|
./utils/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
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ namespace gourou
|
|||||||
const unsigned char* getDeviceKey();
|
const unsigned char* getDeviceKey();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get one value of device.xml (deviceClass, deviceSerial, deviceName, deviceType, jobbes, clientOS, clientLocale)
|
* @brief Get one value of device.xml (deviceClass, deviceSerial, deviceName, deviceType, hobbes, clientOS, clientLocale)
|
||||||
*/
|
*/
|
||||||
std::string getProperty(const std::string& property, const std::string& _default=std::string(""));
|
std::string getProperty(const std::string& property, const std::string& _default=std::string(""));
|
||||||
std::string operator[](const std::string& property);
|
std::string operator[](const std::string& property);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#define _DRMPROCESSORCLIENT_H_
|
#define _DRMPROCESSORCLIENT_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <bytearray.h>
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
@@ -96,8 +97,11 @@ namespace gourou
|
|||||||
* @param URL HTTP URL
|
* @param URL HTTP URL
|
||||||
* @param POSTData POST data if needed, if not set, a GET request is done
|
* @param POSTData POST data if needed, if not set, a GET request is done
|
||||||
* @param contentType Optional content type of POST Data
|
* @param contentType Optional content type of POST Data
|
||||||
|
* @param responseHeaders Optional Response headers of HTTP request
|
||||||
|
*
|
||||||
|
* @return data of HTTP response
|
||||||
*/
|
*/
|
||||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string("")) = 0;
|
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RSAInterface
|
class RSAInterface
|
||||||
@@ -173,6 +177,20 @@ namespace gourou
|
|||||||
* @param keyOutLength Length of result
|
* @param keyOutLength Length of result
|
||||||
*/
|
*/
|
||||||
virtual void extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0;
|
virtual void extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract certificate from PKCS12 blob
|
||||||
|
*
|
||||||
|
* @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 certOut Result certificate
|
||||||
|
* @param certOutLength Result certificate length
|
||||||
|
*/
|
||||||
|
virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
|
const RSA_KEY_TYPE keyType, const std::string& password,
|
||||||
|
unsigned char** certOut, unsigned int* certOutLength) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CryptoInterface
|
class CryptoInterface
|
||||||
@@ -341,6 +359,27 @@ namespace gourou
|
|||||||
* @param handler ZIP file handler
|
* @param handler ZIP file handler
|
||||||
*/
|
*/
|
||||||
virtual void zipClose(void* handler) = 0;
|
virtual void zipClose(void* handler) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inflate algorithm
|
||||||
|
*
|
||||||
|
* @param data Data to inflate
|
||||||
|
* @param result Zipped data
|
||||||
|
* @param wbits Window bits value for libz
|
||||||
|
*/
|
||||||
|
virtual void inflate(std::string data, gourou::ByteArray& result,
|
||||||
|
int wbits=-15) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deflate algorithm
|
||||||
|
*
|
||||||
|
* @param data Data to deflate
|
||||||
|
* @param result Unzipped data
|
||||||
|
* @param wbits Window bits value for libz
|
||||||
|
* @param compressionLevel Compression level for libz
|
||||||
|
*/
|
||||||
|
virtual void deflate(std::string data, gourou::ByteArray& result,
|
||||||
|
int wbits=-15, int compressionLevel=8) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DRMProcessorClient: public DigestInterface, public RandomInterface, public HTTPInterface, \
|
class DRMProcessorClient: public DigestInterface, public RandomInterface, public HTTPInterface, \
|
||||||
|
|||||||
@@ -53,10 +53,16 @@ namespace gourou
|
|||||||
*/
|
*/
|
||||||
std::string getDownloadURL();
|
std::string getDownloadURL();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return resource value
|
||||||
|
*/
|
||||||
|
std::string getResource();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pugi::xml_node metadatas;
|
pugi::xml_node metadatas;
|
||||||
pugi::xml_document rights;
|
pugi::xml_document rights;
|
||||||
std::string downloadURL;
|
std::string downloadURL;
|
||||||
|
std::string resource;
|
||||||
|
|
||||||
void buildRights(const pugi::xml_node& licenseToken, User* user);
|
void buildRights(const pugi::xml_node& licenseToken, User* user);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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.1"
|
#define LIBGOUROU_VERSION "0.4.1"
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
@@ -53,6 +53,7 @@ namespace gourou
|
|||||||
|
|
||||||
static const std::string VERSION;
|
static const std::string VERSION;
|
||||||
|
|
||||||
|
enum ITEM_TYPE { EPUB=0, PDF };
|
||||||
/**
|
/**
|
||||||
* @brief Main constructor. To be used once all is configured (user has signedIn, device is activated)
|
* @brief Main constructor. To be used once all is configured (user has signedIn, device is activated)
|
||||||
*
|
*
|
||||||
@@ -80,8 +81,10 @@ namespace gourou
|
|||||||
*
|
*
|
||||||
* @param item Item from fulfill() method
|
* @param item Item from fulfill() method
|
||||||
* @param path Output file path
|
* @param path Output file path
|
||||||
|
*
|
||||||
|
* @return Type of downloaded item
|
||||||
*/
|
*/
|
||||||
void download(FulfillmentItem* item, std::string path);
|
ITEM_TYPE download(FulfillmentItem* item, std::string path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief SignIn into ACS Server (required to activate device)
|
* @brief SignIn into ACS Server (required to activate device)
|
||||||
@@ -130,8 +133,11 @@ namespace gourou
|
|||||||
* @param URL HTTP URL
|
* @param URL HTTP URL
|
||||||
* @param POSTData POST data if needed, if not set, a GET request is done
|
* @param POSTData POST data if needed, if not set, a GET request is done
|
||||||
* @param contentType Optional content type of POST Data
|
* @param contentType Optional content type of POST Data
|
||||||
|
* @param responseHeaders Optional Response headers of HTTP request
|
||||||
|
*
|
||||||
|
* @return data of HTTP response
|
||||||
*/
|
*/
|
||||||
ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0);
|
ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map<std::string, std::string>* responseHeaders=0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send HTTP POST request to URL with document as POSTData
|
* @brief Send HTTP POST request to URL with document as POSTData
|
||||||
@@ -158,6 +164,8 @@ namespace gourou
|
|||||||
*/
|
*/
|
||||||
std::string serializeRSAPrivateKey(void* rsa);
|
std::string serializeRSAPrivateKey(void* rsa);
|
||||||
|
|
||||||
|
void exportPrivateLicenseKey(std::string path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get current user
|
* @brief Get current user
|
||||||
*/
|
*/
|
||||||
@@ -184,10 +192,18 @@ namespace gourou
|
|||||||
void pushTag(void* sha_ctx, uint8_t tag);
|
void pushTag(void* sha_ctx, uint8_t tag);
|
||||||
void hashNode(const pugi::xml_node& root, void *sha_ctx, std::map<std::string,std::string> nsHash);
|
void hashNode(const pugi::xml_node& root, void *sha_ctx, std::map<std::string,std::string> nsHash);
|
||||||
void hashNode(const pugi::xml_node& root, unsigned char* sha_out);
|
void hashNode(const pugi::xml_node& root, unsigned char* sha_out);
|
||||||
|
std::string signNode(const pugi::xml_node& rootNode);
|
||||||
|
void addNonce(pugi::xml_node& root);
|
||||||
|
void buildAuthRequest(pugi::xml_document& authReq);
|
||||||
|
void buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL);
|
||||||
|
void doOperatorAuth(std::string operatorURL);
|
||||||
|
void operatorAuth(std::string operatorURL);
|
||||||
void buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq);
|
void buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq);
|
||||||
void buildActivateReq(pugi::xml_document& activateReq);
|
void buildActivateReq(pugi::xml_document& activateReq);
|
||||||
ByteArray sendFulfillRequest(const pugi::xml_document& document, const std::string& url);
|
ByteArray sendFulfillRequest(const pugi::xml_document& document, const std::string& url);
|
||||||
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,
|
||||||
|
const std::string& operatorURL);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ namespace gourou
|
|||||||
|
|
||||||
enum DOWNLOAD_ERROR {
|
enum DOWNLOAD_ERROR {
|
||||||
DW_NO_ITEM = 0x1200,
|
DW_NO_ITEM = 0x1200,
|
||||||
|
DW_NO_EBX_HANDLER,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SIGNIN_ERROR {
|
enum SIGNIN_ERROR {
|
||||||
@@ -107,8 +108,8 @@ namespace gourou
|
|||||||
CLIENT_BAD_KEY_SIZE,
|
CLIENT_BAD_KEY_SIZE,
|
||||||
CLIENT_BAD_ZIP_FILE,
|
CLIENT_BAD_ZIP_FILE,
|
||||||
CLIENT_ZIP_ERROR,
|
CLIENT_ZIP_ERROR,
|
||||||
CLIENT_GENERIC_EXCEPTION
|
CLIENT_GENERIC_EXCEPTION,
|
||||||
|
CLIENT_NETWORK_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,6 +129,14 @@ namespace gourou
|
|||||||
fullmessage = strdup(msg.str().c_str());
|
fullmessage = strdup(msg.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Exception(const Exception& other)
|
||||||
|
{
|
||||||
|
this->code = other.code;
|
||||||
|
this->line = line;
|
||||||
|
this->file = file;
|
||||||
|
this->fullmessage = strdup(other.fullmessage);
|
||||||
|
}
|
||||||
|
|
||||||
~Exception()
|
~Exception()
|
||||||
{
|
{
|
||||||
free(fullmessage);
|
free(fullmessage);
|
||||||
@@ -225,6 +234,32 @@ namespace gourou
|
|||||||
return trim(res);
|
return trim(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline std::string extractTextElem(const pugi::xml_node& doc, const char* tagName, bool throwOnNull=true)
|
||||||
|
{
|
||||||
|
pugi::xpath_node xpath_node = doc.select_node(tagName);
|
||||||
|
|
||||||
|
if (!xpath_node)
|
||||||
|
{
|
||||||
|
if (throwOnNull)
|
||||||
|
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pugi::xml_node node = xpath_node.node().first_child();
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
if (throwOnNull)
|
||||||
|
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found");
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string res = node.value();
|
||||||
|
return trim(res);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Append an element to root with a sub text element
|
* @brief Append an element to root with a sub text element
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#define _USER_H_
|
#define _USER_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "bytearray.h"
|
#include "bytearray.h"
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
@@ -46,7 +48,7 @@ namespace gourou
|
|||||||
std::string& getDeviceFingerprint();
|
std::string& getDeviceFingerprint();
|
||||||
std::string& getUsername();
|
std::string& getUsername();
|
||||||
std::string& getLoginMethod();
|
std::string& getLoginMethod();
|
||||||
std::string& getCertificate();
|
std::string getLicenseServiceCertificate(std::string url);
|
||||||
std::string& getAuthenticationCertificate();
|
std::string& getAuthenticationCertificate();
|
||||||
std::string& getPrivateLicenseKey();
|
std::string& getPrivateLicenseKey();
|
||||||
|
|
||||||
@@ -70,6 +72,11 @@ namespace gourou
|
|||||||
*/
|
*/
|
||||||
std::string getProperty(const std::string property);
|
std::string getProperty(const std::string property);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get all nodes with property name
|
||||||
|
*/
|
||||||
|
pugi::xpath_node_set getProperties(const std::string property);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create activation.xml and devicesalt files if they did not exists
|
* @brief Create activation.xml and devicesalt files if they did not exists
|
||||||
*
|
*
|
||||||
@@ -90,7 +97,7 @@ namespace gourou
|
|||||||
std::string deviceFingerprint;
|
std::string deviceFingerprint;
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string loginMethod;
|
std::string loginMethod;
|
||||||
std::string certificate;
|
std::map<std::string,std::string> licenseServiceCertificates;
|
||||||
std::string authenticationCertificate;
|
std::string authenticationCertificate;
|
||||||
std::string privateLicenseKey;
|
std::string privateLicenseKey;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Pugixml
|
# Pugixml
|
||||||
git clone https://github.com/zeux/pugixml.git lib/pugixml
|
if [ ! -d lib/pugixml ] ; then
|
||||||
pushd lib/pugixml
|
git clone https://github.com/zeux/pugixml.git lib/pugixml
|
||||||
git checkout latest
|
pushd lib/pugixml
|
||||||
popd
|
git checkout latest
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Base64
|
# Base64
|
||||||
git clone https://gist.github.com/f0fd86b6c73063283afe550bc5d77594.git lib/base64
|
if [ ! -d lib/base64 ] ; then
|
||||||
|
git clone https://gist.github.com/f0fd86b6c73063283afe550bc5d77594.git lib/base64
|
||||||
|
fi
|
||||||
|
|
||||||
|
# uPDFParser
|
||||||
|
if [ ! -d lib/updfparser ] ; then
|
||||||
|
git clone git://soutade.fr/updfparser.git lib/updfparser
|
||||||
|
pushd lib/updfparser
|
||||||
|
make BUILD_STATIC=1 BUILD_SHARED=0
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|||||||
@@ -37,14 +37,14 @@ namespace gourou
|
|||||||
ByteArray::ByteArray(const char* data, int length)
|
ByteArray::ByteArray(const char* data, int length)
|
||||||
{
|
{
|
||||||
if (length == -1)
|
if (length == -1)
|
||||||
length = strlen(data) + 1;
|
length = strlen(data);
|
||||||
|
|
||||||
initData((const unsigned char*)data, (unsigned int) length);
|
initData((const unsigned char*)data, (unsigned int) length);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArray::ByteArray(const std::string& str)
|
ByteArray::ByteArray(const std::string& str)
|
||||||
{
|
{
|
||||||
initData((unsigned char*)str.c_str(), (unsigned int)str.length() + 1);
|
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)
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ namespace gourou
|
|||||||
if (downloadURL == "")
|
if (downloadURL == "")
|
||||||
EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No download URL in document");
|
EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No download URL in document");
|
||||||
|
|
||||||
|
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/resource").node();
|
||||||
|
resource = node.first_child().value();
|
||||||
|
|
||||||
|
if (resource == "")
|
||||||
|
EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No resource in document");
|
||||||
|
|
||||||
pugi::xml_node licenseToken = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken").node();
|
pugi::xml_node licenseToken = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken").node();
|
||||||
|
|
||||||
if (!licenseToken)
|
if (!licenseToken)
|
||||||
@@ -56,11 +62,13 @@ namespace gourou
|
|||||||
if (!newLicenseToken.attribute("xmlns"))
|
if (!newLicenseToken.attribute("xmlns"))
|
||||||
newLicenseToken.append_attribute("xmlns") = ADOBE_ADEPT_NS;
|
newLicenseToken.append_attribute("xmlns") = ADOBE_ADEPT_NS;
|
||||||
|
|
||||||
pugi::xml_node licenseServiceInfo = root.append_child("licenseServiceInfo");
|
pugi::xml_node licenseServiceInfo = root.append_child("adept:licenseServiceInfo");
|
||||||
licenseServiceInfo.append_attribute("xmlns") = ADOBE_ADEPT_NS;
|
pugi::xml_node licenseURL = licenseToken.select_node("licenseURL").node();
|
||||||
licenseServiceInfo.append_copy(licenseToken.select_node("licenseURL").node());
|
licenseURL.set_name("adept:licenseURL");
|
||||||
pugi::xml_node certificate = licenseServiceInfo.append_child("certificate");
|
licenseServiceInfo.append_copy(licenseURL);
|
||||||
certificate.append_child(pugi::node_pcdata).set_value(user->getCertificate().c_str());
|
pugi::xml_node certificate = licenseServiceInfo.append_child("adept:certificate");
|
||||||
|
std::string certificateValue = user->getLicenseServiceCertificate(licenseURL.first_child().value());
|
||||||
|
certificate.append_child(pugi::node_pcdata).set_value(certificateValue.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FulfillmentItem::getMetadata(std::string name)
|
std::string FulfillmentItem::getMetadata(std::string name)
|
||||||
@@ -88,4 +96,9 @@ namespace gourou
|
|||||||
{
|
{
|
||||||
return downloadURL;
|
return downloadURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string FulfillmentItem::getResource()
|
||||||
|
{
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <uPDFParser.h>
|
||||||
|
|
||||||
#include <libgourou.h>
|
#include <libgourou.h>
|
||||||
#include <libgourou_common.h>
|
#include <libgourou_common.h>
|
||||||
@@ -32,6 +35,7 @@
|
|||||||
#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;
|
||||||
@@ -131,7 +135,8 @@ namespace gourou
|
|||||||
ns = attrName.substr(attrName.find(':')+1);
|
ns = attrName.substr(attrName.find(':')+1);
|
||||||
|
|
||||||
nsHash[ns] = ait->value();
|
nsHash[ns] = ait->value();
|
||||||
break;
|
// Don't break here because we may multiple xmlns definitions
|
||||||
|
// break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,13 +161,28 @@ namespace gourou
|
|||||||
|
|
||||||
pushString(sha_ctx, name);
|
pushString(sha_ctx, name);
|
||||||
|
|
||||||
// Must be parsed in reverse order
|
std::vector<std::string> attributes;
|
||||||
for (pugi::xml_attribute attr = root.last_attribute();
|
pugi::xml_attribute attr;
|
||||||
attr; attr = attr.previous_attribute())
|
|
||||||
|
for (attr = root.first_attribute();
|
||||||
|
attr; attr = attr.next_attribute())
|
||||||
{
|
{
|
||||||
if (std::string(attr.name()).find("xmlns") != std::string::npos)
|
if (std::string(attr.name()).find("xmlns") != std::string::npos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
attributes.push_back(attr.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes must be handled in alphabetical order
|
||||||
|
std::sort(attributes.begin(), attributes.end());
|
||||||
|
|
||||||
|
std::vector<std::string>::iterator attributesIt;
|
||||||
|
for(attributesIt = attributes.begin();
|
||||||
|
attributesIt != attributes.end();
|
||||||
|
attributesIt++)
|
||||||
|
{
|
||||||
|
attr = root.attribute(attributesIt->c_str());
|
||||||
|
|
||||||
pushTag(sha_ctx, ASN_ATTRIBUTE);
|
pushTag(sha_ctx, ASN_ATTRIBUTE);
|
||||||
pushString(sha_ctx, "");
|
pushString(sha_ctx, "");
|
||||||
|
|
||||||
@@ -213,14 +233,79 @@ namespace gourou
|
|||||||
for(int i=0; i<(int)SHA1_LEN; i++)
|
for(int i=0; i<(int)SHA1_LEN; i++)
|
||||||
printf("%02x ", sha_out[i]);
|
printf("%02x ", sha_out[i]);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType)
|
std::string DRMProcessor::signNode(const pugi::xml_node& rootNode)
|
||||||
|
{
|
||||||
|
// Compute hash
|
||||||
|
unsigned char sha_out[SHA1_LEN];
|
||||||
|
|
||||||
|
hashNode(rootNode, sha_out);
|
||||||
|
|
||||||
|
// Sign with private key
|
||||||
|
unsigned char res[RSA_KEY_SIZE];
|
||||||
|
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
|
||||||
|
std::string pkcs12 = user->getPKCS12();
|
||||||
|
ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12);
|
||||||
|
|
||||||
|
client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(),
|
||||||
|
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
|
||||||
|
sha_out, sizeof(sha_out), res);
|
||||||
|
if (logLevel >= DEBUG)
|
||||||
|
{
|
||||||
|
printf("Sig : ");
|
||||||
|
for(int i=0; i<(int)sizeof(res); i++)
|
||||||
|
printf("%02x ", res[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray signature(res, sizeof(res));
|
||||||
|
|
||||||
|
return signature.toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::addNonce(pugi::xml_node& root)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
r4 = tp->time
|
||||||
|
r3 = 0
|
||||||
|
r2 = tm->militime
|
||||||
|
r0 = 0x6f046000
|
||||||
|
r1 = 0x388a
|
||||||
|
|
||||||
|
r3 += high(r4*1000)
|
||||||
|
r2 += low(r4*1000)
|
||||||
|
|
||||||
|
r0 += r2
|
||||||
|
r1 += r3
|
||||||
|
*/
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, 0);
|
||||||
|
uint32_t nonce32[2] = {0x6f046000, 0x388a};
|
||||||
|
uint64_t bigtime = tv.tv_sec*1000;
|
||||||
|
nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);
|
||||||
|
nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);
|
||||||
|
|
||||||
|
ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32));
|
||||||
|
uint32_t tmp = 0;
|
||||||
|
nonce.append((const unsigned char*)&tmp, sizeof(tmp));
|
||||||
|
appendTextElem(root, "adept:nonce", nonce.toBase64().data());
|
||||||
|
|
||||||
|
time_t _time = time(0) + 10*60; // Cur time + 10 minutes
|
||||||
|
struct tm* tm_info = gmtime(&_time);
|
||||||
|
char buffer[32];
|
||||||
|
|
||||||
|
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info);
|
||||||
|
appendTextElem(root, "adept:expiration", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map<std::string, std::string>* responseHeaders)
|
||||||
{
|
{
|
||||||
if (contentType == 0)
|
if (contentType == 0)
|
||||||
contentType = "";
|
contentType = "";
|
||||||
std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType);
|
std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders);
|
||||||
|
|
||||||
pugi::xml_document replyDoc;
|
pugi::xml_document replyDoc;
|
||||||
replyDoc.load_buffer(reply.c_str(), reply.length());
|
replyDoc.load_buffer(reply.c_str(), reply.length());
|
||||||
@@ -243,6 +328,107 @@ namespace gourou
|
|||||||
return sendRequest(url, xmlStr, (const char*)"application/vnd.adobe.adept+xml");
|
return sendRequest(url, xmlStr, (const char*)"application/vnd.adobe.adept+xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::buildAuthRequest(pugi::xml_document& authReq)
|
||||||
|
{
|
||||||
|
pugi::xml_node decl = authReq.append_child(pugi::node_declaration);
|
||||||
|
decl.append_attribute("version") = "1.0";
|
||||||
|
|
||||||
|
pugi::xml_node root = authReq.append_child("adept:credentials");
|
||||||
|
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:user", user->getUUID());
|
||||||
|
|
||||||
|
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
|
||||||
|
unsigned char* pkcs12 = 0;
|
||||||
|
unsigned int pkcs12Length;
|
||||||
|
ByteArray pkcs12Cert = ByteArray::fromBase64(user->getPKCS12());
|
||||||
|
|
||||||
|
client->extractCertificate(pkcs12Cert.data(), pkcs12Cert.length(),
|
||||||
|
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
|
||||||
|
&pkcs12, &pkcs12Length);
|
||||||
|
ByteArray privateCertificate(pkcs12, pkcs12Length);
|
||||||
|
free(pkcs12);
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:certificate", privateCertificate.toBase64());
|
||||||
|
appendTextElem(root, "adept:licenseCertificate", user->getProperty("//adept:licenseCertificate"));
|
||||||
|
appendTextElem(root, "adept:authenticationCertificate", user->getProperty("//adept:authenticationCertificate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL)
|
||||||
|
{
|
||||||
|
pugi::xml_node decl = initLicReq.append_child(pugi::node_declaration);
|
||||||
|
decl.append_attribute("version") = "1.0";
|
||||||
|
|
||||||
|
pugi::xml_node root = initLicReq.append_child("adept:licenseServiceRequest");
|
||||||
|
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
|
root.append_attribute("identity") = "user";
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:operatorURL", operatorURL);
|
||||||
|
addNonce(root);
|
||||||
|
appendTextElem(root, "adept:user", user->getUUID());
|
||||||
|
|
||||||
|
std::string signature = signNode(root);
|
||||||
|
appendTextElem(root, "adept:signature", signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::doOperatorAuth(std::string operatorURL)
|
||||||
|
{
|
||||||
|
pugi::xml_document authReq;
|
||||||
|
buildAuthRequest(authReq);
|
||||||
|
std::string authURL = operatorURL;
|
||||||
|
unsigned int fulfillPos = authURL.rfind("Fulfill");
|
||||||
|
if (fulfillPos == (authURL.size() - (sizeof("Fulfill")-1)))
|
||||||
|
authURL = authURL.substr(0, fulfillPos-1);
|
||||||
|
ByteArray replyData = sendRequest(authReq, authURL + "/Auth");
|
||||||
|
|
||||||
|
pugi::xml_document initLicReq;
|
||||||
|
std::string activationURL = user->getProperty("//adept:activationURL");
|
||||||
|
buildInitLicenseServiceRequest(initLicReq, authURL);
|
||||||
|
sendRequest(initLicReq, activationURL + "/InitLicenseService");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::operatorAuth(std::string operatorURL)
|
||||||
|
{
|
||||||
|
pugi::xpath_node_set operatorList = user->getProperties("//adept:operatorURL");
|
||||||
|
|
||||||
|
for (pugi::xpath_node_set::const_iterator operatorIt = operatorList.begin();
|
||||||
|
operatorIt != operatorList.end(); ++operatorIt)
|
||||||
|
{
|
||||||
|
std::string value = operatorIt->node().first_child().value();
|
||||||
|
if (trim(value) == operatorURL)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Already authenticated to operator " << operatorURL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doOperatorAuth(operatorURL);
|
||||||
|
|
||||||
|
// Add new operatorURL to list
|
||||||
|
pugi::xml_document activationDoc;
|
||||||
|
user->readActivation(activationDoc);
|
||||||
|
|
||||||
|
pugi::xml_node root;
|
||||||
|
pugi::xpath_node xpathRes = activationDoc.select_node("//adept:operatorURLList");
|
||||||
|
|
||||||
|
// Create adept:operatorURLList if it doesn't exists
|
||||||
|
if (!xpathRes)
|
||||||
|
{
|
||||||
|
xpathRes = activationDoc.select_node("/activationInfo");
|
||||||
|
root = xpathRes.node();
|
||||||
|
root = root.append_child("adept:operatorURLList");
|
||||||
|
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:user", user->getUUID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
root = xpathRes.node();
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:operatorURL", operatorURL);
|
||||||
|
|
||||||
|
user->updateActivationFile(activationDoc);
|
||||||
|
}
|
||||||
|
|
||||||
void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)
|
void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)
|
||||||
{
|
{
|
||||||
pugi::xml_node decl = fulfillReq.append_child(pugi::node_declaration);
|
pugi::xml_node decl = fulfillReq.append_child(pugi::node_declaration);
|
||||||
@@ -270,6 +456,49 @@ namespace gourou
|
|||||||
appendTextElem(activationToken, "adept:device", user->getDeviceUUID());
|
appendTextElem(activationToken, "adept:device", user->getDeviceUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::fetchLicenseServiceCertificate(const std::string& licenseURL,
|
||||||
|
const std::string& operatorURL)
|
||||||
|
{
|
||||||
|
if (user->getLicenseServiceCertificate(licenseURL) != "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string licenseServiceInfoReq = operatorURL + "/LicenseServiceInfo?licenseURL=" + licenseURL;
|
||||||
|
|
||||||
|
ByteArray replyData;
|
||||||
|
replyData = sendRequest(licenseServiceInfoReq);
|
||||||
|
|
||||||
|
pugi::xml_document licenseServicesDoc;
|
||||||
|
licenseServicesDoc.load_buffer(replyData.data(), replyData.length());
|
||||||
|
|
||||||
|
// Add new license certificate
|
||||||
|
pugi::xml_document activationDoc;
|
||||||
|
user->readActivation(activationDoc);
|
||||||
|
|
||||||
|
pugi::xml_node root;
|
||||||
|
pugi::xpath_node xpathRes = activationDoc.select_node("//adept:licenseServices");
|
||||||
|
|
||||||
|
// Create adept:licenseServices if it doesn't exists
|
||||||
|
if (!xpathRes)
|
||||||
|
{
|
||||||
|
xpathRes = activationDoc.select_node("/activationInfo");
|
||||||
|
root = xpathRes.node();
|
||||||
|
root = root.append_child("adept:licenseServices");
|
||||||
|
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
root = xpathRes.node();
|
||||||
|
|
||||||
|
root = root.append_child("adept:licenseServiceInfo");
|
||||||
|
|
||||||
|
std::string certificate = extractTextElem(licenseServicesDoc,
|
||||||
|
"/licenseServiceInfo/certificate");
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:licenseURL", licenseURL);
|
||||||
|
appendTextElem(root, "adept:certificate", certificate);
|
||||||
|
|
||||||
|
user->updateActivationFile(activationDoc);
|
||||||
|
}
|
||||||
|
|
||||||
FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile)
|
FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile)
|
||||||
{
|
{
|
||||||
if (!user->getPKCS12().length())
|
if (!user->getPKCS12().length())
|
||||||
@@ -277,7 +506,7 @@ namespace gourou
|
|||||||
|
|
||||||
pugi::xml_document acsmDoc;
|
pugi::xml_document acsmDoc;
|
||||||
|
|
||||||
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single))
|
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8))
|
||||||
EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile);
|
EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile);
|
||||||
|
|
||||||
GOUROU_LOG(INFO, "Fulfill " << ACSMFile);
|
GOUROU_LOG(INFO, "Fulfill " << ACSMFile);
|
||||||
@@ -300,59 +529,69 @@ namespace gourou
|
|||||||
|
|
||||||
hmacParentNode.remove_child(hmacNode);
|
hmacParentNode.remove_child(hmacNode);
|
||||||
|
|
||||||
// Compute hash
|
std::string signature = signNode(rootNode);
|
||||||
unsigned char sha_out[SHA1_LEN];
|
|
||||||
|
|
||||||
hashNode(rootNode, sha_out);
|
|
||||||
|
|
||||||
// Sign with private key
|
|
||||||
unsigned char res[RSA_KEY_SIZE];
|
|
||||||
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
|
|
||||||
std::string pkcs12 = user->getPKCS12();
|
|
||||||
ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12);
|
|
||||||
|
|
||||||
client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(),
|
|
||||||
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
|
|
||||||
sha_out, sizeof(sha_out), res);
|
|
||||||
if (logLevel >= DEBUG)
|
|
||||||
{
|
|
||||||
printf("Sig : ");
|
|
||||||
for(int i=0; i<(int)sizeof(res); i++)
|
|
||||||
printf("%02x ", res[i]);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add removed HMAC
|
// Add removed HMAC
|
||||||
appendTextElem(hmacParentNode, hmacNode.name(), hmacNode.first_child().value());
|
appendTextElem(hmacParentNode, hmacNode.name(), hmacNode.first_child().value());
|
||||||
|
|
||||||
// Add base64 encoded signature
|
appendTextElem(rootNode, "adept:signature", signature);
|
||||||
ByteArray signature(res, sizeof(res));
|
|
||||||
std::string b64Signature = signature.toBase64();
|
|
||||||
|
|
||||||
appendTextElem(rootNode, "adept:signature", b64Signature);
|
|
||||||
|
|
||||||
pugi::xpath_node node = acsmDoc.select_node("//operatorURL");
|
pugi::xpath_node node = acsmDoc.select_node("//operatorURL");
|
||||||
if (!node)
|
if (!node)
|
||||||
EXCEPTION(FF_NO_OPERATOR_URL, "OperatorURL not found in ACSM document");
|
EXCEPTION(FF_NO_OPERATOR_URL, "OperatorURL not found in ACSM document");
|
||||||
|
|
||||||
std::string operatorURL = node.node().first_child().value();
|
std::string operatorURL = node.node().first_child().value();
|
||||||
operatorURL = trim(operatorURL) + "/Fulfill";
|
operatorURL = trim(operatorURL);
|
||||||
|
std::string fulfillURL = operatorURL + "/Fulfill";
|
||||||
|
|
||||||
ByteArray replyData = sendRequest(fulfillReq, operatorURL);
|
operatorAuth(fulfillURL);
|
||||||
|
|
||||||
|
ByteArray replyData;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
replyData = sendRequest(fulfillReq, fulfillURL);
|
||||||
|
}
|
||||||
|
catch (gourou::Exception& e)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Operator requires authentication even if it's already in
|
||||||
|
our operator list
|
||||||
|
*/
|
||||||
|
std::string errorMsg(e.what());
|
||||||
|
if (e.getErrorCode() == GOUROU_ADEPT_ERROR &&
|
||||||
|
errorMsg.find("E_ADEPT_DISTRIBUTOR_AUTH") != std::string::npos)
|
||||||
|
{
|
||||||
|
doOperatorAuth(fulfillURL);
|
||||||
|
replyData = sendRequest(fulfillReq, fulfillURL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pugi::xml_document fulfillReply;
|
pugi::xml_document fulfillReply;
|
||||||
|
|
||||||
fulfillReply.load_string((const char*)replyData.data());
|
fulfillReply.load_string((const char*)replyData.data());
|
||||||
|
|
||||||
|
std::string licenseURL = extractTextElem(fulfillReply, "//licenseToken/licenseURL");
|
||||||
|
|
||||||
|
fetchLicenseServiceCertificate(licenseURL, operatorURL);
|
||||||
|
|
||||||
return new FulfillmentItem(fulfillReply, user);
|
return new FulfillmentItem(fulfillReply, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::download(FulfillmentItem* item, std::string path)
|
DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path)
|
||||||
{
|
{
|
||||||
|
ITEM_TYPE res = EPUB;
|
||||||
|
|
||||||
if (!item)
|
if (!item)
|
||||||
EXCEPTION(DW_NO_ITEM, "No item");
|
EXCEPTION(DW_NO_ITEM, "No item");
|
||||||
|
|
||||||
ByteArray replyData = sendRequest(item->getDownloadURL());
|
std::map<std::string, std::string> headers;
|
||||||
|
|
||||||
|
ByteArray replyData = sendRequest(item->getDownloadURL(), "", 0, &headers);
|
||||||
|
|
||||||
writeFile(path, replyData);
|
writeFile(path, replyData);
|
||||||
|
|
||||||
@@ -360,10 +599,61 @@ namespace gourou
|
|||||||
|
|
||||||
std::string rightsStr = item->getRights();
|
std::string rightsStr = item->getRights();
|
||||||
|
|
||||||
|
if (headers.count("Content-Type") && headers["Content-Type"] == "application/pdf")
|
||||||
|
res = PDF;
|
||||||
|
|
||||||
|
if (res == EPUB)
|
||||||
|
{
|
||||||
void* handler = client->zipOpen(path);
|
void* handler = client->zipOpen(path);
|
||||||
client->zipWriteFile(handler, "META-INF/rights.xml", rightsStr);
|
client->zipWriteFile(handler, "META-INF/rights.xml", rightsStr);
|
||||||
client->zipClose(handler);
|
client->zipClose(handler);
|
||||||
}
|
}
|
||||||
|
else if (res == PDF)
|
||||||
|
{
|
||||||
|
uPDFParser::Parser parser;
|
||||||
|
bool EBXHandlerFound = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Parse PDF");
|
||||||
|
parser.parse(path);
|
||||||
|
}
|
||||||
|
catch(std::invalid_argument& e)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(ERROR, "Invalid PDF");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uPDFParser::Object*> objects = parser.objects();
|
||||||
|
std::vector<uPDFParser::Object*>::reverse_iterator it;
|
||||||
|
|
||||||
|
for(it = objects.rbegin(); it != objects.rend(); it++)
|
||||||
|
{
|
||||||
|
// Update EBX_HANDLER with rights
|
||||||
|
if ((*it)->hasKey("Filter") && (**it)["Filter"]->str() == "/EBX_HANDLER")
|
||||||
|
{
|
||||||
|
EBXHandlerFound = true;
|
||||||
|
uPDFParser::Object* ebx = (*it)->clone();
|
||||||
|
(*ebx)["ADEPT_ID"] = new uPDFParser::String(item->getResource());
|
||||||
|
(*ebx)["EBX_BOOKID"] = new uPDFParser::String(item->getResource());
|
||||||
|
ByteArray zipped;
|
||||||
|
client->deflate(rightsStr, zipped);
|
||||||
|
(*ebx)["ADEPT_LICENSE"] = new uPDFParser::String(zipped.toBase64());
|
||||||
|
parser.addObject(ebx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EBXHandlerFound)
|
||||||
|
parser.write(path, true);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXCEPTION(DW_NO_EBX_HANDLER, "EBX_HANDLER not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void DRMProcessor::buildSignInRequest(pugi::xml_document& signInRequest,
|
void DRMProcessor::buildSignInRequest(pugi::xml_document& signInRequest,
|
||||||
const std::string& adobeID, const std::string& adobePassword,
|
const std::string& adobeID, const std::string& adobePassword,
|
||||||
@@ -373,7 +663,11 @@ namespace gourou
|
|||||||
decl.append_attribute("version") = "1.0";
|
decl.append_attribute("version") = "1.0";
|
||||||
pugi::xml_node signIn = signInRequest.append_child("adept:signIn");
|
pugi::xml_node signIn = signInRequest.append_child("adept:signIn");
|
||||||
signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
signIn.append_attribute("method") = user->getLoginMethod().c_str();
|
std::string loginMethod = user->getLoginMethod();
|
||||||
|
if (loginMethod.size())
|
||||||
|
signIn.append_attribute("method") = loginMethod.c_str();
|
||||||
|
else
|
||||||
|
signIn.append_attribute("method") = "AdobeID";
|
||||||
|
|
||||||
unsigned char encryptedSignInData[RSA_KEY_SIZE];
|
unsigned char encryptedSignInData[RSA_KEY_SIZE];
|
||||||
const unsigned char* deviceKey = device->getDeviceKey();
|
const unsigned char* deviceKey = device->getDeviceKey();
|
||||||
@@ -504,37 +798,7 @@ namespace gourou
|
|||||||
appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]);
|
appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]);
|
||||||
appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]);
|
appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]);
|
||||||
|
|
||||||
/*
|
addNonce(root);
|
||||||
r4 = tp->time
|
|
||||||
r3 = 0
|
|
||||||
r2 = tm->militime
|
|
||||||
r0 = 0x6f046000
|
|
||||||
r1 = 0x388a
|
|
||||||
|
|
||||||
r3 += high(r4*1000)
|
|
||||||
r2 += low(r4*1000)
|
|
||||||
|
|
||||||
r0 += r2
|
|
||||||
r1 += r3
|
|
||||||
*/
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, 0);
|
|
||||||
uint32_t nonce32[2] = {0x6f046000, 0x388a};
|
|
||||||
uint64_t bigtime = tv.tv_sec*1000;
|
|
||||||
nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);
|
|
||||||
nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);
|
|
||||||
|
|
||||||
ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32));
|
|
||||||
uint32_t tmp = 0;
|
|
||||||
nonce.append((const unsigned char*)&tmp, sizeof(tmp));
|
|
||||||
appendTextElem(root, "adept:nonce", nonce.toBase64().data());
|
|
||||||
|
|
||||||
time_t _time = time(0) + 10*60; // Cur time + 10 minutes
|
|
||||||
struct tm* tm_info = localtime(&_time);
|
|
||||||
char buffer[32];
|
|
||||||
|
|
||||||
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info);
|
|
||||||
appendTextElem(root, "adept:expiration", buffer);
|
|
||||||
|
|
||||||
appendTextElem(root, "adept:user", user->getUUID());
|
appendTextElem(root, "adept:user", user->getUUID());
|
||||||
}
|
}
|
||||||
@@ -547,28 +811,12 @@ namespace gourou
|
|||||||
|
|
||||||
buildActivateReq(activateReq);
|
buildActivateReq(activateReq);
|
||||||
|
|
||||||
// Compute hash
|
|
||||||
unsigned char sha_out[SHA1_LEN];
|
|
||||||
|
|
||||||
pugi::xml_node root = activateReq.select_node("adept:activate").node();
|
pugi::xml_node root = activateReq.select_node("adept:activate").node();
|
||||||
hashNode(root, sha_out);
|
|
||||||
|
|
||||||
// Sign with private key
|
std::string signature = signNode(root);
|
||||||
ByteArray RSAKey = ByteArray::fromBase64(user->getPKCS12());
|
|
||||||
unsigned char res[RSA_KEY_SIZE];
|
|
||||||
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE);
|
|
||||||
|
|
||||||
client->RSAPrivateEncrypt(RSAKey.data(), RSAKey.length(), RSAInterface::RSA_KEY_PKCS12,
|
|
||||||
deviceKey.toBase64().c_str(),
|
|
||||||
sha_out, sizeof(sha_out),
|
|
||||||
res);
|
|
||||||
|
|
||||||
// Add base64 encoded signature
|
|
||||||
ByteArray signature(res, sizeof(res));
|
|
||||||
std::string b64Signature = signature.toBase64();
|
|
||||||
|
|
||||||
root = activateReq.select_node("adept:activate").node();
|
root = activateReq.select_node("adept:activate").node();
|
||||||
appendTextElem(root, "adept:signature", b64Signature);
|
appendTextElem(root, "adept:signature", signature);
|
||||||
|
|
||||||
pugi::xml_document activationDoc;
|
pugi::xml_document activationDoc;
|
||||||
user->readActivation(activationDoc);
|
user->readActivation(activationDoc);
|
||||||
@@ -658,6 +906,19 @@ namespace gourou
|
|||||||
return res.toBase64();
|
return res.toBase64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::exportPrivateLicenseKey(std::string path)
|
||||||
|
{
|
||||||
|
int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU);
|
||||||
|
if (fd <= 0)
|
||||||
|
EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
|
||||||
|
|
||||||
|
ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey());
|
||||||
|
/* In adobekey.py, we get base64 decoded data [26:] */
|
||||||
|
write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
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;}
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/user.cpp
25
src/user.cpp
@@ -48,7 +48,6 @@ namespace gourou {
|
|||||||
uuid = gourou::extractTextElem(activationDoc, "//adept:user", throwOnNull);
|
uuid = gourou::extractTextElem(activationDoc, "//adept:user", throwOnNull);
|
||||||
deviceUUID = gourou::extractTextElem(activationDoc, "//device", throwOnNull);
|
deviceUUID = gourou::extractTextElem(activationDoc, "//device", throwOnNull);
|
||||||
deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull);
|
deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull);
|
||||||
certificate = gourou::extractTextElem(activationDoc, "//adept:certificate", 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);
|
username = gourou::extractTextElem(activationDoc, "//adept:username", throwOnNull);
|
||||||
@@ -61,6 +60,15 @@ namespace gourou {
|
|||||||
if (throwOnNull)
|
if (throwOnNull)
|
||||||
EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");
|
EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pugi::xpath_node_set nodeSet = activationDoc.select_nodes("//adept:licenseServices/adept:licenseServiceInfo");
|
||||||
|
for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();
|
||||||
|
it != nodeSet.end(); ++it)
|
||||||
|
{
|
||||||
|
std::string url = gourou::extractTextElem(it->node(), "adept:licenseURL");
|
||||||
|
std::string certificate = gourou::extractTextElem(it->node(), "adept:certificate");
|
||||||
|
licenseServiceCertificates[url] = certificate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(gourou::Exception& e)
|
catch(gourou::Exception& e)
|
||||||
{
|
{
|
||||||
@@ -74,7 +82,6 @@ namespace gourou {
|
|||||||
std::string& User::getDeviceFingerprint() { return deviceFingerprint; }
|
std::string& User::getDeviceFingerprint() { return deviceFingerprint; }
|
||||||
std::string& User::getUsername() { return username; }
|
std::string& User::getUsername() { return username; }
|
||||||
std::string& User::getLoginMethod() { return loginMethod; }
|
std::string& User::getLoginMethod() { return loginMethod; }
|
||||||
std::string& User::getCertificate() { return certificate; }
|
|
||||||
std::string& User::getAuthenticationCertificate() { return authenticationCertificate; }
|
std::string& User::getAuthenticationCertificate() { return authenticationCertificate; }
|
||||||
std::string& User::getPrivateLicenseKey() { return privateLicenseKey; }
|
std::string& User::getPrivateLicenseKey() { return privateLicenseKey; }
|
||||||
|
|
||||||
@@ -110,6 +117,11 @@ namespace gourou {
|
|||||||
return trim(res);
|
return trim(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pugi::xpath_node_set User::getProperties(const std::string property)
|
||||||
|
{
|
||||||
|
return activationDoc.select_nodes(property.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
User* User::createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer)
|
User* User::createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer)
|
||||||
{
|
{
|
||||||
struct stat _stat;
|
struct stat _stat;
|
||||||
@@ -195,4 +207,13 @@ namespace gourou {
|
|||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string User::getLicenseServiceCertificate(std::string url)
|
||||||
|
{
|
||||||
|
if (licenseServiceCertificates.count(trim(url)))
|
||||||
|
return licenseServiceCertificates[trim(url)];
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
TARGETS=acsmdownloader activate
|
TARGETS=acsmdownloader adept_activate
|
||||||
|
|
||||||
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/
|
||||||
|
LDFLAGS=`pkg-config --libs Qt5Core Qt5Network` -L$(ROOT) -lcrypto -lzip -lz
|
||||||
ifneq ($(STATIC_UTILS),)
|
ifneq ($(STATIC_UTILS),)
|
||||||
LDFLAGS=`pkg-config --libs Qt5Core Qt5Network` -L$(ROOT) $(ROOT)/libgourou.a -lcrypto -lzip
|
LDFLAGS += $(ROOT)/libgourou.a
|
||||||
else
|
else
|
||||||
LDFLAGS=`pkg-config --libs Qt5Core Qt5Network` -L$(ROOT) -lgourou -lcrypto -lzip
|
LDFLAGS += -lgourou
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(DEBUG),)
|
ifneq ($(DEBUG),)
|
||||||
@@ -19,7 +20,7 @@ all: $(TARGETS)
|
|||||||
acsmdownloader: drmprocessorclientimpl.cpp acsmdownloader.cpp
|
acsmdownloader: drmprocessorclientimpl.cpp acsmdownloader.cpp
|
||||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
activate: drmprocessorclientimpl.cpp activate.cpp
|
adept_activate: drmprocessorclientimpl.cpp adept_activate.cpp
|
||||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ static const char* deviceFile = "device.xml";
|
|||||||
static const char* activationFile = "activation.xml";
|
static const char* activationFile = "activation.xml";
|
||||||
static const char* devicekeyFile = "devicesalt";
|
static const char* devicekeyFile = "devicesalt";
|
||||||
static const char* acsmFile = 0;
|
static const char* acsmFile = 0;
|
||||||
|
static bool exportPrivateKey = false;
|
||||||
static const char* outputFile = 0;
|
static const char* outputFile = 0;
|
||||||
static const char* outputDir = 0;
|
static const char* outputDir = 0;
|
||||||
static const char* defaultDirs[] = {
|
static const char* defaultDirs[] = {
|
||||||
@@ -66,22 +67,20 @@ public:
|
|||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DRMProcessorClientImpl client;
|
DRMProcessorClientImpl client;
|
||||||
gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
|
gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
|
||||||
|
gourou::User* user = processor.getUser();
|
||||||
|
|
||||||
gourou::FulfillmentItem* item = processor.fulfill(acsmFile);
|
if (exportPrivateKey)
|
||||||
|
{
|
||||||
std::string filename;
|
std::string filename;
|
||||||
if (!outputFile)
|
if (!outputFile)
|
||||||
{
|
filename = std::string("Adobe_PrivateLicenseKey--") + user->getUsername() + ".der";
|
||||||
filename = item->getMetadata("title");
|
|
||||||
if (filename == "")
|
|
||||||
filename = "output.epub";
|
|
||||||
else
|
else
|
||||||
filename += ".epub";
|
filename = outputFile;
|
||||||
}
|
|
||||||
|
|
||||||
if (outputDir)
|
if (outputDir)
|
||||||
{
|
{
|
||||||
@@ -92,15 +91,55 @@ public:
|
|||||||
filename = std::string(outputDir) + "/" + filename;
|
filename = std::string(outputDir) + "/" + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.download(item, filename);
|
processor.exportPrivateLicenseKey(filename);
|
||||||
|
|
||||||
|
std::cout << "Private license key exported to " << filename << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gourou::FulfillmentItem* item = processor.fulfill(acsmFile);
|
||||||
|
|
||||||
|
std::string filename;
|
||||||
|
if (!outputFile)
|
||||||
|
{
|
||||||
|
filename = item->getMetadata("title");
|
||||||
|
if (filename == "")
|
||||||
|
filename = "output";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filename = outputFile;
|
||||||
|
|
||||||
|
if (outputDir)
|
||||||
|
{
|
||||||
|
QDir dir(outputDir);
|
||||||
|
if (!dir.exists(outputDir))
|
||||||
|
dir.mkpath(outputDir);
|
||||||
|
|
||||||
|
filename = std::string(outputDir) + "/" + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename);
|
||||||
|
|
||||||
|
if (!outputFile)
|
||||||
|
{
|
||||||
|
std::string finalName = filename;
|
||||||
|
if (type == gourou::DRMProcessor::ITEM_TYPE::PDF)
|
||||||
|
finalName += ".pdf";
|
||||||
|
else
|
||||||
|
finalName += ".epub";
|
||||||
|
QDir dir;
|
||||||
|
dir.rename(filename.c_str(), finalName.c_str());
|
||||||
|
filename = finalName;
|
||||||
|
}
|
||||||
std::cout << "Created " << filename << std::endl;
|
std::cout << "Created " << filename << std::endl;
|
||||||
|
}
|
||||||
} catch(std::exception& e)
|
} catch(std::exception& e)
|
||||||
{
|
{
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << e.what() << std::endl;
|
||||||
this->app->exit(1);
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->app->exit(0);
|
this->app->exit(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -136,14 +175,15 @@ static void usage(const char* cmd)
|
|||||||
{
|
{
|
||||||
std::cout << "Download EPUB file from ACSM request file" << std::endl;
|
std::cout << "Download EPUB file from ACSM request file" << std::endl;
|
||||||
|
|
||||||
std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-s|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output.epub] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm" << std::endl << 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|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << 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 << " " << "-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 << " " << "-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-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
|
||||||
std::cout << " " << "-o|--output-file" << "\t" << "Optional output epub filename (default <title.epub>)" << std::endl;
|
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default <title.(epub|pdf|der)>)" << std::endl;
|
||||||
std::cout << " " << "-f|--acsm-file" << "\t" << "ACSM request file for epub download" << std::endl;
|
std::cout << " " << "-f|--acsm-file" << "\t" << "ACSM request file for epub download" << std::endl;
|
||||||
|
std::cout << " " << "-e|--export-private-key"<< "\t" << "Export private key in DER format" << std::endl;
|
||||||
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << 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 << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
||||||
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
||||||
@@ -172,13 +212,14 @@ int main(int argc, char** argv)
|
|||||||
{"output-dir", required_argument, 0, 'O' },
|
{"output-dir", required_argument, 0, 'O' },
|
||||||
{"output-file", required_argument, 0, 'o' },
|
{"output-file", required_argument, 0, 'o' },
|
||||||
{"acsm-file", required_argument, 0, 'f' },
|
{"acsm-file", required_argument, 0, 'f' },
|
||||||
|
{"export-private-key",no_argument, 0, 'e' },
|
||||||
{"verbose", no_argument, 0, 'v' },
|
{"verbose", no_argument, 0, 'v' },
|
||||||
{"version", no_argument, 0, 'V' },
|
{"version", no_argument, 0, 'V' },
|
||||||
{"help", no_argument, 0, 'h' },
|
{"help", no_argument, 0, 'h' },
|
||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "d:a:k:O:o:f:vVh",
|
c = getopt_long(argc, argv, "d:a:k:O:o:f:evVh",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
@@ -202,6 +243,9 @@ int main(int argc, char** argv)
|
|||||||
case 'o':
|
case 'o':
|
||||||
outputFile = optarg;
|
outputFile = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'e':
|
||||||
|
exportPrivateKey = true;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
break;
|
break;
|
||||||
@@ -219,7 +263,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
gourou::DRMProcessor::setLogLevel(verbose);
|
gourou::DRMProcessor::setLogLevel(verbose);
|
||||||
|
|
||||||
if (!acsmFile || (outputDir && !outputDir[0]) ||
|
if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) ||
|
||||||
(outputFile && !outputFile[0]))
|
(outputFile && !outputFile[0]))
|
||||||
{
|
{
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
@@ -230,17 +274,33 @@ int main(int argc, char** argv)
|
|||||||
ACSMDownloader downloader(&app);
|
ACSMDownloader downloader(&app);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
bool hasErrors = false;
|
||||||
|
const char* orig;
|
||||||
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
||||||
{
|
{
|
||||||
|
orig = *files[i];
|
||||||
*files[i] = findFile(*files[i]);
|
*files[i] = findFile(*files[i]);
|
||||||
if (!*files[i])
|
if (!*files[i])
|
||||||
{
|
{
|
||||||
std::cout << "Error : " << *files[i] << " doesn't exists" << std::endl;
|
std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl;
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto end;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasErrors)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (exportPrivateKey)
|
||||||
|
{
|
||||||
|
if (acsmFile)
|
||||||
|
{
|
||||||
|
usage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
QFile file(acsmFile);
|
QFile file(acsmFile);
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
{
|
{
|
||||||
@@ -248,6 +308,7 @@ int main(int argc, char** argv)
|
|||||||
ret = -1;
|
ret = -1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(&downloader);
|
QThreadPool::globalInstance()->start(&downloader);
|
||||||
|
|
||||||
|
|||||||
@@ -100,10 +100,10 @@ static std::string getpass(const char *prompt, bool show_asterisk=false)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Activate: public QRunnable
|
class ADEPTActivate: public QRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Activate(QCoreApplication* app):
|
ADEPTActivate(QCoreApplication* app):
|
||||||
app(app)
|
app(app)
|
||||||
{
|
{
|
||||||
setAutoDelete(false);
|
setAutoDelete(false);
|
||||||
@@ -111,6 +111,7 @@ public:
|
|||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DRMProcessorClientImpl client;
|
DRMProcessorClientImpl client;
|
||||||
@@ -124,10 +125,10 @@ public:
|
|||||||
} catch(std::exception& e)
|
} catch(std::exception& e)
|
||||||
{
|
{
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << e.what() << std::endl;
|
||||||
this->app->exit(1);
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->app->exit(0);
|
this->app->exit(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -262,7 +263,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
Activate activate(&app);
|
ADEPTActivate activate(&app);
|
||||||
QThreadPool::globalInstance()->start(&activate);
|
QThreadPool::globalInstance()->start(&activate);
|
||||||
|
|
||||||
ret = app.exec();
|
ret = app.exec();
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
#include <libgourou_common.h>
|
#include <libgourou_common.h>
|
||||||
#include <libgourou_log.h>
|
#include <libgourou_log.h>
|
||||||
@@ -82,7 +83,7 @@ void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int len
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* HTTP interface */
|
/* HTTP interface */
|
||||||
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType)
|
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()));
|
||||||
QNetworkAccessManager networkManager;
|
QNetworkAccessManager networkManager;
|
||||||
@@ -111,6 +112,24 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
while (!reply->isFinished())
|
while (!reply->isFinished())
|
||||||
app->processEvents();
|
app->processEvents();
|
||||||
|
|
||||||
|
QByteArray location = reply->rawHeader("Location");
|
||||||
|
if (location.size() != 0)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(gourou::DEBUG, "New location");
|
||||||
|
return sendHTTPRequest(location.constData(), POSTData, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError)
|
||||||
|
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << reply->errorString().toStdString());
|
||||||
|
|
||||||
|
QList<QByteArray> headers = reply->rawHeaderList();
|
||||||
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
|
if (gourou::logLevel >= gourou::DEBUG)
|
||||||
|
std::cout << headers[i].constData() << " : " << reply->rawHeader(headers[i]).constData() << std::endl;
|
||||||
|
if (responseHeaders)
|
||||||
|
(*responseHeaders)[headers[i].constData()] = reply->rawHeader(headers[i]).constData();
|
||||||
|
}
|
||||||
|
|
||||||
replyData = reply->readAll();
|
replyData = reply->readAll();
|
||||||
if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
|
if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
|
||||||
{
|
{
|
||||||
@@ -213,6 +232,25 @@ void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char**
|
|||||||
EVP_PKEY_free(evpKey);
|
EVP_PKEY_free(evpKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
|
const RSA_KEY_TYPE keyType, const std::string& password,
|
||||||
|
unsigned char** certOut, unsigned int* certOutLength)
|
||||||
|
{
|
||||||
|
PKCS12 * pkcs12;
|
||||||
|
EVP_PKEY* pkey = 0;
|
||||||
|
X509* cert = 0;
|
||||||
|
STACK_OF(X509)* ca;
|
||||||
|
|
||||||
|
pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);
|
||||||
|
if (!pkcs12)
|
||||||
|
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
PKCS12_parse(pkcs12, password.c_str(), &pkey, &cert, &ca);
|
||||||
|
|
||||||
|
*certOutLength = i2d_X509(cert, certOut);
|
||||||
|
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
}
|
||||||
|
|
||||||
/* Crypto interface */
|
/* Crypto interface */
|
||||||
void DRMProcessorClientImpl::AESEncrypt(CHAINING_MODE chaining,
|
void DRMProcessorClientImpl::AESEncrypt(CHAINING_MODE chaining,
|
||||||
const unsigned char* key, unsigned int keyLength,
|
const unsigned char* key, unsigned int keyLength,
|
||||||
@@ -244,8 +282,8 @@ void* DRMProcessorClientImpl::AESEncryptInit(CHAINING_MODE chaining,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
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);
|
||||||
@@ -383,3 +421,78 @@ void DRMProcessorClientImpl::zipClose(void* handler)
|
|||||||
{
|
{
|
||||||
zip_close((zip_t*)handler);
|
zip_close((zip_t*)handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRMProcessorClientImpl::inflate(std::string data, gourou::ByteArray& result,
|
||||||
|
int wbits)
|
||||||
|
{
|
||||||
|
unsigned int dataSize = data.size()*2;
|
||||||
|
unsigned char* buffer = new unsigned char[dataSize];
|
||||||
|
|
||||||
|
z_stream infstream;
|
||||||
|
|
||||||
|
infstream.zalloc = Z_NULL;
|
||||||
|
infstream.zfree = Z_NULL;
|
||||||
|
infstream.opaque = Z_NULL;
|
||||||
|
|
||||||
|
infstream.avail_in = (uInt)data.size();
|
||||||
|
infstream.next_in = (Bytef *)data.c_str(); // input char array
|
||||||
|
infstream.avail_out = (uInt)dataSize; // size of output
|
||||||
|
infstream.next_out = (Bytef *)buffer; // output char array
|
||||||
|
|
||||||
|
int ret = inflateInit2(&infstream, wbits);
|
||||||
|
|
||||||
|
ret = ::inflate(&infstream, Z_SYNC_FLUSH);
|
||||||
|
while (ret == Z_OK || ret == Z_STREAM_END)
|
||||||
|
{
|
||||||
|
result.append(buffer, dataSize-infstream.avail_out);
|
||||||
|
if (ret == Z_STREAM_END) break;
|
||||||
|
infstream.avail_out = (uInt)dataSize; // size of output
|
||||||
|
infstream.next_out = (Bytef *)buffer; // output char array
|
||||||
|
ret = ::inflate(&infstream, Z_SYNC_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
inflateEnd(&infstream);
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR)
|
||||||
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, zError(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessorClientImpl::deflate(std::string data, gourou::ByteArray& result,
|
||||||
|
int wbits, int compressionLevel)
|
||||||
|
{
|
||||||
|
unsigned int dataSize = data.size();
|
||||||
|
unsigned char* buffer = new unsigned char[dataSize];
|
||||||
|
|
||||||
|
z_stream defstream;
|
||||||
|
|
||||||
|
defstream.zalloc = Z_NULL;
|
||||||
|
defstream.zfree = Z_NULL;
|
||||||
|
defstream.opaque = Z_NULL;
|
||||||
|
|
||||||
|
defstream.avail_in = (uInt)data.size();
|
||||||
|
defstream.next_in = (Bytef *)data.c_str(); // input char array
|
||||||
|
defstream.avail_out = (uInt)dataSize; // size of output
|
||||||
|
defstream.next_out = (Bytef *)buffer; // output char array
|
||||||
|
|
||||||
|
int ret = deflateInit2(&defstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits,
|
||||||
|
compressionLevel, Z_DEFAULT_STRATEGY);
|
||||||
|
|
||||||
|
ret = ::deflate(&defstream, Z_SYNC_FLUSH);
|
||||||
|
while (ret == Z_OK || ret == Z_STREAM_END)
|
||||||
|
{
|
||||||
|
result.append(buffer, dataSize-defstream.avail_out);
|
||||||
|
if (ret == Z_STREAM_END) break;
|
||||||
|
defstream.avail_out = (uInt)dataSize; // size of output
|
||||||
|
defstream.next_out = (Bytef *)buffer; // output char array
|
||||||
|
ret = ::deflate(&defstream, Z_SYNC_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
deflateEnd(&defstream);
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR)
|
||||||
|
EXCEPTION(gourou::CLIENT_ZIP_ERROR, zError(ret));
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class DRMProcessorClientImpl : public gourou::DRMProcessorClient
|
|||||||
virtual void randBytes(unsigned char* bytesOut, unsigned int length);
|
virtual void randBytes(unsigned char* bytesOut, unsigned int length);
|
||||||
|
|
||||||
/* HTTP interface */
|
/* HTTP interface */
|
||||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""));
|
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0);
|
||||||
|
|
||||||
virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
const RSA_KEY_TYPE keyType, const std::string& password,
|
const RSA_KEY_TYPE keyType, const std::string& password,
|
||||||
@@ -63,6 +63,9 @@ class DRMProcessorClientImpl : public gourou::DRMProcessorClient
|
|||||||
|
|
||||||
virtual void extractRSAPublicKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
|
virtual void extractRSAPublicKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
|
||||||
virtual void extractRSAPrivateKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
|
virtual void extractRSAPrivateKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
|
||||||
|
virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
|
const RSA_KEY_TYPE keyType, const std::string& password,
|
||||||
|
unsigned char** certOut, unsigned int* certOutLength);
|
||||||
|
|
||||||
/* Crypto interface */
|
/* Crypto interface */
|
||||||
virtual void AESEncrypt(CHAINING_MODE chaining,
|
virtual void AESEncrypt(CHAINING_MODE chaining,
|
||||||
@@ -105,6 +108,10 @@ class DRMProcessorClientImpl : public gourou::DRMProcessorClient
|
|||||||
|
|
||||||
virtual void zipClose(void* handler);
|
virtual void zipClose(void* handler);
|
||||||
|
|
||||||
|
virtual void inflate(std::string data, gourou::ByteArray& result, int wbits=-15);
|
||||||
|
|
||||||
|
virtual void deflate(std::string data, gourou::ByteArray& result,
|
||||||
|
int wbits=-15, int compressionLevel=8);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user