Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e3958f09e | ||
|
|
2dbd4cc343 | ||
|
|
e28dc39a68 | ||
|
|
7b8c7acbad | ||
|
|
56b3231f92 | ||
|
|
7084fb7025 | ||
|
|
7f5b787cb9 | ||
|
|
086e9b0610 | ||
|
|
600535d52c | ||
|
|
57c3a58994 | ||
|
|
210b265693 | ||
|
|
33bb983283 | ||
|
|
3c73b8ccb3 | ||
|
|
4acf401031 | ||
|
|
7666d2a241 | ||
|
|
201ec69b11 | ||
|
|
5e018ddbd8 | ||
|
|
81563056e0 | ||
|
|
22880c71c6 | ||
|
|
4f288f4e24 | ||
|
|
3d4e6e3918 | ||
|
|
7b6b1471fe | ||
|
|
4f9b2de5a5 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
obj
|
||||
lib
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*~
|
||||
|
||||
12
Makefile
12
Makefile
@@ -1,10 +1,9 @@
|
||||
|
||||
AR ?= $(CROSS)ar
|
||||
CXX ?= $(CROSS)g++
|
||||
|
||||
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
|
||||
|
||||
CXXFLAGS=-Wall -fPIC -I./include -I./lib -I./lib/pugixml/src/ -I./lib/updfparser/include
|
||||
CXXFLAGS=-Wall -fPIC -I./include -I./lib/pugixml/src/ -I./lib/updfparser/include
|
||||
LDFLAGS = $(UPDFPARSERLIB)
|
||||
|
||||
BUILD_STATIC ?= 0
|
||||
@@ -24,11 +23,15 @@ endif
|
||||
|
||||
|
||||
ifneq ($(DEBUG),)
|
||||
CXXFLAGS += -ggdb -O0
|
||||
CXXFLAGS += -ggdb -O0 -DDEBUG
|
||||
else
|
||||
CXXFLAGS += -O2
|
||||
endif
|
||||
|
||||
ifneq ($(STATIC_NONCE),)
|
||||
CXXFLAGS += -DSTATIC_NONCE=1
|
||||
endif
|
||||
|
||||
SRCDIR := src
|
||||
INCDIR := inc
|
||||
BUILDDIR := obj
|
||||
@@ -45,6 +48,9 @@ lib:
|
||||
mkdir lib
|
||||
./scripts/setup.sh
|
||||
|
||||
update_lib:
|
||||
./scripts/update_lib.sh
|
||||
|
||||
obj:
|
||||
mkdir obj
|
||||
|
||||
|
||||
23
README.md
23
README.md
@@ -35,8 +35,16 @@ Dependencies
|
||||
|
||||
For libgourou :
|
||||
|
||||
_externals_ :
|
||||
|
||||
* None
|
||||
|
||||
_internals_ :
|
||||
|
||||
* PugiXML
|
||||
* Base64
|
||||
* uPDFParser
|
||||
|
||||
For utils :
|
||||
|
||||
* libcurl
|
||||
@@ -44,12 +52,18 @@ For utils :
|
||||
* libzip
|
||||
|
||||
|
||||
Internal libraries are automatically fetched and statically compiled during the first run.
|
||||
When you update libgourou's repository, **don't forget to update internal libraries** with :
|
||||
|
||||
make update_lib
|
||||
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
Use _make_ command
|
||||
|
||||
make [CROSS=XXX] [DEBUG=(0*|1)] [STATIC_UTILS=(0*|1)] [BUILD_UTILS=(0|1*)] [BUILD_STATIC=(0*|1)] [BUILD_SHARED=(0|1*)]
|
||||
make [CROSS=XXX] [DEBUG=(0*|1)] [STATIC_UTILS=(0*|1)] [BUILD_UTILS=(0|1*)] [BUILD_STATIC=(0*|1)] [BUILD_SHARED=(0|1*)] [all*|clean|ultraclean|build_utils]
|
||||
|
||||
CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-)
|
||||
|
||||
@@ -104,6 +118,11 @@ To return a loaned book :
|
||||
You can get utils full options description with -h or --help switch
|
||||
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
A docker image (by bcliang) is available at [https://github.com/bcliang/docker-libgourou/](https://github.com/bcliang/docker-libgourou/)
|
||||
|
||||
|
||||
Copyright
|
||||
---------
|
||||
@@ -111,7 +130,6 @@ Copyright
|
||||
Grégory Soutadé
|
||||
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
@@ -120,7 +138,6 @@ libgourou : LGPL v3 or later
|
||||
utils : BSD
|
||||
|
||||
|
||||
|
||||
Special thanks
|
||||
--------------
|
||||
|
||||
|
||||
124
include/Base64.h
Normal file
124
include/Base64.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#ifndef _MACARON_BASE64_H_
|
||||
#define _MACARON_BASE64_H_
|
||||
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2016 tomykaira
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace macaron {
|
||||
|
||||
class Base64 {
|
||||
public:
|
||||
|
||||
static std::string Encode(const std::string data) {
|
||||
static constexpr char sEncodingTable[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
size_t in_len = data.size();
|
||||
size_t out_len = 4 * ((in_len + 2) / 3);
|
||||
std::string ret(out_len, '\0');
|
||||
size_t i;
|
||||
char *p = const_cast<char*>(ret.c_str());
|
||||
|
||||
for (i = 0; i < in_len - 2; i += 3) {
|
||||
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)];
|
||||
*p++ = sEncodingTable[data[i + 2] & 0x3F];
|
||||
}
|
||||
if (i < in_len) {
|
||||
*p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
|
||||
if (i == (in_len - 1)) {
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4)];
|
||||
*p++ = '=';
|
||||
}
|
||||
else {
|
||||
*p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
*p++ = '=';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string Decode(const std::string& input, std::string& out) {
|
||||
static constexpr unsigned char kDecodingTable[] = {
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
|
||||
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
|
||||
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
|
||||
};
|
||||
|
||||
size_t in_len = input.size();
|
||||
if (in_len % 4 != 0) return "Input data size is not a multiple of 4";
|
||||
|
||||
size_t out_len = in_len / 4 * 3;
|
||||
if (input[in_len - 1] == '=') out_len--;
|
||||
if (input[in_len - 2] == '=') out_len--;
|
||||
|
||||
out.resize(out_len);
|
||||
|
||||
for (size_t i = 0, j = 0; i < in_len;) {
|
||||
uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast<int>(input[i++])];
|
||||
|
||||
uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
|
||||
|
||||
if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF;
|
||||
if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF;
|
||||
if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* _MACARON_BASE64_H_ */
|
||||
@@ -104,6 +104,13 @@ namespace gourou
|
||||
*/
|
||||
std::string toBase64();
|
||||
|
||||
/**
|
||||
* @brief Convert hex string into bytes
|
||||
*
|
||||
* @param str Hex string
|
||||
*/
|
||||
static ByteArray fromHex(const std::string& str);
|
||||
|
||||
/**
|
||||
* @brief Return a string with human readable hex encoded internal data
|
||||
*/
|
||||
@@ -130,7 +137,7 @@ namespace gourou
|
||||
void append(const std::string& str);
|
||||
|
||||
/**
|
||||
* @brief Get internal data. Must bot be freed
|
||||
* @brief Get internal data. Must not be freed
|
||||
*/
|
||||
unsigned char* data() {return _data;}
|
||||
|
||||
|
||||
@@ -47,20 +47,16 @@ namespace gourou
|
||||
* @param handler Digest handler
|
||||
* @param data Data to digest
|
||||
* @param length Length of data
|
||||
*
|
||||
* @return OK/KO
|
||||
*/
|
||||
virtual int digestUpdate(void* handler, unsigned char* data, unsigned int length) = 0;
|
||||
virtual void digestUpdate(void* handler, unsigned char* data, unsigned int length) = 0;
|
||||
|
||||
/**
|
||||
* @brief Finalize digest with remained buffered data and destroy handler
|
||||
*
|
||||
* @param handler Digest handler
|
||||
* @param digestOut Digest result (buffer must be pre allocated with right size)
|
||||
*
|
||||
* @return OK/KO
|
||||
*/
|
||||
virtual int digestFinalize(void* handler, unsigned char* digestOut) = 0;
|
||||
virtual void digestFinalize(void* handler, unsigned char* digestOut) = 0;
|
||||
|
||||
/**
|
||||
* @brief Global digest function
|
||||
@@ -69,10 +65,8 @@ namespace gourou
|
||||
* @param data Data to digest
|
||||
* @param length Length of data
|
||||
* @param digestOut Digest result (buffer must be pre allocated with right size)
|
||||
*
|
||||
* @return OK/KO
|
||||
*/
|
||||
virtual int digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut) = 0;
|
||||
virtual void digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut) = 0;
|
||||
};
|
||||
|
||||
class RandomInterface
|
||||
@@ -111,6 +105,7 @@ namespace gourou
|
||||
public:
|
||||
enum RSA_KEY_TYPE {
|
||||
RSA_KEY_PKCS12 = 0,
|
||||
RSA_KEY_PKCS8,
|
||||
RSA_KEY_X509
|
||||
};
|
||||
|
||||
@@ -238,7 +233,7 @@ namespace gourou
|
||||
* @param dataOut Encrypted data
|
||||
* @param dataOutLength Length of encrypted data
|
||||
*/
|
||||
virtual void Encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength,
|
||||
const unsigned char* dataIn, unsigned int dataInLength,
|
||||
@@ -255,7 +250,7 @@ namespace gourou
|
||||
*
|
||||
* @return AES handler
|
||||
*/
|
||||
virtual void* EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void* encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv=0, unsigned int ivLength=0) = 0;
|
||||
|
||||
@@ -268,7 +263,7 @@ namespace gourou
|
||||
* @param dataOut Encrypted data
|
||||
* @param dataOutLength Length of encrypted data
|
||||
*/
|
||||
virtual void EncryptUpdate(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;
|
||||
|
||||
/**
|
||||
@@ -279,7 +274,7 @@ namespace gourou
|
||||
* @param dataOut Last block of encrypted data
|
||||
* @param dataOutLength Length of encrypted data
|
||||
*/
|
||||
virtual void EncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||
virtual void encryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||
|
||||
/**
|
||||
* @brief Do decryption. If length of data is not multiple of block size, PKCS#5 padding is done
|
||||
@@ -295,7 +290,7 @@ namespace gourou
|
||||
* @param dataOut Encrypted data
|
||||
* @param dataOutLength Length of encrypted data
|
||||
*/
|
||||
virtual void Decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength,
|
||||
const unsigned char* dataIn, unsigned int dataInLength,
|
||||
@@ -312,7 +307,7 @@ namespace gourou
|
||||
*
|
||||
* @return AES handler
|
||||
*/
|
||||
virtual void* DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void* decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv=0, unsigned int ivLength=0) = 0;
|
||||
|
||||
@@ -325,7 +320,7 @@ namespace gourou
|
||||
* @param dataOut Decrypted data
|
||||
* @param dataOutLength Length of decrypted data
|
||||
*/
|
||||
virtual void DecryptUpdate(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;
|
||||
/**
|
||||
* @brief Finalize decryption (decrypt last block and remove padding if it is set).
|
||||
@@ -335,7 +330,7 @@ namespace gourou
|
||||
* @param dataOut Last block decrypted data
|
||||
* @param dataOutLength Length of decrypted data
|
||||
*/
|
||||
virtual void DecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||
virtual void decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
||||
#endif
|
||||
|
||||
#define LIBGOUROU_VERSION "0.7"
|
||||
#define LIBGOUROU_VERSION "0.8"
|
||||
|
||||
namespace gourou
|
||||
{
|
||||
@@ -231,6 +231,7 @@ namespace gourou
|
||||
void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate);
|
||||
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
||||
const std::string& operatorURL);
|
||||
std::string encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType, ITEM_TYPE type);
|
||||
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,
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace gourou
|
||||
CLIENT_INVALID_PKCS12,
|
||||
CLIENT_INVALID_CERTIFICATE,
|
||||
CLIENT_NO_PRIV_KEY,
|
||||
CLIENT_NO_PUB_KEY,
|
||||
CLIENT_RSA_ERROR,
|
||||
CLIENT_BAD_CHAINING,
|
||||
CLIENT_BAD_KEY_SIZE,
|
||||
@@ -114,7 +115,10 @@ namespace gourou
|
||||
CLIENT_GENERIC_EXCEPTION,
|
||||
CLIENT_NETWORK_ERROR,
|
||||
CLIENT_INVALID_PKCS8,
|
||||
CLIENT_FILE_ERROR
|
||||
CLIENT_FILE_ERROR,
|
||||
CLIENT_OSSL_ERROR,
|
||||
CLIENT_CRYPT_ERROR,
|
||||
CLIENT_DIGEST_ERROR,
|
||||
};
|
||||
|
||||
enum DRM_REMOVAL_ERROR {
|
||||
@@ -124,7 +128,8 @@ namespace gourou
|
||||
DRM_FORMAT_NOT_SUPPORTED,
|
||||
DRM_IN_OUT_EQUALS,
|
||||
DRM_MISSING_PARAMETER,
|
||||
DRM_INVALID_KEY_SIZE
|
||||
DRM_INVALID_KEY_SIZE,
|
||||
DRM_ERR_ENCRYPTION_KEY_FP
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -139,7 +144,7 @@ namespace gourou
|
||||
std::stringstream msg;
|
||||
msg << "Exception code : 0x" << std::setbase(16) << code << std::endl;
|
||||
msg << "Message : " << message << std::endl;
|
||||
if (logLevel >= DEBUG)
|
||||
if (logLevel >= LG_LOG_DEBUG)
|
||||
msg << "File : " << file << ":" << std::setbase(10) << line << std::endl;
|
||||
fullmessage = strdup(msg.str().c_str());
|
||||
}
|
||||
@@ -223,35 +228,9 @@ namespace gourou
|
||||
* It can throw an exception if tag does not exists
|
||||
* or just return an empty value
|
||||
*/
|
||||
static inline std::string extractTextElem(const pugi::xml_document& doc, const char* tagName, bool throwOnNull=true)
|
||||
static inline std::string extractTextElem(const pugi::xml_node& root, 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);
|
||||
}
|
||||
|
||||
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);
|
||||
pugi::xpath_node xpath_node = root.select_node(tagName);
|
||||
|
||||
if (!xpath_node)
|
||||
{
|
||||
@@ -275,6 +254,37 @@ namespace gourou
|
||||
return trim(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract text attribute from tag in document
|
||||
* It can throw an exception if attribute does not exists
|
||||
* or just return an empty value
|
||||
*/
|
||||
static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true)
|
||||
{
|
||||
pugi::xpath_node xpath_node = root.select_node(tagName);
|
||||
|
||||
if (!xpath_node)
|
||||
{
|
||||
if (throwOnNull)
|
||||
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
pugi::xml_attribute attr = xpath_node.node().attribute(attributeName);
|
||||
|
||||
if (!attr)
|
||||
{
|
||||
if (throwOnNull)
|
||||
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Attribute element " << attributeName << " for tag " << tagName << " not found");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string res = attr.value();
|
||||
return trim(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append an element to root with a sub text element
|
||||
*
|
||||
@@ -288,6 +298,29 @@ namespace gourou
|
||||
node.append_child(pugi::node_pcdata).set_value(value.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove "urn:uuid:" prefix and all '-' from uuid
|
||||
* urn:uuid:9cb786e8-586a-4950-8901-fff8d2ee6025
|
||||
* ->
|
||||
* 9cb786e8586a49508901fff8d2ee6025
|
||||
*/
|
||||
static inline std::string extractIdFromUUID(const std::string& uuid)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
std::string res;
|
||||
|
||||
if (uuid.find("urn:uuid:") == 0)
|
||||
i = 9;
|
||||
|
||||
for(; i<uuid.size(); i++)
|
||||
{
|
||||
if (uuid[i] != '-')
|
||||
res += uuid[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open a file descriptor on path. If it already exists and truncate == true, it's truncated
|
||||
*
|
||||
@@ -413,6 +446,20 @@ namespace gourou
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dumpBuffer(GOUROU_LOG_LEVEL level, const char* title, const unsigned char* data, unsigned int len)
|
||||
{
|
||||
if (gourou::logLevel < level)
|
||||
return;
|
||||
|
||||
printf("%s", title);
|
||||
for(unsigned int i=0; i<len; i++)
|
||||
{
|
||||
if (i && !(i%16)) printf("\n");
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,16 +24,16 @@
|
||||
|
||||
namespace gourou {
|
||||
enum GOUROU_LOG_LEVEL {
|
||||
ERROR,
|
||||
WARN,
|
||||
INFO,
|
||||
DEBUG,
|
||||
TRACE
|
||||
LG_LOG_ERROR,
|
||||
LG_LOG_WARN,
|
||||
LG_LOG_INFO,
|
||||
LG_LOG_DEBUG,
|
||||
LG_LOG_TRACE
|
||||
};
|
||||
|
||||
extern GOUROU_LOG_LEVEL logLevel;
|
||||
|
||||
#define GOUROU_LOG(__lvl, __msg) if (__lvl <= gourou::logLevel) {std::cout << __msg << std::endl << std::flush;}
|
||||
#define GOUROU_LOG(__lvl, __msg) if (gourou::LG_LOG_##__lvl <= gourou::logLevel) {std::cout << __msg << std::endl << std::flush;}
|
||||
#define GOUROU_LOG_FUNC() GOUROU_LOG(TRACE, __FUNCTION__ << "() @ " << __FILE__ << ":" << __LINE__)
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,11 +8,6 @@ if [ ! -d lib/pugixml ] ; then
|
||||
popd
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
18
scripts/update_lib.sh
Executable file
18
scripts/update_lib.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d lib/pugixml -o ! -d lib/updfparser ] ; then
|
||||
echo "Some libraries are missing"
|
||||
echo "You must run this script at the top of libgourou working direcotry."
|
||||
echo "./lib/setup.sh must be called first (make all)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Pugixml
|
||||
pushd lib/pugixml
|
||||
git pull origin latest
|
||||
popd
|
||||
|
||||
# uPDFParser
|
||||
pushd lib/updfparser
|
||||
git pull origin master
|
||||
make clean all BUILD_STATIC=1 BUILD_SHARED=0
|
||||
@@ -17,8 +17,9 @@
|
||||
along with libgourou. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <base64/Base64.h>
|
||||
#include <Base64.h>
|
||||
|
||||
#include <bytearray.h>
|
||||
|
||||
@@ -155,6 +156,47 @@ namespace gourou
|
||||
return macaron::Base64::Encode(std::string((char*)_data, _length));
|
||||
}
|
||||
|
||||
ByteArray ByteArray::fromHex(const std::string& str)
|
||||
{
|
||||
if (str.size() % 2)
|
||||
throw std::invalid_argument("Size of hex string not multiple of 2");
|
||||
|
||||
ByteArray res((unsigned int)(str.size()/2));
|
||||
unsigned int i;
|
||||
|
||||
unsigned char* data = res.data();
|
||||
unsigned char cur, tmp;
|
||||
|
||||
for (i=0; i<str.size(); i+=2)
|
||||
{
|
||||
cur = 0;
|
||||
|
||||
tmp = str[i];
|
||||
if (tmp >= 'a' && tmp <= 'f')
|
||||
cur = (tmp - 'a' + 10) << 4;
|
||||
else if (tmp >= 'A' && tmp <= 'F')
|
||||
cur = (tmp - 'A' + 10) << 4;
|
||||
else if (tmp >= '0' && tmp <= '9')
|
||||
cur = (tmp - '0') << 4;
|
||||
else
|
||||
throw std::invalid_argument("Invalid character in hex string");
|
||||
|
||||
tmp = str[i+1];
|
||||
if (tmp >= 'a' && tmp <= 'f')
|
||||
cur += tmp - 'a' + 10;
|
||||
else if (tmp >= 'A' && tmp <= 'F')
|
||||
cur += tmp - 'A' + 10;
|
||||
else if (tmp >= '0' && tmp <= '9')
|
||||
cur += tmp - '0';
|
||||
else
|
||||
throw std::invalid_argument("Invalid character in hex string");
|
||||
|
||||
data[i/2] = cur;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string ByteArray::toHex()
|
||||
{
|
||||
char* tmp = new char[_length*2+1];
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
namespace gourou
|
||||
{
|
||||
GOUROU_LOG_LEVEL logLevel = WARN;
|
||||
GOUROU_LOG_LEVEL logLevel = LG_LOG_WARN;
|
||||
const std::string DRMProcessor::VERSION = LIBGOUROU_VERSION;
|
||||
|
||||
DRMProcessor::DRMProcessor(DRMProcessorClient* client):client(client), device(0), user(0)
|
||||
@@ -89,7 +89,7 @@ namespace gourou
|
||||
uint16_t nlength = htons(length);
|
||||
char c;
|
||||
|
||||
if (logLevel >= TRACE)
|
||||
if (logLevel >= LG_LOG_TRACE)
|
||||
printf("%02x %02x ", ((uint8_t*)&nlength)[0], ((uint8_t*)&nlength)[1]);
|
||||
|
||||
client->digestUpdate(sha_ctx, (unsigned char*)&nlength, sizeof(nlength));
|
||||
@@ -98,17 +98,17 @@ namespace gourou
|
||||
{
|
||||
c = string[i];
|
||||
client->digestUpdate(sha_ctx, (unsigned char*)&c, 1);
|
||||
if (logLevel >= TRACE)
|
||||
if (logLevel >= LG_LOG_TRACE)
|
||||
printf("%c", c);
|
||||
}
|
||||
if (logLevel >= TRACE)
|
||||
if (logLevel >= LG_LOG_TRACE)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void DRMProcessor::pushTag(void* sha_ctx, uint8_t tag)
|
||||
{
|
||||
client->digestUpdate(sha_ctx, &tag, sizeof(tag));
|
||||
if (logLevel >= TRACE)
|
||||
if (logLevel >= LG_LOG_TRACE)
|
||||
printf("%02x ", tag);
|
||||
}
|
||||
|
||||
@@ -226,14 +226,7 @@ namespace gourou
|
||||
|
||||
client->digestFinalize(sha_ctx, sha_out);
|
||||
|
||||
if (logLevel >= DEBUG)
|
||||
{
|
||||
printf("\nSHA OUT : ");
|
||||
for(int i=0; i<(int)SHA1_LEN; i++)
|
||||
printf("%02x ", sha_out[i]);
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "\nSHA OUT : ", sha_out, SHA1_LEN);
|
||||
}
|
||||
|
||||
void DRMProcessor::signNode(pugi::xml_node& rootNode)
|
||||
@@ -252,13 +245,8 @@ namespace gourou
|
||||
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");
|
||||
}
|
||||
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "Sig : ", res, sizeof(res));
|
||||
|
||||
std::string signature = ByteArray(res, sizeof(res)).toBase64();
|
||||
appendTextElem(rootNode, "adept:signature", signature);
|
||||
@@ -282,7 +270,11 @@ namespace gourou
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
uint32_t nonce32[2] = {0x6f046000, 0x388a};
|
||||
#ifdef STATIC_NONCE
|
||||
uint64_t bigtime = 0xAA001122BBCCAAULL;
|
||||
#else
|
||||
uint64_t bigtime = tv.tv_sec*1000;
|
||||
#endif
|
||||
nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);
|
||||
nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);
|
||||
|
||||
@@ -877,7 +869,7 @@ namespace gourou
|
||||
// Generate IV in front
|
||||
client->randBytes(encrypted_data, 16);
|
||||
|
||||
client->Encrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
client->encrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
deviceKey, 16, encrypted_data, 16,
|
||||
data, len,
|
||||
encrypted_data+16, &outLen);
|
||||
@@ -896,7 +888,7 @@ namespace gourou
|
||||
const unsigned char* deviceKey = device->getDeviceKey();
|
||||
unsigned char* decrypted_data = new unsigned char[len-16];
|
||||
|
||||
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
deviceKey, 16, data, 16,
|
||||
data+16, len-16,
|
||||
decrypted_data, &outLen);
|
||||
@@ -959,22 +951,102 @@ namespace gourou
|
||||
|
||||
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();
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length());
|
||||
|
||||
client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(),
|
||||
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
|
||||
RSAInterface::RSA_KEY_PKCS8, "",
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA Key can be over encrypted with AES128-CBC if keyType attribute is set
|
||||
* For EPUB, Key = SHA256(keyType)[14:22] || SHA256(keyType)[7:13]
|
||||
* For PDF, Key = SHA256(keyType)[6:19] || SHA256(keyType)[3:6]
|
||||
* IV = DeviceID ^ FulfillmentId ^ VoucherId
|
||||
*
|
||||
* @return Base64 encoded decrypted key
|
||||
*/
|
||||
std::string DRMProcessor::encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType, ITEM_TYPE type)
|
||||
{
|
||||
unsigned char digest[32], key[16], iv[16];
|
||||
unsigned int dataOutLength;
|
||||
std::string id;
|
||||
|
||||
client->digest("SHA256", (unsigned char*)keyType.c_str(), keyType.size(), digest);
|
||||
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "SHA of KeyType : ", digest, sizeof(digest));
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case EPUB:
|
||||
memcpy(key, &digest[14], 9);
|
||||
memcpy(&key[9], &digest[7], 7);
|
||||
break;
|
||||
case PDF:
|
||||
memcpy(key, &digest[6], 13);
|
||||
memcpy(&key[13], &digest[3], 3);
|
||||
break;
|
||||
}
|
||||
|
||||
id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/device");
|
||||
if (id == "")
|
||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Device id not found in rights.xml");
|
||||
ByteArray deviceId = ByteArray::fromHex(extractIdFromUUID(id));
|
||||
unsigned char* _deviceId = deviceId.data();
|
||||
|
||||
id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/fulfillment");
|
||||
if (id == "")
|
||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Fulfillment id not found in rights.xml");
|
||||
ByteArray fulfillmentId = ByteArray::fromHex(extractIdFromUUID(id));
|
||||
unsigned char* _fulfillmentId = fulfillmentId.data();
|
||||
|
||||
id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/voucher");
|
||||
if (id == "")
|
||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Voucher id not found in rights.xml");
|
||||
ByteArray voucherId = ByteArray::fromHex(extractIdFromUUID(id));
|
||||
unsigned char* _voucherId = voucherId.data();
|
||||
|
||||
if (deviceId.size() < sizeof(iv) || fulfillmentId.size() < sizeof(iv) || voucherId.size() < sizeof(iv))
|
||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "One id has a bad length");
|
||||
|
||||
for(unsigned int i=0; i<sizeof(iv); i++)
|
||||
iv[i] = _deviceId[i] ^ _fulfillmentId[i] ^ _voucherId[i];
|
||||
|
||||
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
||||
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass key : ", key, sizeof(key));
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass IV : ", iv, sizeof(iv));
|
||||
|
||||
unsigned char* clearRSAKey = new unsigned char[arrayEncryptedKey.size()];
|
||||
|
||||
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
(const unsigned char*)key, (unsigned int)sizeof(key),
|
||||
(const unsigned char*)iv, (unsigned int)sizeof(iv),
|
||||
(const unsigned char*)arrayEncryptedKey.data(), arrayEncryptedKey.size(),
|
||||
(unsigned char*)clearRSAKey, &dataOutLength);
|
||||
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "\nDecrypted key : ", clearRSAKey, dataOutLength);
|
||||
|
||||
/* Last block could be 0x10*16 which is OpenSSL padding, remove it if it's the case */
|
||||
bool skipLastLine = true;
|
||||
for(unsigned int i=dataOutLength-16; i<dataOutLength; i++)
|
||||
{
|
||||
if (clearRSAKey[i] != 0x10)
|
||||
{
|
||||
skipLastLine = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ByteArray res(clearRSAKey, (skipLastLine)?dataOutLength-16:dataOutLength);
|
||||
|
||||
delete[] clearRSAKey;
|
||||
|
||||
return res.toBase64();
|
||||
}
|
||||
|
||||
void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||
@@ -991,7 +1063,20 @@ namespace gourou
|
||||
unsigned char decryptedKey[RSA_KEY_SIZE];
|
||||
|
||||
if (!encryptionKey)
|
||||
{
|
||||
std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);
|
||||
|
||||
if (keyType != "")
|
||||
encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType, EPUB);
|
||||
|
||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", decryptedKey, RSA_KEY_SIZE);
|
||||
|
||||
if (decryptedKey[0] != 0x00 || decryptedKey[1] != 0x02 ||
|
||||
decryptedKey[RSA_KEY_SIZE-16-1] != 0x00)
|
||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
|
||||
}
|
||||
else
|
||||
{
|
||||
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
||||
@@ -1037,7 +1122,7 @@ namespace gourou
|
||||
gourou::ByteArray inflateData(true);
|
||||
unsigned int dataOutLength;
|
||||
|
||||
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||
decryptedKey+sizeof(decryptedKey)-16, 16, /* Key */
|
||||
_data, 16, /* IV */
|
||||
&_data[16], zipData.length()-16,
|
||||
@@ -1173,7 +1258,20 @@ namespace gourou
|
||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||
|
||||
if (!encryptionKey)
|
||||
{
|
||||
std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);
|
||||
|
||||
if (keyType != "")
|
||||
encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType, PDF);
|
||||
|
||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||
|
||||
dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", decryptedKey, RSA_KEY_SIZE);
|
||||
|
||||
if (decryptedKey[0] != 0x00 || decryptedKey[1] != 0x02 ||
|
||||
decryptedKey[RSA_KEY_SIZE-16-1] != 0x00)
|
||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
|
||||
}
|
||||
else
|
||||
{
|
||||
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
||||
@@ -1241,8 +1339,8 @@ namespace gourou
|
||||
|
||||
GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength);
|
||||
|
||||
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||
tmpKey, 16, /* Key */
|
||||
client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||
tmpKey, sizeof(tmpKey), /* Key */
|
||||
NULL, 0, /* IV */
|
||||
encryptedData, dataLength,
|
||||
clearData, &dataOutLength);
|
||||
@@ -1274,8 +1372,8 @@ namespace gourou
|
||||
|
||||
GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength());
|
||||
|
||||
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||
tmpKey, 16, /* Key */
|
||||
client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||
tmpKey, sizeof(tmpKey), /* Key */
|
||||
NULL, 0, /* IV */
|
||||
encryptedData, dataLength,
|
||||
clearData, &dataOutLength);
|
||||
|
||||
@@ -31,10 +31,23 @@ namespace gourou
|
||||
|
||||
node = doc.select_node("/envelope/loanToken/loan").node();
|
||||
|
||||
if (!node)
|
||||
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/loan element in document");
|
||||
|
||||
if (node)
|
||||
properties["id"] = node.first_child().value();
|
||||
else
|
||||
{
|
||||
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/display/loan").node();
|
||||
|
||||
if (node)
|
||||
properties["id"] = node.first_child().value();
|
||||
else
|
||||
{
|
||||
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/play/loan").node();
|
||||
if (node)
|
||||
properties["id"] = node.first_child().value();
|
||||
else
|
||||
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/loan element in document");
|
||||
}
|
||||
}
|
||||
|
||||
node = doc.select_node("/envelope/loanToken/operatorURL").node();
|
||||
|
||||
@@ -50,6 +63,7 @@ namespace gourou
|
||||
else
|
||||
{
|
||||
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/play/until").node();
|
||||
|
||||
if (node)
|
||||
properties["validity"] = node.first_child().value();
|
||||
else
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
TARGETS=acsmdownloader adept_activate adept_remove adept_loan_mgt
|
||||
TARGETS=acsmdownloader adept_activate adept_remove adept_loan_mgt launcher
|
||||
|
||||
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
|
||||
|
||||
STATIC_DEP=
|
||||
LDFLAGS=-L$(ROOT) -lcrypto -lzip -lz -lcurl
|
||||
LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl
|
||||
|
||||
ifneq ($(STATIC_UTILS),)
|
||||
STATIC_DEP = $(ROOT)/libgourou.a
|
||||
@@ -13,11 +13,12 @@ LDFLAGS += -lgourou
|
||||
endif
|
||||
|
||||
ifneq ($(DEBUG),)
|
||||
CXXFLAGS += -ggdb -O0
|
||||
CXXFLAGS += -ggdb -O0 -DDEBUG
|
||||
else
|
||||
CXXFLAGS += -O2
|
||||
endif
|
||||
|
||||
|
||||
COMMON_DEPS = drmprocessorclientimpl.cpp utils_common.cpp
|
||||
COMMON_OBJECTS = $(COMMON_DEPS:.cpp=.o)
|
||||
COMMON_LIB = utils.a
|
||||
@@ -28,19 +29,10 @@ ${COMMON_LIB}: ${COMMON_DEPS} ${STATIC_DEP}
|
||||
$(CXX) $(CXXFLAGS) ${COMMON_DEPS} $(LDFLAGS) -c
|
||||
$(AR) crs $@ ${COMMON_OBJECTS} $(STATIC_DEP)
|
||||
|
||||
acsmdownloader: acsmdownloader.cpp ${COMMON_LIB}
|
||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||
|
||||
adept_activate: adept_activate.cpp ${COMMON_LIB}
|
||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||
|
||||
adept_remove: adept_remove.cpp ${COMMON_LIB}
|
||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||
|
||||
adept_loan_mgt: adept_loan_mgt.cpp ${COMMON_LIB}
|
||||
%: %.cpp ${COMMON_LIB}
|
||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS)
|
||||
rm -f $(TARGETS) $(COMMON_LIB)
|
||||
|
||||
ultraclean: clean
|
||||
|
||||
@@ -184,7 +184,7 @@ static void usage(const char* cmd)
|
||||
{
|
||||
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] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-r|--resume)] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << std::endl;
|
||||
std::cout << "Usage: " << basename((char*)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)] [(-r|--resume)] [(-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 << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
|
||||
|
||||
@@ -126,7 +126,7 @@ static void usage(const char* cmd)
|
||||
{
|
||||
std::cout << "Create new device files used by ADEPT DRM" << 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 << "Usage: " << basename((char*)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;
|
||||
|
||||
@@ -336,7 +336,7 @@ static void usage(const char* cmd)
|
||||
{
|
||||
std::cout << "Manage loaned books" << std::endl;
|
||||
|
||||
std::cout << "Usage: " << cmd << " [(-d|--activation-dir) dir] (-l|--list)|(-D|--delete loanID)|(-R|--delete loanID) [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
|
||||
std::cout << "Usage: " << basename((char*)cmd) << " [(-d|--activation-dir) dir] (-l|--list)|(-D|--delete loanID)|(-R|--delete loanID) [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
|
||||
|
||||
std::cout << " " << "-d|--activation-dir" << "\t" << "Directory of device.xml/activation.xml and device key" << std::endl;
|
||||
std::cout << " " << "-l|--list" << "\t\t" << "List all loaned books" << std::endl;
|
||||
|
||||
@@ -114,16 +114,16 @@ public:
|
||||
// Use temp file for PDF
|
||||
if (type == gourou::DRMProcessor::ITEM_TYPE::PDF)
|
||||
{
|
||||
char* tempFile = tempnam("/tmp", NULL);
|
||||
processor.removeDRM(inputFile, tempFile, type, encryptionKey, encryptionKeySize);
|
||||
std::string tempFile = filename + ".tmp";
|
||||
/* Be sure there is not already a temp file */
|
||||
unlink(tempFile.c_str());
|
||||
processor.removeDRM(filename, tempFile, type, encryptionKey, encryptionKeySize);
|
||||
/* Original file must be removed before doing a copy... */
|
||||
unlink(inputFile);
|
||||
if (!rename(tempFile, filename.c_str()))
|
||||
unlink(filename.c_str());
|
||||
if (rename(tempFile.c_str(), filename.c_str()))
|
||||
{
|
||||
free(tempFile);
|
||||
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile << " into " << filename);
|
||||
}
|
||||
free(tempFile);
|
||||
}
|
||||
else
|
||||
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||
@@ -143,7 +143,7 @@ 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 << "Usage: " << basename((char*)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;
|
||||
|
||||
@@ -31,10 +31,14 @@
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
|
||||
#define OPENSSL_NO_DEPRECATED 1
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
@@ -42,9 +46,33 @@
|
||||
#include <zip.h>
|
||||
|
||||
#include <libgourou_common.h>
|
||||
#include <libgourou_log.h>
|
||||
#include "drmprocessorclientimpl.h"
|
||||
|
||||
DRMProcessorClientImpl::DRMProcessorClientImpl():
|
||||
legacy(0), deflt(0)
|
||||
{
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
legacy = OSSL_PROVIDER_load(NULL, "legacy");
|
||||
if (!legacy)
|
||||
EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL legacy provider not available");
|
||||
|
||||
deflt = OSSL_PROVIDER_load(NULL, "default");
|
||||
if (!deflt)
|
||||
EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
DRMProcessorClientImpl::~DRMProcessorClientImpl()
|
||||
{
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
if (legacy)
|
||||
OSSL_PROVIDER_unload(legacy);
|
||||
|
||||
if (deflt)
|
||||
OSSL_PROVIDER_unload(deflt);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Digest interface */
|
||||
void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
|
||||
{
|
||||
@@ -54,32 +82,32 @@ void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
|
||||
if (EVP_DigestInit(md_ctx, md) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(md_ctx);
|
||||
return 0;
|
||||
EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
return md_ctx;
|
||||
}
|
||||
|
||||
int DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length)
|
||||
void DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length)
|
||||
{
|
||||
return (EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length)) ? 0 : -1;
|
||||
if (EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length) != 1)
|
||||
EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
int DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut)
|
||||
void DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut)
|
||||
{
|
||||
int res = EVP_DigestFinal((EVP_MD_CTX *)handler, digestOut, NULL);
|
||||
EVP_MD_CTX_free((EVP_MD_CTX *)handler);
|
||||
return (res == 1) ? 0 : -1;
|
||||
|
||||
if (res <= 0)
|
||||
EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
int DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut)
|
||||
void DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut)
|
||||
{
|
||||
void* handler = createDigest(digestName);
|
||||
if (!handler)
|
||||
return -1;
|
||||
if (digestUpdate(handler, data, length))
|
||||
return -1;
|
||||
return digestFinalize(handler, digestOut);
|
||||
digestUpdate(handler, data, length);
|
||||
digestFinalize(handler, digestOut);
|
||||
}
|
||||
|
||||
/* Random interface */
|
||||
@@ -97,7 +125,7 @@ static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
|
||||
curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
// For "big" files only
|
||||
if (dltotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN)
|
||||
if (dltotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::LG_LOG_WARN)
|
||||
{
|
||||
int percent = 0;
|
||||
if (dltotal)
|
||||
@@ -147,7 +175,7 @@ static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userda
|
||||
|
||||
(*responseHeaders)[key] = value;
|
||||
|
||||
if (gourou::logLevel >= gourou::DEBUG)
|
||||
if (gourou::logLevel >= gourou::LG_LOG_DEBUG)
|
||||
std::cout << key << " : " << value << std::endl;
|
||||
}
|
||||
|
||||
@@ -162,10 +190,10 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
||||
if (!responseHeaders)
|
||||
responseHeaders = &localHeaders;
|
||||
|
||||
GOUROU_LOG(gourou::INFO, "Send request to " << URL);
|
||||
GOUROU_LOG(INFO, "Send request to " << URL);
|
||||
if (POSTData.size())
|
||||
{
|
||||
GOUROU_LOG(gourou::DEBUG, "<<< " << std::endl << POSTData);
|
||||
GOUROU_LOG(DEBUG, "<<< " << std::endl << POSTData);
|
||||
}
|
||||
|
||||
unsigned prevDownloadedBytes;
|
||||
@@ -175,11 +203,11 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
||||
struct stat _stat;
|
||||
if (!fstat(fd, &_stat))
|
||||
{
|
||||
GOUROU_LOG(gourou::WARN, "Resume download @ " << _stat.st_size << " bytes");
|
||||
GOUROU_LOG(WARN, "Resume download @ " << _stat.st_size << " bytes");
|
||||
downloadedBytes = _stat.st_size;
|
||||
}
|
||||
else
|
||||
GOUROU_LOG(gourou::WARN, "Want to resume, but fstat failed");
|
||||
GOUROU_LOG(WARN, "Want to resume, but fstat failed");
|
||||
}
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
@@ -235,7 +263,7 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
||||
// Connexion failed, wait & retry
|
||||
if (res == CURLE_COULDNT_CONNECT)
|
||||
{
|
||||
GOUROU_LOG(gourou::WARN, "\nConnection failed, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
GOUROU_LOG(WARN, "\nConnection failed, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
}
|
||||
// Transfer failed but some data has been received
|
||||
// --> try again without incrementing tries
|
||||
@@ -243,11 +271,11 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
||||
{
|
||||
if (prevDownloadedBytes != downloadedBytes)
|
||||
{
|
||||
GOUROU_LOG(gourou::WARN, "\nConnection broken, but data received, try again");
|
||||
GOUROU_LOG(WARN, "\nConnection broken, but data received, try again");
|
||||
i--;
|
||||
}
|
||||
else
|
||||
GOUROU_LOG(gourou::WARN, "\nConnection broken and no data received, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
GOUROU_LOG(WARN, "\nConnection broken and no data received, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
}
|
||||
// Other error --> fail
|
||||
else
|
||||
@@ -264,46 +292,79 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
||||
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));
|
||||
|
||||
if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&
|
||||
gourou::logLevel >= gourou::WARN)
|
||||
gourou::logLevel >= gourou::LG_LOG_WARN)
|
||||
std::cout << std::endl;
|
||||
|
||||
if ((*responseHeaders)["Content-Type"] == "application/vnd.adobe.adept+xml")
|
||||
{
|
||||
GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data());
|
||||
GOUROU_LOG(DEBUG, ">>> " << std::endl << replyData.data());
|
||||
}
|
||||
|
||||
return std::string((char*)replyData.data(), replyData.length());
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::padWithPKCS1(unsigned char* out, unsigned int outLength,
|
||||
const unsigned char* in, unsigned int inLength)
|
||||
{
|
||||
if (outLength < (inLength + 3))
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding");
|
||||
|
||||
/*
|
||||
PKCS1v5 Padding is :
|
||||
0x00 0x01 0xff * n 0x00 dataIn
|
||||
*/
|
||||
|
||||
memset(out, 0xFF, outLength);
|
||||
|
||||
out[0] = 0x0;
|
||||
out[1] = 0x1;
|
||||
out[outLength - inLength - 1] = 0x00;
|
||||
memcpy(&out[outLength - inLength], in, inLength);
|
||||
}
|
||||
|
||||
|
||||
void DRMProcessorClientImpl::RSAPrivateEncrypt(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)
|
||||
{
|
||||
PKCS12 * pkcs12;
|
||||
EVP_PKEY* pkey;
|
||||
X509* cert;
|
||||
STACK_OF(X509)* ca;
|
||||
RSA * rsa;
|
||||
EVP_PKEY_CTX *ctx;
|
||||
EVP_PKEY* pkey = NULL;
|
||||
size_t outlen;
|
||||
unsigned char* tmp;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||
|
||||
int ret = RSA_private_encrypt(dataLength, data, res, rsa, RSA_PKCS1_PADDING);
|
||||
if (PKCS12_parse(pkcs12, password.c_str(), &pkey, NULL, NULL) <= 0)
|
||||
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (ret < 0)
|
||||
outlen = EVP_PKEY_get_size(pkey);
|
||||
|
||||
ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
||||
|
||||
/* Use RSA private key */
|
||||
if (EVP_PKEY_decrypt_init(ctx) <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (gourou::logLevel >= gourou::DEBUG)
|
||||
{
|
||||
printf("Encrypted : ");
|
||||
for(int i=0; i<ret; i++)
|
||||
printf("%02x ", res[i]);
|
||||
printf("\n");
|
||||
}
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
tmp = (unsigned char*)malloc(outlen);
|
||||
|
||||
/* PKCS1 functions are no more exported */
|
||||
padWithPKCS1(tmp, outlen, data, dataLength);
|
||||
|
||||
ret = EVP_PKEY_decrypt(ctx, res, &outlen, tmp, outlen);
|
||||
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
free(tmp);
|
||||
|
||||
if (ret <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::RSAPrivateDecrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||
@@ -317,24 +378,30 @@ void DRMProcessorClientImpl::RSAPrivateDecrypt(const unsigned char* RSAKey, unsi
|
||||
if (!p8inf)
|
||||
EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
EVP_PKEY_CTX *ctx;
|
||||
EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);
|
||||
RSA * rsa;
|
||||
size_t outlen = dataLength;
|
||||
int ret;
|
||||
|
||||
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||
if (!pkey)
|
||||
EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
ret = RSA_private_decrypt(dataLength, data, res, rsa, RSA_NO_PADDING);
|
||||
ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
||||
|
||||
if (ret < 0)
|
||||
if (EVP_PKEY_decrypt_init(ctx) <= 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("\n");
|
||||
}
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
ret = EVP_PKEY_decrypt(ctx, res, &outlen, data, dataLength);
|
||||
|
||||
PKCS8_PRIV_KEY_INFO_free(p8inf);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
BIO_free(mem);
|
||||
|
||||
if (ret <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||
@@ -342,61 +409,79 @@ void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsig
|
||||
const unsigned char* data, unsigned dataLength,
|
||||
unsigned char* res)
|
||||
{
|
||||
size_t outlen;
|
||||
|
||||
X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength);
|
||||
if (!x509)
|
||||
EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate");
|
||||
|
||||
EVP_PKEY_CTX *ctx;
|
||||
EVP_PKEY * evpKey = X509_get_pubkey(x509);
|
||||
RSA* rsa = EVP_PKEY_get1_RSA(evpKey);
|
||||
EVP_PKEY_free(evpKey);
|
||||
|
||||
if (!rsa)
|
||||
EXCEPTION(gourou::CLIENT_NO_PRIV_KEY, "No private key in certificate");
|
||||
if (!evpKey)
|
||||
EXCEPTION(gourou::CLIENT_NO_PUB_KEY, "No public key in certificate");
|
||||
|
||||
ctx = EVP_PKEY_CTX_new(evpKey, NULL);
|
||||
|
||||
if (EVP_PKEY_encrypt_init(ctx) <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
int ret = EVP_PKEY_encrypt(ctx, res, &outlen, data, dataLength);
|
||||
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
|
||||
int ret = RSA_public_encrypt(dataLength, data, res, rsa, RSA_PKCS1_PADDING);
|
||||
if (ret < 0)
|
||||
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
EVP_PKEY_free(evpKey);
|
||||
}
|
||||
|
||||
void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits)
|
||||
{
|
||||
BIGNUM * bn = BN_new();
|
||||
RSA * rsa = RSA_new();
|
||||
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
EVP_PKEY *key = NULL;
|
||||
|
||||
BN_set_word(bn, 0x10001);
|
||||
RSA_generate_key_ex(rsa, keyLengthBits, bn, 0);
|
||||
|
||||
EVP_PKEY_keygen_init(ctx);
|
||||
|
||||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keyLengthBits);
|
||||
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);
|
||||
EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, bn);
|
||||
EVP_PKEY_keygen(ctx, &key);
|
||||
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
BN_free(bn);
|
||||
|
||||
return rsa;
|
||||
return key;
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::destroyRSAHandler(void* handler)
|
||||
{
|
||||
RSA_free((RSA*)handler);
|
||||
free(handler);
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::extractRSAPublicKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)
|
||||
{
|
||||
EVP_PKEY * evpKey = EVP_PKEY_new();
|
||||
EVP_PKEY_set1_RSA(evpKey, (RSA*)handler);
|
||||
X509_PUBKEY *x509_pubkey = 0;
|
||||
X509_PUBKEY_set(&x509_pubkey, evpKey);
|
||||
X509_PUBKEY_set(&x509_pubkey, (EVP_PKEY*)handler);
|
||||
|
||||
*keyOutLength = i2d_X509_PUBKEY(x509_pubkey, keyOut);
|
||||
|
||||
X509_PUBKEY_free(x509_pubkey);
|
||||
EVP_PKEY_free(evpKey);
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)
|
||||
{
|
||||
EVP_PKEY * evpKey = EVP_PKEY_new();
|
||||
EVP_PKEY_set1_RSA(evpKey, (RSA*)handler);
|
||||
PKCS8_PRIV_KEY_INFO * privKey = EVP_PKEY2PKCS8(evpKey);
|
||||
PKCS8_PRIV_KEY_INFO * privKey = EVP_PKEY2PKCS8((EVP_PKEY*)handler);
|
||||
|
||||
*keyOutLength = i2d_PKCS8_PRIV_KEY_INFO(privKey, keyOut);
|
||||
|
||||
PKCS8_PRIV_KEY_INFO_free(privKey);
|
||||
EVP_PKEY_free(evpKey);
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||
@@ -406,12 +491,14 @@ void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, uns
|
||||
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);
|
||||
PKCS12_parse(pkcs12, password.c_str(), &pkey, &cert, NULL);
|
||||
|
||||
if (!cert)
|
||||
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
*certOutLength = i2d_X509(cert, certOut);
|
||||
|
||||
@@ -419,24 +506,27 @@ void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, uns
|
||||
}
|
||||
|
||||
/* Crypto interface */
|
||||
void DRMProcessorClientImpl::Encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
void DRMProcessorClientImpl::encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength,
|
||||
const unsigned char* dataIn, unsigned int dataInLength,
|
||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||
{
|
||||
void* handler = EncryptInit(algo, chaining, key, keyLength, iv, ivLength);
|
||||
EncryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
||||
EncryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
||||
void* handler = encryptInit(algo, chaining, key, keyLength, iv, ivLength);
|
||||
encryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
||||
encryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
||||
}
|
||||
|
||||
void* DRMProcessorClientImpl::EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
void* DRMProcessorClientImpl::encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
int ret = 0;
|
||||
|
||||
if (algo == ALGO_AES)
|
||||
switch (algo)
|
||||
{
|
||||
case ALGO_AES:
|
||||
{
|
||||
switch(keyLength)
|
||||
{
|
||||
@@ -444,10 +534,10 @@ void* DRMProcessorClientImpl::EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaini
|
||||
switch(chaining)
|
||||
{
|
||||
case CHAIN_ECB:
|
||||
EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, iv);
|
||||
ret = EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, iv);
|
||||
break;
|
||||
case CHAIN_CBC:
|
||||
EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv);
|
||||
ret = EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv);
|
||||
break;
|
||||
default:
|
||||
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
||||
@@ -457,26 +547,39 @@ void* DRMProcessorClientImpl::EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaini
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (algo == ALGO_RC4)
|
||||
case 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);
|
||||
ret = EVP_DecryptInit(ctx, EVP_rc4(), key, iv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void* DRMProcessorClientImpl::DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
void* DRMProcessorClientImpl::decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
int ret = 0;
|
||||
|
||||
if (algo == ALGO_AES)
|
||||
switch(algo)
|
||||
{
|
||||
case ALGO_AES:
|
||||
{
|
||||
switch(keyLength)
|
||||
{
|
||||
@@ -484,10 +587,10 @@ void* DRMProcessorClientImpl::DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaini
|
||||
switch(chaining)
|
||||
{
|
||||
case CHAIN_ECB:
|
||||
EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, iv);
|
||||
ret = EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, iv);
|
||||
break;
|
||||
case CHAIN_CBC:
|
||||
EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);
|
||||
ret = EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);
|
||||
break;
|
||||
default:
|
||||
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
|
||||
@@ -497,58 +600,81 @@ void* DRMProcessorClientImpl::DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaini
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (algo == ALGO_RC4)
|
||||
case 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);
|
||||
ret = EVP_DecryptInit(ctx, EVP_rc4(), key, iv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::EncryptUpdate(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)
|
||||
{
|
||||
EVP_EncryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
||||
int ret = EVP_EncryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
||||
|
||||
if (ret <= 0)
|
||||
EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::EncryptFinalize(void* handler,
|
||||
void DRMProcessorClientImpl::encryptFinalize(void* handler,
|
||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||
{
|
||||
int len;
|
||||
EVP_EncryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
|
||||
int len, ret;
|
||||
|
||||
ret = EVP_EncryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
|
||||
*dataOutLength += len;
|
||||
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);
|
||||
|
||||
if (ret <= 0)
|
||||
EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::Decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
void DRMProcessorClientImpl::decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength,
|
||||
const unsigned char* dataIn, unsigned int dataInLength,
|
||||
unsigned char* dataOut, unsigned int* dataOutLength)
|
||||
{
|
||||
void* handler = DecryptInit(algo, chaining, key, keyLength, iv, ivLength);
|
||||
DecryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
||||
DecryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
||||
void* handler = decryptInit(algo, chaining, key, keyLength, iv, ivLength);
|
||||
decryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
|
||||
decryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::DecryptUpdate(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)
|
||||
{
|
||||
EVP_DecryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
||||
int ret = EVP_DecryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
|
||||
|
||||
if (ret <= 0)
|
||||
EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
void DRMProcessorClientImpl::DecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength)
|
||||
void DRMProcessorClientImpl::decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength)
|
||||
{
|
||||
int len;
|
||||
EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
|
||||
int len, ret;
|
||||
|
||||
ret = EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
|
||||
*dataOutLength += len;
|
||||
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);
|
||||
|
||||
if (ret <= 0)
|
||||
EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
void* DRMProcessorClientImpl::zipOpen(const std::string& path)
|
||||
|
||||
@@ -31,16 +31,23 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
#include <openssl/provider.h>
|
||||
#endif
|
||||
|
||||
#include <drmprocessorclient.h>
|
||||
|
||||
class DRMProcessorClientImpl : public gourou::DRMProcessorClient
|
||||
{
|
||||
public:
|
||||
DRMProcessorClientImpl();
|
||||
~DRMProcessorClientImpl();
|
||||
|
||||
/* Digest interface */
|
||||
virtual void* createDigest(const std::string& digestName);
|
||||
virtual int digestUpdate(void* handler, unsigned char* data, unsigned int length);
|
||||
virtual int digestFinalize(void* handler,unsigned char* digestOut);
|
||||
virtual int digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut);
|
||||
virtual void digestUpdate(void* handler, unsigned char* data, unsigned int length);
|
||||
virtual void digestFinalize(void* handler,unsigned char* digestOut);
|
||||
virtual void digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut);
|
||||
|
||||
/* Random interface */
|
||||
virtual void randBytes(unsigned char* bytesOut, unsigned int length);
|
||||
@@ -73,34 +80,34 @@ public:
|
||||
unsigned char** certOut, unsigned int* certOutLength);
|
||||
|
||||
/* Crypto interface */
|
||||
virtual void Encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength,
|
||||
const unsigned char* dataIn, unsigned int dataInLength,
|
||||
unsigned char* dataOut, unsigned int* dataOutLength);
|
||||
|
||||
virtual void* EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void* encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv=0, unsigned int ivLength=0);
|
||||
|
||||
|
||||
virtual void EncryptUpdate(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);
|
||||
virtual void EncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
||||
virtual void encryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
||||
|
||||
virtual void Decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv, unsigned int ivLength,
|
||||
const unsigned char* dataIn, unsigned int dataInLength,
|
||||
unsigned char* dataOut, unsigned int* dataOutLength);
|
||||
|
||||
virtual void* DecryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
virtual void* decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining,
|
||||
const unsigned char* key, unsigned int keyLength,
|
||||
const unsigned char* iv=0, unsigned int ivLength=0);
|
||||
|
||||
virtual void DecryptUpdate(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);
|
||||
virtual void DecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
||||
virtual void decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
|
||||
|
||||
/* ZIP Interface */
|
||||
virtual void* zipOpen(const std::string& path);
|
||||
@@ -118,6 +125,17 @@ public:
|
||||
|
||||
virtual void deflate(gourou::ByteArray& data, gourou::ByteArray& result,
|
||||
int wbits=-15, int compressionLevel=8);
|
||||
|
||||
private:
|
||||
|
||||
void padWithPKCS1(unsigned char* out, unsigned int outLength,
|
||||
const unsigned char* in, unsigned int inLength);
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
OSSL_PROVIDER *legacy, *deflt;
|
||||
#else
|
||||
void *legacy, *deflt;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
40
utils/launcher.cpp
Normal file
40
utils/launcher.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils_common.h"
|
||||
|
||||
#ifndef DEFAULT_UTIL
|
||||
#define DEFAULT_UTIL "acsmdownloader"
|
||||
#endif
|
||||
|
||||
/* Inspired from https://discourse.appimage.org/t/call-alternative-binary-from-appimage/93/10*/
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
char* util, *argv0;
|
||||
char* mountPoint = getenv("APPDIR");
|
||||
std::string fullPath;
|
||||
|
||||
/* Original command is in ARGV0 env variable*/
|
||||
argv0 = strdup(getenv("ARGV0"));
|
||||
util = basename(argv0);
|
||||
|
||||
fullPath = std::string(mountPoint) + util;
|
||||
|
||||
if (std::string(util) == "launcher" || !fileExists(fullPath.c_str()))
|
||||
fullPath = std::string(mountPoint) + DEFAULT_UTIL;
|
||||
|
||||
free(argv0);
|
||||
|
||||
argv[0] = strdup(fullPath.c_str());
|
||||
|
||||
if (execvp(argv[0], argv))
|
||||
std::cout << "Unable to launch '" << argv[0] << "'" << std::endl;
|
||||
|
||||
/* Should not happens */
|
||||
free(argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user