43 Commits

Author SHA1 Message Date
Grégory Soutadé
c19279397f Update README.md 2023-08-09 20:58:06 +02:00
Grégory Soutadé
bb5349d710 Update version 2023-08-08 20:14:23 +02:00
Grégory Soutadé
9a75213b49 Fix misuse of DESTDIR and PREFIX in Makefile 2023-08-08 20:13:03 +02:00
Grégory Soutadé
e06d20a392 DRM removal: Forgot to decrypt HexaString objects 2023-08-05 14:43:48 +02:00
Grégory Soutadé
a0f6324999 Try to fix GCC 13 compilation errors 2023-05-03 21:15:31 +02:00
Grégory Soutadé
c259cbd5a3 Add missing libgen.h in utils for basename() call 2023-03-28 20:32:05 +02:00
Grégory Soutadé
46afe771c7 Remove old pugixml include in utils Makefile 2023-01-15 09:51:00 +01:00
Grégory Soutadé
cad2189fc2 Typo fix 2023-01-14 18:21:06 +01:00
Grégory Soutadé
50bc16079f Fix static build 2023-01-14 12:56:06 +01:00
Grégory Soutadé
1213b34250 Fix ADEPT path creation within adept_activate 2023-01-11 19:57:47 +01:00
Grégory Soutadé
a66dcb959c Work on Makefile 2023-01-11 19:57:47 +01:00
Grégory Soutadé
84b01a5de3 Use system version of pugixml, not a checkouted one 2023-01-08 21:15:33 +01:00
Grégory Soutadé
be78d24236 Add man pages for utils 2023-01-08 21:05:22 +01:00
Grégory Soutadé
8aec5be244 Update Makefile to be more GNU/Linux style 2023-01-08 21:05:04 +01:00
Grégory Soutadé
3a0ab4b438 Update --help for utils and README.md 2023-01-08 21:04:23 +01:00
Grégory Soutadé
891ed05926 We can now specify directly file path for acsmdownloader and adept_remove (-f stille keeped for compatibility) 2023-01-08 20:58:41 +01:00
Grégory Soutadé
fc839e671a Manage ACSM files that contains server internal error 2023-01-06 21:17:57 +01:00
Grégory Soutadé
ab5afa5003 Add new default ADEPT directories : /home/<user>/.config/adept and $ADEPT_DIR environment variable 2023-01-05 21:29:55 +01:00
Grégory Soutadé
937c27fc23 Fix some warnings 2023-01-05 21:27:50 +01:00
Grégory Soutadé
34216d1b6e Check for target user before trying to decrypt a file 2023-01-05 21:26:05 +01:00
Berwyn
ffd2004cbb Correct 'any book loaned' message 2023-01-04 20:23:57 +01:00
Grégory Soutadé
c41dd46ca7 Check for potential write error (or not buffer fully consumed) 2022-12-23 17:51:51 +01:00
Grégory Soutadé
e4bd73c03d Add global option -D to utils, allowing to specify .adept directory instead of every single files. WARNING : -D has been changed by -d in adept_loan_mgt ! 2022-12-21 21:23:42 +01:00
Grégory Soutadé
f65e8cd9eb Check for target user before trying to decrypt a file 2022-12-21 21:06:03 +01:00
Grégory Soutadé
24bae89095 Factorize decryptADEPTKey() for ePub and PDF 2022-12-21 20:58:49 +01:00
Grégory Soutadé
afab1c0012 Fix over encrypted RSA key decryption algorithm 2022-12-21 20:15:11 +01:00
Grégory Soutadé
7878f91cdd Add support for MacOS and old compilers (not supporting C++11). Main patch is from Samuel Marks. 2022-11-21 17:56:29 +01:00
Grégory Soutadé
6e3958f09e Compute over encrypted key also for PDF files 2022-09-04 09:25:06 +02:00
Grégory Soutadé
2dbd4cc343 Update version 2022-08-29 15:19:23 +02:00
Grégory Soutadé
e28dc39a68 Make DRMProcessorClient API more consistent 2022-08-29 15:19:23 +02:00
Grégory Soutadé
7b8c7acbad Compute first pass for encryptedKey if keyType attribute is set 2022-08-29 11:57:33 +02:00
Grégory Soutadé
56b3231f92 Add dumpBuffer() in libgourou_common 2022-08-29 11:56:47 +02:00
Grégory Soutadé
7084fb7025 Add fromHex() static function to ByteArray 2022-08-29 11:56:47 +02:00
Grégory Soutadé
7f5b787cb9 Add launcher util for AppImage 2022-08-29 11:56:47 +02:00
Grégory Soutadé
086e9b0610 Update version 2022-08-29 11:56:47 +02:00
Grégory Soutadé
600535d52c Utils: Migration to OpenSSL3 2022-08-29 11:56:47 +02:00
Grégory Soutadé
57c3a58994 Add STATIC_NONCE option for build (developper mode) 2022-08-10 21:34:30 +02:00
Grégory Soutadé
210b265693 Forward DEBUG flag in Makefile 2022-08-10 21:34:30 +02:00
Grégory Soutadé
33bb983283 Change log levels names to avoid collisions 2022-08-10 21:34:30 +02:00
Grégory Soutadé
3c73b8ccb3 Update .gitignore
Patch from Nguyễn Gia Phong
2022-07-03 09:22:06 +02:00
Grégory Soutadé
4acf401031 Don't clone base64 repository at first build, use a static version of Base64.h (not modified since many years)
Patch from Nguyễn Gia Phong
2022-07-03 09:20:05 +02:00
Grégory Soutadé
7666d2a241 Update README 2022-06-12 15:03:14 +02:00
Grégory Soutadé
201ec69b11 Add scripts/update_lib.sh 2022-06-12 15:03:14 +02:00
29 changed files with 1338 additions and 414 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
obj obj
lib lib
*.o
*.a *.a
*.so *.so
*~ *~

View File

@@ -1,22 +1,34 @@
PREFIX ?= /usr/local
LIBDIR ?= /lib
INCDIR ?= /include
AR ?= $(CROSS)ar AR ?= $(CROSS)ar
CXX ?= $(CROSS)g++ CXX ?= $(CROSS)g++
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a 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./usr/include/pugixml -I./lib/updfparser/include
LDFLAGS = $(UPDFPARSERLIB) LDFLAGS = -lpugixml
VERSION := $(shell cat include/libgourou.h |grep LIBGOUROU_VERSION|cut -d '"' -f2)
BUILD_STATIC ?= 0 BUILD_STATIC ?= 0
BUILD_SHARED ?= 1 BUILD_SHARED ?= 1
BUILD_UTILS ?= 1 BUILD_UTILS ?= 1
TARGETS = TARGETS =
TARGET_LIBRARIES =
ifneq ($(STATIC_UTILS),)
BUILD_STATIC=1
endif
ifneq ($(BUILD_STATIC), 0) ifneq ($(BUILD_STATIC), 0)
TARGETS += libgourou.a TARGETS += libgourou.a
TARGET_LIBRARIES += libgourou.a
STATIC_UTILS=1
endif endif
ifneq ($(BUILD_SHARED), 0) ifneq ($(BUILD_SHARED), 0)
TARGETS += libgourou.so TARGETS += libgourou.so
TARGET_LIBRARIES += libgourou.so libgourou.so.$(VERSION)
endif endif
ifneq ($(BUILD_UTILS), 0) ifneq ($(BUILD_UTILS), 0)
TARGETS += build_utils TARGETS += build_utils
@@ -24,48 +36,77 @@ endif
ifneq ($(DEBUG),) ifneq ($(DEBUG),)
CXXFLAGS += -ggdb -O0 CXXFLAGS += -ggdb -O0 -DDEBUG
else else
CXXFLAGS += -O2 CXXFLAGS += -O2
endif endif
ifneq ($(STATIC_NONCE),)
CXXFLAGS += -DSTATIC_NONCE=1
endif
SRCDIR := src SRCDIR := src
INCDIR := inc
BUILDDIR := obj BUILDDIR := obj
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/loan_token.cpp src/bytearray.cpp src/pugixml.cpp SOURCES = src/libgourou.cpp src/user.cpp src/device.cpp src/fulfillment_item.cpp src/loan_token.cpp src/bytearray.cpp
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT))) OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
all: lib obj $(TARGETS) all: version lib obj $(TARGETS)
version:
@echo "Building libgourou $(VERSION)"
lib: lib:
mkdir lib mkdir lib
./scripts/setup.sh ./scripts/setup.sh
update_lib:
./scripts/update_lib.sh
obj: obj:
mkdir obj mkdir obj
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
$(CXX) $(CXXFLAGS) -c $^ -o $@ $(CXX) $(CXXFLAGS) -c $^ -o $@
libgourou: libgourou.a libgourou.so libgourou: $(TARGET_LIBRARIES)
libgourou.a: $(OBJECTS) $(UPDFPARSERLIB) libgourou.a: $(OBJECTS) $(UPDFPARSERLIB)
$(AR) crs $@ obj/*.o $(UPDFPARSERLIB) $(AR) rcs --thin $@ $^
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB) libgourou.so.$(VERSION): $(OBJECTS) $(UPDFPARSERLIB)
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared $(CXX) $^ -Wl,-soname,$@ $(LDFLAGS) -o $@ -shared
build_utils: libgourou.so: libgourou.so.$(VERSION)
make -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) OPENSSL3=$(OPENSSL3) ln -f -s $^ $@
build_utils: $(TARGET_LIBRARIES)
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX)
install: $(TARGET_LIBRARIES)
install -d $(DESTDIR)$(PREFIX)$(LIBDIR)
# Use cp to preserver symlinks
cp --no-dereference $(TARGET_LIBRARIES) $(DESTDIR)$(PREFIX)$(LIBDIR)
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) install
uninstall:
cd $(DESTDIR)$(PREFIX)/$(LIBDIR)
rm -f $(TARGET_LIBRARIES) libgourou.so.$(VERSION)
cd -
install_headers:
install -d $(DESTDIR)$(PREFIX)/$(INCDIR)/libgourou
cp --no-dereference include/*.h $(DESTDIR)$(PREFIX)/$(INCDIR)/libgourou
uninstall_headers:
rm -rf $(DESTDIR)$(PREFIX)/$(INCDIR)/libgourou
clean: clean:
rm -rf libgourou.a libgourou.so obj rm -rf libgourou.a libgourou.so libgourou.so.$(VERSION)* obj
make -C utils clean $(MAKE) -C utils clean
ultraclean: clean ultraclean: clean
rm -rf lib rm -rf lib
make -C utils ultraclean $(MAKE) -C utils ultraclean

View File

@@ -35,13 +35,26 @@ Dependencies
For libgourou : For libgourou :
* None _externals_ :
* libpugixml
_internals_ :
* uPDFParser
For utils : For utils :
* libcurl * libcurl
* OpenSSL * OpenSSL
* libzip * libzip
* libpugixml
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 Compilation
@@ -49,7 +62,7 @@ Compilation
Use _make_ command 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|install|uninstall]
CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-) CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-)
@@ -63,44 +76,51 @@ BUILD_STATIC build libgourou.a if 1, nothing if 0, can be combined with BUILD_SH
BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_STATIC BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_STATIC
other variables are DESTDIR and PREFIX to handle destination install directory
* Default value * Default value
Utils Utils
----- -----
You can import configuration from your eReader or create a new one with _utils/adept\_activate_ : First, add libgourou.so to your LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
You can optionaly specify your .adept directory
export ADEPT_DIR=/home/XXX
Then, use utils as following :
You can import configuration from your eReader or create a new one with _utils/adept\_activate_ :
./utils/adept_activate -u <AdobeID USERNAME> ./utils/adept_activate -u <AdobeID USERNAME>
Then a _./.adept_ directory is created with all configuration file Then a _/home/<user>/.config/adept_ directory is created with all configuration file
To download an ePub/PDF : To download an ePub/PDF :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD ./utils/acsmdownloader <ACSM_FILE>
./utils/acsmdownloader -f <ACSM_FILE>
To export your private key (for DeDRM software) : To export your private key (for DeDRM software) :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./utils/acsmdownloader --export-private-key [-o adobekey_1.der] ./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
To remove ADEPT DRM : To remove ADEPT DRM :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD ./utils/adept_remove <encryptedFile>
./utils/adept_remove -f <encryptedFile>
To list loaned books : To list loaned books :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./utils/adept_loan_mgt [-l] ./utils/adept_loan_mgt [-l]
To return a loaned book : To return a loaned book :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
./utils/adept_loan_mgt -r <id> ./utils/adept_loan_mgt -r <id>
You can get utils full options description with -h or --help switch You can get utils full options description with -h or --help switch
@@ -116,7 +136,6 @@ Copyright
Grégory Soutadé Grégory Soutadé
License License
------- -------
@@ -125,9 +144,9 @@ libgourou : LGPL v3 or later
utils : BSD utils : BSD
Special thanks Special thanks
-------------- --------------
* _Jens_ for all test samples and utils testing * _Jens_ for all test samples and utils testing
* _Milian_ for debug & code * _Milian_ for debug & code
* _Berwyn H_ for all test samples, feedbacks, patches and kind donation

133
include/Base64.h Normal file
View File

@@ -0,0 +1,133 @@
#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>
#include <stdint.h>
namespace macaron {
class Base64 {
public:
static std::string Encode(const std::string data) {
static
#if __STDC_VERSION__ >= 201112L
constexpr
#endif /* __STDC_VERSION__ >= 201112L */
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
#if __STDC_VERSION__ >= 201112L
constexpr
#endif /* __STDC_VERSION__ >= 201112L */
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_ */

View File

@@ -104,6 +104,13 @@ namespace gourou
*/ */
std::string toBase64(); 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 * @brief Return a string with human readable hex encoded internal data
*/ */
@@ -130,7 +137,7 @@ namespace gourou
void append(const std::string& str); 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;} unsigned char* data() {return _data;}

View File

@@ -47,20 +47,16 @@ namespace gourou
* @param handler Digest handler * @param handler Digest handler
* @param data Data to digest * @param data Data to digest
* @param length Length of data * @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 * @brief Finalize digest with remained buffered data and destroy handler
* *
* @param handler Digest handler * @param handler Digest handler
* @param digestOut Digest result (buffer must be pre allocated with right size) * @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 * @brief Global digest function
@@ -69,10 +65,8 @@ namespace gourou
* @param data Data to digest * @param data Data to digest
* @param length Length of data * @param length Length of data
* @param digestOut Digest result (buffer must be pre allocated with right size) * @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 class RandomInterface
@@ -111,6 +105,7 @@ namespace gourou
public: public:
enum RSA_KEY_TYPE { enum RSA_KEY_TYPE {
RSA_KEY_PKCS12 = 0, RSA_KEY_PKCS12 = 0,
RSA_KEY_PKCS8,
RSA_KEY_X509 RSA_KEY_X509
}; };
@@ -238,7 +233,7 @@ namespace gourou
* @param dataOut Encrypted data * @param dataOut Encrypted data
* @param dataOutLength Length of 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength, const unsigned char* iv, unsigned int ivLength,
const unsigned char* dataIn, unsigned int dataInLength, const unsigned char* dataIn, unsigned int dataInLength,
@@ -255,7 +250,7 @@ namespace gourou
* *
* @return AES handler * @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* key, unsigned int keyLength,
const unsigned char* iv=0, unsigned int ivLength=0) = 0; const unsigned char* iv=0, unsigned int ivLength=0) = 0;
@@ -268,18 +263,18 @@ namespace gourou
* @param dataOut Encrypted data * @param dataOut Encrypted data
* @param dataOutLength Length of 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; unsigned char* dataOut, unsigned int* dataOutLength) = 0;
/** /**
* @brief Finalizeencryption (pad and encrypt last block if needed) * @brief Finalize encryption (pad and encrypt last block if needed)
* Destroy handler at the end * Destroy handler at the end
* *
* @param handler Crypto handler * @param handler Crypto handler
* @param dataOut Last block of encrypted data * @param dataOut Last block of encrypted data
* @param dataOutLength Length of encrypted data * @param dataOutLength Length of encrypted data
*/ */
virtual void 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 * @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 dataOut Encrypted data
* @param dataOutLength Length of 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength, const unsigned char* iv, unsigned int ivLength,
const unsigned char* dataIn, unsigned int dataInLength, const unsigned char* dataIn, unsigned int dataInLength,
@@ -312,7 +307,7 @@ namespace gourou
* *
* @return AES handler * @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* key, unsigned int keyLength,
const unsigned char* iv=0, unsigned int ivLength=0) = 0; const unsigned char* iv=0, unsigned int ivLength=0) = 0;
@@ -325,7 +320,7 @@ namespace gourou
* @param dataOut Decrypted data * @param dataOut Decrypted data
* @param dataOutLength Length of 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; unsigned char* dataOut, unsigned int* dataOutLength) = 0;
/** /**
* @brief Finalize decryption (decrypt last block and remove padding if it is set). * @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 dataOut Last block decrypted data
* @param dataOutLength Length of 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;
}; };

View File

@@ -27,20 +27,17 @@
#include "drmprocessorclient.h" #include "drmprocessorclient.h"
#include <pugixml.hpp> #include <pugixml.hpp>
#include <stdint.h>
#ifndef HOBBES_DEFAULT_VERSION #ifndef HOBBES_DEFAULT_VERSION
#define HOBBES_DEFAULT_VERSION "10.0.4" #define HOBBES_DEFAULT_VERSION "10.0.4"
#endif #endif
#ifndef DEFAULT_ADEPT_DIR
#define DEFAULT_ADEPT_DIR "./.adept"
#endif
#ifndef ACS_SERVER #ifndef ACS_SERVER
#define ACS_SERVER "http://adeactivate.adobe.com/adept" #define ACS_SERVER "http://adeactivate.adobe.com/adept"
#endif #endif
#define LIBGOUROU_VERSION "0.7.2" #define LIBGOUROU_VERSION "0.8.2"
namespace gourou namespace gourou
{ {
@@ -107,6 +104,11 @@ namespace gourou
* @param operatorURL URL of operator that loans this book * @param operatorURL URL of operator that loans this book
*/ */
void returnLoan(const std::string& loanID, const std::string& operatorURL); void returnLoan(const std::string& loanID, const std::string& operatorURL);
/**
* @brief Return default ADEPT directory (ie /home/<user>/.config/adept)
*/
static std::string getDefaultAdeptDir(void);
/** /**
* @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml). * @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml).
@@ -118,7 +120,7 @@ namespace gourou
* @param ACSServer Override main ACS server (default adeactivate.adobe.com) * @param ACSServer Override main ACS server (default adeactivate.adobe.com)
*/ */
static DRMProcessor* createDRMProcessor(DRMProcessorClient* client, static DRMProcessor* createDRMProcessor(DRMProcessorClient* client,
bool randomSerial=false, const std::string& dirName=std::string(DEFAULT_ADEPT_DIR), bool randomSerial=false, std::string dirName=std::string(""),
const std::string& hobbes=std::string(HOBBES_DEFAULT_VERSION), const std::string& hobbes=std::string(HOBBES_DEFAULT_VERSION),
const std::string& ACSServer=ACS_SERVER); const std::string& ACSServer=ACS_SERVER);
@@ -231,7 +233,8 @@ namespace gourou
void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate); void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate);
void fetchLicenseServiceCertificate(const std::string& licenseURL, void fetchLicenseServiceCertificate(const std::string& licenseURL,
const std::string& operatorURL); const std::string& operatorURL);
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey); std::string encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType);
void decryptADEPTKey(pugi::xml_document& rightsDoc, unsigned char* decryptedKey, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0);
void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize); void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
void generatePDFObjectKey(int version, void generatePDFObjectKey(int version,
const unsigned char* masterKey, unsigned int masterKeyLength, const unsigned char* masterKey, unsigned int masterKeyLength,

View File

@@ -64,7 +64,8 @@ namespace gourou
FF_INVALID_ACSM_FILE, FF_INVALID_ACSM_FILE,
FF_NO_HMAC_IN_ACSM_FILE, FF_NO_HMAC_IN_ACSM_FILE,
FF_NOT_ACTIVATED, FF_NOT_ACTIVATED,
FF_NO_OPERATOR_URL FF_NO_OPERATOR_URL,
FF_SERVER_INTERNAL_ERROR
}; };
enum DOWNLOAD_ERROR { enum DOWNLOAD_ERROR {
@@ -106,6 +107,7 @@ namespace gourou
CLIENT_INVALID_PKCS12, CLIENT_INVALID_PKCS12,
CLIENT_INVALID_CERTIFICATE, CLIENT_INVALID_CERTIFICATE,
CLIENT_NO_PRIV_KEY, CLIENT_NO_PRIV_KEY,
CLIENT_NO_PUB_KEY,
CLIENT_RSA_ERROR, CLIENT_RSA_ERROR,
CLIENT_BAD_CHAINING, CLIENT_BAD_CHAINING,
CLIENT_BAD_KEY_SIZE, CLIENT_BAD_KEY_SIZE,
@@ -116,6 +118,8 @@ namespace gourou
CLIENT_INVALID_PKCS8, CLIENT_INVALID_PKCS8,
CLIENT_FILE_ERROR, CLIENT_FILE_ERROR,
CLIENT_OSSL_ERROR, CLIENT_OSSL_ERROR,
CLIENT_CRYPT_ERROR,
CLIENT_DIGEST_ERROR,
}; };
enum DRM_REMOVAL_ERROR { enum DRM_REMOVAL_ERROR {
@@ -125,9 +129,21 @@ namespace gourou
DRM_FORMAT_NOT_SUPPORTED, DRM_FORMAT_NOT_SUPPORTED,
DRM_IN_OUT_EQUALS, DRM_IN_OUT_EQUALS,
DRM_MISSING_PARAMETER, DRM_MISSING_PARAMETER,
DRM_INVALID_KEY_SIZE DRM_INVALID_KEY_SIZE,
DRM_ERR_ENCRYPTION_KEY_FP,
DRM_INVALID_USER
}; };
#ifndef _NOEXCEPT
#if __STDC_VERSION__ >= 201112L
# define _NOEXCEPT noexcept
# define _NOEXCEPT_(x) noexcept(x)
#else
# define _NOEXCEPT throw()
# define _NOEXCEPT_(x)
#endif
#endif /* !_NOEXCEPT */
/** /**
* Generic exception class * Generic exception class
*/ */
@@ -140,7 +156,7 @@ namespace gourou
std::stringstream msg; std::stringstream msg;
msg << "Exception code : 0x" << std::setbase(16) << code << std::endl; msg << "Exception code : 0x" << std::setbase(16) << code << std::endl;
msg << "Message : " << message << std::endl; msg << "Message : " << message << std::endl;
if (logLevel >= DEBUG) if (logLevel >= LG_LOG_DEBUG)
msg << "File : " << file << ":" << std::setbase(10) << line << std::endl; msg << "File : " << file << ":" << std::setbase(10) << line << std::endl;
fullmessage = strdup(msg.str().c_str()); fullmessage = strdup(msg.str().c_str());
} }
@@ -153,7 +169,7 @@ namespace gourou
this->fullmessage = strdup(other.fullmessage); this->fullmessage = strdup(other.fullmessage);
} }
~Exception() ~Exception() _NOEXCEPT
{ {
free(fullmessage); free(fullmessage);
} }
@@ -224,35 +240,9 @@ namespace gourou
* It can throw an exception if tag does not exists * It can throw an exception if tag does not exists
* or just return an empty value * 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); 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_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);
if (!xpath_node) if (!xpath_node)
{ {
@@ -276,6 +266,37 @@ namespace gourou
return trim(res); 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 * @brief Append an element to root with a sub text element
* *
@@ -289,6 +310,29 @@ namespace gourou
node.append_child(pugi::node_pcdata).set_value(value.c_str()); 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 * @brief Open a file descriptor on path. If it already exists and truncate == true, it's truncated
* *
@@ -414,6 +458,20 @@ namespace gourou
} }
return 0; 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 #endif

View File

@@ -24,16 +24,16 @@
namespace gourou { namespace gourou {
enum GOUROU_LOG_LEVEL { enum GOUROU_LOG_LEVEL {
ERROR, LG_LOG_ERROR,
WARN, LG_LOG_WARN,
INFO, LG_LOG_INFO,
DEBUG, LG_LOG_DEBUG,
TRACE LG_LOG_TRACE
}; };
extern GOUROU_LOG_LEVEL logLevel; 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__) #define GOUROU_LOG_FUNC() GOUROU_LOG(TRACE, __FUNCTION__ << "() @ " << __FILE__ << ":" << __LINE__)
/** /**

View File

@@ -1,18 +1,5 @@
#!/bin/bash #!/bin/bash
# Pugixml
if [ ! -d lib/pugixml ] ; then
git clone https://github.com/zeux/pugixml.git lib/pugixml
pushd lib/pugixml
git checkout latest
popd
fi
# Base64
if [ ! -d lib/base64 ] ; then
git clone https://gist.github.com/f0fd86b6c73063283afe550bc5d77594.git lib/base64
fi
# uPDFParser # uPDFParser
if [ ! -d lib/updfparser ] ; then if [ ! -d lib/updfparser ] ; then
git clone git://soutade.fr/updfparser.git lib/updfparser git clone git://soutade.fr/updfparser.git lib/updfparser

13
scripts/update_lib.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
if [ ! -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
# uPDFParser
pushd lib/updfparser
git pull origin master
make clean all BUILD_STATIC=1 BUILD_SHARED=0

View File

@@ -17,8 +17,9 @@
along with libgourou. If not, see <http://www.gnu.org/licenses/>. along with libgourou. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <string.h> #include <string.h>
#include <stdexcept>
#include <base64/Base64.h> #include <Base64.h>
#include <bytearray.h> #include <bytearray.h>
@@ -155,6 +156,47 @@ namespace gourou
return macaron::Base64::Encode(std::string((char*)_data, _length)); 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() std::string ByteArray::toHex()
{ {
char* tmp = new char[_length*2+1]; char* tmp = new char[_length*2+1];

View File

@@ -29,13 +29,23 @@
#include <libgourou_log.h> #include <libgourou_log.h>
#include <device.h> #include <device.h>
// From https://stackoverflow.com/questions/1779715/how-to-get-mac-address-of-your-machine-using-a-c-program/35242525 #include <string.h>
#if defined(__linux__) || defined(linux) || defined(__linux)
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <net/if.h> #include <net/if.h>
#include <unistd.h> #include <unistd.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <string.h> #elif (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
|| defined(__bsdi__) || defined(__DragonFly__) || defined(__APPLE__))
#include <ifaddrs.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#define BSD_HEADERS 1
#endif
#if defined(__linux__) || defined(linux) || defined(__linux)
// From https://stackoverflow.com/questions/1779715/how-to-get-mac-address-of-your-machine-using-a-c-program/35242525
int get_mac_address(unsigned char* mac_address) int get_mac_address(unsigned char* mac_address)
{ {
struct ifreq ifr; struct ifreq ifr;
@@ -74,6 +84,43 @@ int get_mac_address(unsigned char* mac_address)
return 1; return 1;
} }
#elif BSD_HEADERS
// https://stackoverflow.com/a/3978293
int get_mac_address(unsigned char* mac_address, const char* if_name = "en0")
{
ifaddrs* iflist;
int found = 0;
if (getifaddrs(&iflist) == 0) {
for (ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {
if ((cur->ifa_addr->sa_family == AF_LINK) &&
(strcmp(cur->ifa_name, if_name) == 0) &&
cur->ifa_addr) {
sockaddr_dl* sdl = (sockaddr_dl*)cur->ifa_addr;
memcpy(mac_address, LLADDR(sdl), sdl->sdl_alen);
found = 1;
break;
}
}
freeifaddrs(iflist);
}
return found;
}
#else
int get_mac_address(unsigned char* mac_address)
{
GOUROU_LOG(INFO, "get_mac_address() not implemented for your platform, using a static address");
mac_address[0] = 0x8D;
mac_address[1] = 0x70;
mac_address[2] = 0x13;
mac_address[3] = 0x8D;
mac_address[4] = 0x43;
mac_address[5] = 0x27;
return 1;
}
#endif /* defined(__linux__) || defined(linux) || defined(__linux) */
namespace gourou namespace gourou

View File

@@ -17,6 +17,7 @@
along with libgourou. If not, see <http://www.gnu.org/licenses/>. along with libgourou. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cctype>
#include <fulfillment_item.h> #include <fulfillment_item.h>
#include <libgourou_common.h> #include <libgourou_common.h>
#include "user.h" #include "user.h"
@@ -93,8 +94,12 @@ namespace gourou
std::string FulfillmentItem::getMetadata(std::string name) std::string FulfillmentItem::getMetadata(std::string name)
{ {
// https://stackoverflow.com/questions/313970/how-to-convert-an-instance-of-stdstring-to-lower-case // https://stackoverflow.com/questions/313970/how-to-convert-an-instance-of-stdstring-to-lower-case
#if __STDC_VERSION__ >= 201112L
std::transform(name.begin(), name.end(), name.begin(), std::transform(name.begin(), name.end(), name.begin(),
[](unsigned char c){ return std::tolower(c); }); [](unsigned char c){ return std::tolower(c); });
#else
std::transform(name.begin(), name.end(), name.begin(), tolower);
#endif
name = std::string("dc:") + name; name = std::string("dc:") + name;
pugi::xpath_node path = metadatas.select_node(name.c_str()); pugi::xpath_node path = metadatas.select_node(name.c_str());

View File

@@ -28,6 +28,8 @@
#include <libgourou_common.h> #include <libgourou_common.h>
#include <libgourou_log.h> #include <libgourou_log.h>
#define LOCAL_ADEPT_DIR "./.adept"
#define ASN_NONE 0x00 #define ASN_NONE 0x00
#define ASN_NS_TAG 0x01 #define ASN_NS_TAG 0x01
#define ASN_CHILD 0x02 #define ASN_CHILD 0x02
@@ -37,7 +39,7 @@
namespace gourou namespace gourou
{ {
GOUROU_LOG_LEVEL logLevel = WARN; GOUROU_LOG_LEVEL logLevel = LG_LOG_WARN;
const std::string DRMProcessor::VERSION = LIBGOUROU_VERSION; const std::string DRMProcessor::VERSION = LIBGOUROU_VERSION;
DRMProcessor::DRMProcessor(DRMProcessorClient* client):client(client), device(0), user(0) DRMProcessor::DRMProcessor(DRMProcessorClient* client):client(client), device(0), user(0)
@@ -68,11 +70,14 @@ namespace gourou
if (user) delete user; if (user) delete user;
} }
DRMProcessor* DRMProcessor::createDRMProcessor(DRMProcessorClient* client, bool randomSerial, const std::string& dirName, DRMProcessor* DRMProcessor::createDRMProcessor(DRMProcessorClient* client, bool randomSerial, std::string dirName,
const std::string& hobbes, const std::string& ACSServer) const std::string& hobbes, const std::string& ACSServer)
{ {
DRMProcessor* processor = new DRMProcessor(client); DRMProcessor* processor = new DRMProcessor(client);
if (dirName == "")
dirName = getDefaultAdeptDir();
Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial); Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial);
processor->device = device; processor->device = device;
@@ -89,7 +94,7 @@ namespace gourou
uint16_t nlength = htons(length); uint16_t nlength = htons(length);
char c; char c;
if (logLevel >= TRACE) if (logLevel >= LG_LOG_TRACE)
printf("%02x %02x ", ((uint8_t*)&nlength)[0], ((uint8_t*)&nlength)[1]); printf("%02x %02x ", ((uint8_t*)&nlength)[0], ((uint8_t*)&nlength)[1]);
client->digestUpdate(sha_ctx, (unsigned char*)&nlength, sizeof(nlength)); client->digestUpdate(sha_ctx, (unsigned char*)&nlength, sizeof(nlength));
@@ -98,17 +103,17 @@ namespace gourou
{ {
c = string[i]; c = string[i];
client->digestUpdate(sha_ctx, (unsigned char*)&c, 1); client->digestUpdate(sha_ctx, (unsigned char*)&c, 1);
if (logLevel >= TRACE) if (logLevel >= LG_LOG_TRACE)
printf("%c", c); printf("%c", c);
} }
if (logLevel >= TRACE) if (logLevel >= LG_LOG_TRACE)
printf("\n"); printf("\n");
} }
void DRMProcessor::pushTag(void* sha_ctx, uint8_t tag) void DRMProcessor::pushTag(void* sha_ctx, uint8_t tag)
{ {
client->digestUpdate(sha_ctx, &tag, sizeof(tag)); client->digestUpdate(sha_ctx, &tag, sizeof(tag));
if (logLevel >= TRACE) if (logLevel >= LG_LOG_TRACE)
printf("%02x ", tag); printf("%02x ", tag);
} }
@@ -226,14 +231,7 @@ namespace gourou
client->digestFinalize(sha_ctx, sha_out); client->digestFinalize(sha_ctx, sha_out);
if (logLevel >= DEBUG) dumpBuffer(gourou::LG_LOG_DEBUG, "\nSHA OUT : ", sha_out, SHA1_LEN);
{
printf("\nSHA OUT : ");
for(int i=0; i<(int)SHA1_LEN; i++)
printf("%02x ", sha_out[i]);
printf("\n");
}
} }
void DRMProcessor::signNode(pugi::xml_node& rootNode) void DRMProcessor::signNode(pugi::xml_node& rootNode)
@@ -252,13 +250,8 @@ namespace gourou
client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(), client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(),
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(), RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
sha_out, sizeof(sha_out), res); sha_out, sizeof(sha_out), res);
if (logLevel >= DEBUG)
{ dumpBuffer(gourou::LG_LOG_DEBUG, "Sig : ", res, sizeof(res));
printf("Sig : ");
for(int i=0; i<(int)sizeof(res); i++)
printf("%02x ", res[i]);
printf("\n");
}
std::string signature = ByteArray(res, sizeof(res)).toBase64(); std::string signature = ByteArray(res, sizeof(res)).toBase64();
appendTextElem(rootNode, "adept:signature", signature); appendTextElem(rootNode, "adept:signature", signature);
@@ -282,7 +275,11 @@ namespace gourou
struct timeval tv; struct timeval tv;
gettimeofday(&tv, 0); gettimeofday(&tv, 0);
uint32_t nonce32[2] = {0x6f046000, 0x388a}; uint32_t nonce32[2] = {0x6f046000, 0x388a};
#ifdef STATIC_NONCE
uint64_t bigtime = 0xAA001122BBCCAAULL;
#else
uint64_t bigtime = tv.tv_sec*1000; uint64_t bigtime = tv.tv_sec*1000;
#endif
nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000); nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000);
nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF); nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF);
@@ -508,6 +505,13 @@ namespace gourou
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8)) 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);
// Could be an server internal error
pugi::xml_node rootNode = acsmDoc.first_child();
if (std::string(rootNode.name()) == "error")
{
EXCEPTION(FF_SERVER_INTERNAL_ERROR, rootNode.attribute("data").value());
}
GOUROU_LOG(INFO, "Fulfill " << ACSMFile); GOUROU_LOG(INFO, "Fulfill " << ACSMFile);
// Build req file // Build req file
@@ -515,7 +519,7 @@ namespace gourou
buildFulfillRequest(acsmDoc, fulfillReq); buildFulfillRequest(acsmDoc, fulfillReq);
pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill"); pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill");
pugi::xml_node rootNode = root.node(); rootNode = root.node();
// Remove HMAC // Remove HMAC
pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac"); pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac");
@@ -852,7 +856,23 @@ namespace gourou
addNonce(root); addNonce(root);
signNode(root); signNode(root);
} }
std::string DRMProcessor::getDefaultAdeptDir(void)
{
#ifndef DEFAULT_ADEPT_DIR
const char* user = getenv("USER");
if (user && user[0])
{
return std::string("/home/") + user + std::string("/.config/adept/");
}
else
return LOCAL_ADEPT_DIR;
#else
return DEFAULT_ADEPT_DIR "/";
#endif
}
void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL) void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL)
{ {
pugi::xml_document returnReq; pugi::xml_document returnReq;
@@ -877,7 +897,7 @@ namespace gourou
// Generate IV in front // Generate IV in front
client->randBytes(encrypted_data, 16); 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, deviceKey, 16, encrypted_data, 16,
data, len, data, len,
encrypted_data+16, &outLen); encrypted_data+16, &outLen);
@@ -896,7 +916,7 @@ namespace gourou
const unsigned char* deviceKey = device->getDeviceKey(); const unsigned char* deviceKey = device->getDeviceKey();
unsigned char* decrypted_data = new unsigned char[len-16]; unsigned char* decrypted_data = new unsigned char[len-16];
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
deviceKey, 16, data, 16, deviceKey, 16, data, 16,
data+16, len-16, data+16, len-16,
decrypted_data, &outLen); decrypted_data, &outLen);
@@ -939,43 +959,155 @@ namespace gourou
void DRMProcessor::exportPrivateLicenseKey(std::string path) void DRMProcessor::exportPrivateLicenseKey(std::string path)
{ {
int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU);
int ret;
if (fd <= 0) if (fd <= 0)
EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path); EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey()); ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey());
/* In adobekey.py, we get base64 decoded data [26:] */ /* In adobekey.py, we get base64 decoded data [26:] */
write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26); ret = write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26);
close(fd); close(fd);
if (ret != (int)(privateLicenseKey.length()-26))
{
EXCEPTION(gourou::GOUROU_FILE_ERROR, "Error writing " << path);
}
} }
int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;} int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;}
void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;} void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;}
void DRMProcessor::decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey) /**
* RSA Key can be over encrypted with AES128-CBC if keyType attribute is set
* remainder = keyType % 16
* Key = SHA256(keyType)[remainder*2:remainder*2+(16-remainder)] || SHA256(keyType)[16-remainder:16]
* 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)
{ {
if (encryptedKey.size() != 172) unsigned char digest[32], key[16], iv[16];
EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported"); 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));
long nonce = std::stol(keyType);
int remainder = nonce % 16;
memcpy(key, &digest[remainder*2], 16-remainder);
memcpy(&key[16-remainder], &digest[remainder], remainder);
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];
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass key : ", key, sizeof(key));
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass IV : ", iv, sizeof(iv));
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey); ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
unsigned char* clearRSAKey = new unsigned char[arrayEncryptedKey.size()];
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
std::string privateKeyData = user->getPrivateLicenseKey(); (const unsigned char*)key, (unsigned int)sizeof(key),
ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData); (const unsigned char*)iv, (unsigned int)sizeof(iv),
(const unsigned char*)arrayEncryptedKey.data(), arrayEncryptedKey.size(),
(unsigned char*)clearRSAKey, &dataOutLength);
ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE); dumpBuffer(gourou::LG_LOG_DEBUG, "\nDecrypted key : ", clearRSAKey, dataOutLength);
std::string pkcs12 = user->getPKCS12();
client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(),
RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(),
arrayEncryptedKey.data(), arrayEncryptedKey.length(), decryptedKey);
if (decryptedKey[0] != 0x00 || decryptedKey[1] != 0x02 || /* Last block could be 0x10*16 which is OpenSSL padding, remove it if it's the case */
decryptedKey[RSA_KEY_SIZE-16-1] != 0x00) bool skipLastLine = true;
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key"); 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::decryptADEPTKey(pugi::xml_document& rightsDoc, unsigned char* decryptedKey, const unsigned char* encryptionKey, unsigned encryptionKeySize)
{
unsigned char rsaKey[RSA_KEY_SIZE];
std::string user = extractTextElem(rightsDoc, "/adept:rights/licenseToken/user");
if (!encryptionKey)
{
if (this->user->getUUID() != user)
{
EXCEPTION(DRM_INVALID_USER, "This book has been downloaded for another user (" << user << ")");
}
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);
if (keyType != "")
encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType);
if (encryptedKey.size() != 172)
EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported");
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
std::string privateKeyData = this->user->getPrivateLicenseKey();
ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData);
dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length());
client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(),
RSAInterface::RSA_KEY_PKCS8, "",
arrayEncryptedKey.data(), arrayEncryptedKey.length(), rsaKey);
dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", rsaKey, sizeof(rsaKey));
if (rsaKey[0] != 0x00 || rsaKey[1] != 0x02 ||
rsaKey[RSA_KEY_SIZE-16-1] != 0x00)
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
memcpy(decryptedKey, &rsaKey[sizeof(rsaKey)-16], 16);
}
else
{
GOUROU_LOG(DEBUG, "Use provided encryption key");
if (encryptionKeySize != 16)
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
memcpy(decryptedKey, encryptionKey, encryptionKeySize);
}
}
void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut,
const unsigned char* encryptionKey, unsigned encryptionKeySize) const unsigned char* encryptionKey, unsigned encryptionKeySize)
{ {
@@ -987,19 +1119,9 @@ namespace gourou
pugi::xml_document rightsDoc; pugi::xml_document rightsDoc;
rightsDoc.load_string((const char*)zipData.data()); rightsDoc.load_string((const char*)zipData.data());
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey"); unsigned char decryptedKey[16];
unsigned char decryptedKey[RSA_KEY_SIZE];
if (!encryptionKey) decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize);
decryptADEPTKey(encryptedKey, decryptedKey);
else
{
GOUROU_LOG(DEBUG, "Use provided encryption key");
if (encryptionKeySize != 16)
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
}
client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData); client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData);
pugi::xml_document encryptionDoc; pugi::xml_document encryptionDoc;
@@ -1037,8 +1159,8 @@ namespace gourou
gourou::ByteArray inflateData(true); gourou::ByteArray inflateData(true);
unsigned int dataOutLength; 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 */ decryptedKey, sizeof(decryptedKey), /* Key */
_data, 16, /* IV */ _data, 16, /* IV */
&_data[16], zipData.length()-16, &_data[16], zipData.length()-16,
_clearData, &dataOutLength); _clearData, &dataOutLength);
@@ -1131,7 +1253,7 @@ namespace gourou
std::vector<uPDFParser::Object*> objects = parser.objects(); std::vector<uPDFParser::Object*> objects = parser.objects();
std::vector<uPDFParser::Object*>::iterator it; std::vector<uPDFParser::Object*>::iterator it;
std::vector<uPDFParser::Object*>::reverse_iterator rIt; std::vector<uPDFParser::Object*>::reverse_iterator rIt;
unsigned char decryptedKey[RSA_KEY_SIZE]; unsigned char decryptedKey[16];
int ebxId; int ebxId;
for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++) for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++)
@@ -1170,18 +1292,7 @@ namespace gourou
pugi::xml_document rightsDoc; pugi::xml_document rightsDoc;
rightsDoc.load_string((const char*)rightsStr.data()); rightsDoc.load_string((const char*)rightsStr.data());
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey"); decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize);
if (!encryptionKey)
decryptADEPTKey(encryptedKey, decryptedKey);
else
{
GOUROU_LOG(DEBUG, "Use provided encryption key");
if (encryptionKeySize != 16)
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
}
ebxId = ebx->objectId(); ebxId = ebx->objectId();
@@ -1213,10 +1324,10 @@ namespace gourou
GOUROU_LOG(DEBUG, "Obj " << object->objectId()); GOUROU_LOG(DEBUG, "Obj " << object->objectId());
unsigned char tmpKey[16]; unsigned char tmpKey[sizeof(decryptedKey)];
generatePDFObjectKey(ebxVersion->value(), generatePDFObjectKey(ebxVersion->value(),
decryptedKey+sizeof(decryptedKey)-16, 16, decryptedKey, sizeof(decryptedKey),
object->objectId(), object->generationNumber(), object->objectId(), object->generationNumber(),
tmpKey); tmpKey);
@@ -1241,8 +1352,8 @@ namespace gourou
GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength); GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength);
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
tmpKey, 16, /* Key */ tmpKey, sizeof(tmpKey), /* Key */
NULL, 0, /* IV */ NULL, 0, /* IV */
encryptedData, dataLength, encryptedData, dataLength,
clearData, &dataOutLength); clearData, &dataOutLength);
@@ -1252,6 +1363,30 @@ namespace gourou
delete[] clearData; delete[] clearData;
} }
else if (dictData->type() == uPDFParser::DataType::HEXASTRING)
{
string = ((uPDFParser::HexaString*) dictData)->value();
ByteArray hexStr = ByteArray::fromHex(string);
unsigned char* encryptedData = hexStr.data();
unsigned int dataLength = hexStr.size();
unsigned char* clearData = new unsigned char[dataLength];
unsigned int dataOutLength;
GOUROU_LOG(DEBUG, "Decrypt hexa string " << dictIt->first << " " << dataLength);
client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
tmpKey, sizeof(tmpKey), /* Key */
NULL, 0, /* IV */
encryptedData, dataLength,
clearData, &dataOutLength);
ByteArray clearHexStr = ByteArray(clearData, dataOutLength);
decodedStrings[dictIt->first] = new uPDFParser::HexaString(
clearHexStr.toHex());
delete[] clearData;
}
} }
for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++) for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++)
@@ -1274,8 +1409,8 @@ namespace gourou
GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength()); GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength());
client->Decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
tmpKey, 16, /* Key */ tmpKey, sizeof(tmpKey), /* Key */
NULL, 0, /* IV */ NULL, 0, /* IV */
encryptedData, dataLength, encryptedData, dataLength,
clearData, &dataOutLength); clearData, &dataOutLength);

View File

@@ -1 +0,0 @@
../lib/pugixml/src/pugixml.cpp

View File

@@ -1,20 +1,15 @@
BINDIR ?= /bin
MANDIR ?= /share/man
TARGETS=acsmdownloader adept_activate adept_remove adept_loan_mgt TARGET_BINARIES=acsmdownloader adept_activate adept_remove adept_loan_mgt
TARGETS=$(TARGET_BINARIES) launcher
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/ MAN_PAGES=acsmdownloader adept_activate adept_remove adept_loan_mgt
LDFLAGS=
ifneq ($(OPENSSL3),)
# OpenSSL 1.1.0 compat
CXXFLAGS += -DOPENSSL_API_COMPAT=0x10100000L
CXXFLAGS += -I/tmp/openssl3/usr/include/ -I/tmp/openssl3/usr/include/x86_64-linux-gnu
LDFLAGS += -L/tmp/openssl3/usr/lib/x86_64-linux-gnu -L/tmp/openssl3/usr/lib/x86_64-linux-gnu/ossl-modules
endif
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include
STATIC_DEP= STATIC_DEP=
LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl -lpugixml
ifneq ($(STATIC_UTILS),) ifneq ($(STATIC_UTILS),)
STATIC_DEP = $(ROOT)/libgourou.a STATIC_DEP = $(ROOT)/libgourou.a
@@ -23,7 +18,7 @@ LDFLAGS += -lgourou
endif endif
ifneq ($(DEBUG),) ifneq ($(DEBUG),)
CXXFLAGS += -ggdb -O0 CXXFLAGS += -ggdb -O0 -DDEBUG
else else
CXXFLAGS += -O2 CXXFLAGS += -O2
endif endif
@@ -35,23 +30,27 @@ COMMON_LIB = utils.a
all: $(TARGETS) all: $(TARGETS)
${COMMON_LIB}: ${COMMON_DEPS} ${STATIC_DEP} ${COMMON_LIB}: $(COMMON_DEPS)
$(CXX) $(CXXFLAGS) ${COMMON_DEPS} $(LDFLAGS) -c $(CXX) $(CXXFLAGS) $(COMMON_DEPS) $(LDFLAGS) -c
$(AR) crs $@ ${COMMON_OBJECTS} $(STATIC_DEP) $(AR) crs $@ $(COMMON_OBJECTS)
acsmdownloader: acsmdownloader.cpp ${COMMON_LIB} %: %.cpp $(COMMON_LIB) $(STATIC_DEP)
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ $(CXX) $(CXXFLAGS) $^ $(STATIC_DEP) $(LDFLAGS) -o $@
adept_activate: adept_activate.cpp ${COMMON_LIB} install: $(TARGET_BINARIES)
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ install -d $(DESTDIR)$(PREFIX)/$(BINDIR)
install -m 755 $(TARGET_BINARIES) $(DESTDIR)$(PREFIX)/$(BINDIR)
install -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
install -m 644 man/*.1 $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
adept_remove: adept_remove.cpp ${COMMON_LIB} uninstall:
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ cd $(DESTDIR)$(PREFIX)/$(BINDIR)
rm -f $(TARGET_BINARIES)
adept_loan_mgt: adept_loan_mgt.cpp ${COMMON_LIB} cd -
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ cd $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
rm -f $(addsuffix .1,$(TARGET_BINARIES)
clean: clean:
rm -f $(TARGETS) rm -f $(TARGETS) $(COMMON_LIB)
ultraclean: clean ultraclean: clean

View File

@@ -181,25 +181,31 @@ private:
static void usage(const char* cmd) static void usage(const char* cmd)
{ {
std::cout << "Download EPUB file from ACSM request file" << std::endl; std::cout << basename((char*)cmd) << " download EPUB file from ACSM request file" << std::endl << std::endl;
std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] 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)] [(-r|--resume)] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << std::endl; std::cout << "Global Options:" << std::endl;
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl; std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default <title.(epub|pdf|der)>)" << 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" << "Backward compatibility: 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 << " " << "-e|--export-private-key"<< "\t" << "Export private key in DER format" << std::endl;
std::cout << " " << "-r|--resume" << "\t\t" << "Try to resume download (in case of previous failure)" << std::endl; std::cout << " " << "-r|--resume" << "\t\t" << "Try to resume download (in case of previous failure)" << 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;
std::cout << "ADEPT Options:" << std::endl;
std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << std::endl;
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;
std::cout << std::endl; std::cout << std::endl;
std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
std::cout << "Environment:" << std::endl;
std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl << std::endl;
std::cout << " * $ADEPT_DIR environment variable" << std::endl;
std::cout << " * /home/<user>/.config/adept" << std::endl;
std::cout << " * Current directory" << std::endl; std::cout << " * Current directory" << std::endl;
std::cout << " * .adept" << std::endl; std::cout << " * .adept" << std::endl;
std::cout << " * adobe-digital-editions directory" << std::endl; std::cout << " * adobe-digital-editions directory" << std::endl;
@@ -209,6 +215,7 @@ static void usage(const char* cmd)
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int c, ret = -1; int c, ret = -1;
std::string _deviceFile, _activationFile, _devicekeyFile;
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile}; const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
int verbose = gourou::DRMProcessor::getLogLevel(); int verbose = gourou::DRMProcessor::getLogLevel();
@@ -216,6 +223,7 @@ int main(int argc, char** argv)
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"adept-directory", required_argument, 0, 'D' },
{"device-file", required_argument, 0, 'd' }, {"device-file", required_argument, 0, 'd' },
{"activation-file", required_argument, 0, 'a' }, {"activation-file", required_argument, 0, 'a' },
{"device-key-file", required_argument, 0, 'k' }, {"device-key-file", required_argument, 0, 'k' },
@@ -230,12 +238,20 @@ int main(int argc, char** argv)
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
c = getopt_long(argc, argv, "d:a:k:O:o:f:ervVh", c = getopt_long(argc, argv, "D:d:a:k:O:o:f:ervVh",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
switch (c) { switch (c) {
case 'D':
_deviceFile = std::string(optarg) + "/device.xml";
_activationFile = std::string(optarg) + "/activation.xml";
_devicekeyFile = std::string(optarg) + "/devicesalt";
deviceFile = _deviceFile.c_str();
activationFile = _activationFile.c_str();
devicekeyFile = _devicekeyFile.c_str();
break;
case 'd': case 'd':
deviceFile = optarg; deviceFile = optarg;
break; break;
@@ -277,6 +293,9 @@ int main(int argc, char** argv)
gourou::DRMProcessor::setLogLevel(verbose); gourou::DRMProcessor::setLogLevel(verbose);
if (optind == argc-1)
acsmFile = argv[optind];
if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) || if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) ||
(outputFile && !outputFile[0])) (outputFile && !outputFile[0]))
{ {

View File

@@ -31,6 +31,7 @@
#include <termios.h> #include <termios.h>
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
#include <libgen.h>
#include <iostream> #include <iostream>
#include <ostream> #include <ostream>
@@ -124,10 +125,11 @@ public:
static void usage(const char* cmd) static void usage(const char* cmd)
{ {
std::cout << "Create new device files used by ADEPT DRM" << std::endl; std::cout << basename((char*)cmd) << " create new device files used by ADEPT DRM" << std::endl << std::endl;
std::cout << "Usage: " << cmd << " (-a|--anonymous) | ( (-u|--username) username [(-p|--password) password] ) [(-O|--output-dir) dir] [(-r|--random-serial)] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
std::cout << "Usage: " << basename((char*)cmd) << " OPTIONS" << std::endl << std::endl;
std::cout << "Global Options:" << 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 << " " << "-a|--anonymous" << "\t" << "Anonymous account, no need for username/password (Use it only with a DRM removal software)" << std::endl;
std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl; std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl;
std::cout << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl; std::cout << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl;
@@ -230,7 +232,7 @@ int main(int argc, char** argv)
if (!_outputDir || _outputDir[0] == 0) if (!_outputDir || _outputDir[0] == 0)
{ {
outputDir = strdup(abspath(DEFAULT_ADEPT_DIR)); outputDir = strdup(gourou::DRMProcessor::getDefaultAdeptDir().c_str());
} }
else else
{ {

View File

@@ -45,7 +45,7 @@
#define MAX_SIZE_BOOK_NAME 30 #define MAX_SIZE_BOOK_NAME 30
static char* activationDir = 0; static char* adeptDir = 0;
static const char* deviceFile = "device.xml"; 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";
@@ -106,7 +106,7 @@ private:
struct Loan* loan; struct Loan* loan;
char * res; char * res;
std::string loanDir = std::string(activationDir) + std::string("/") + LOANS_DIR; std::string loanDir = std::string(adeptDir) + std::string("/") + LOANS_DIR;
if (!fileExists(loanDir.c_str())) if (!fileExists(loanDir.c_str()))
return; return;
@@ -209,7 +209,7 @@ private:
{ {
if (!loanedBooks.size()) if (!loanedBooks.size())
{ {
std::cout << "Any book loaned" << std::endl; std::cout << "No books loaned" << std::endl;
return; return;
} }
@@ -252,7 +252,7 @@ private:
std::cout.width (fillExpiration); std::cout.width (fillExpiration);
std::cout << ""; std::cout << "";
std::cout << "Exipration"; std::cout << "Expiration";
std::cout.width (fillExpiration); std::cout.width (fillExpiration);
std::cout << "" << std::endl; std::cout << "" << std::endl;
@@ -334,20 +334,26 @@ private:
static void usage(const char* cmd) static void usage(const char* cmd)
{ {
std::cout << "Manage loaned books" << std::endl; std::cout << basename((char*)cmd) << " manage loaned books" << std::endl << 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) << " [OPTIONS]" << std::endl << std::endl;
std::cout << " " << "-d|--activation-dir" << "\t" << "Directory of device.xml/activation.xml and device key" << std::endl; std::cout << "Global Options:" << std::endl;
std::cout << " " << "-l|--list" << "\t\t" << "List all loaned books" << std::endl; std::cout << " " << "-l|--list" << "\t\t" << "List all loaned books" << std::endl;
std::cout << " " << "-r|--return" << "\t\t" << "Return a loaned book" << std::endl; std::cout << " " << "-r|--return" << "\t\t" << "Return a loaned book" << std::endl;
std::cout << " " << "-D|--delete" << "\t\t" << "Delete a loan entry without returning it" << std::endl; std::cout << " " << "-d|--delete" << "\t\t" << "Delete a loan entry without returning it" << 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;
std::cout << "ADEPT Options:" << std::endl;
std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << std::endl;
std::cout << std::endl; std::cout << std::endl;
std::cout << "Activation directory is optional. If not set, it's looked into :" << std::endl;
std::cout << "Environment:" << std::endl;
std::cout << "ADEPT directory is optional. If not set, it's looked into :" << std::endl;
std::cout << " * $ADEPT_DIR environment variable" << std::endl;
std::cout << " * /home/<user>/.config/adept" << std::endl;
std::cout << " * Current directory" << std::endl; std::cout << " * Current directory" << std::endl;
std::cout << " * .adept" << std::endl; std::cout << " * .adept" << std::endl;
std::cout << " * adobe-digital-editions directory" << std::endl; std::cout << " * adobe-digital-editions directory" << std::endl;
@@ -365,10 +371,10 @@ int main(int argc, char** argv)
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"activation-dir", required_argument, 0, 'd' }, {"adept-directory", required_argument, 0, 'D' },
{"list", no_argument, 0, 'l' }, {"list", no_argument, 0, 'l' },
{"return", no_argument, 0, 'r' }, {"return", no_argument, 0, 'r' },
{"delete", no_argument, 0, 'D' }, {"delete", no_argument, 0, 'd' },
{"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' },
@@ -381,8 +387,8 @@ int main(int argc, char** argv)
break; break;
switch (c) { switch (c) {
case 'd': case 'D':
activationDir = optarg; adeptDir = optarg;
break; break;
case 'l': case 'l':
list = true; list = true;
@@ -392,7 +398,7 @@ int main(int argc, char** argv)
returnID = optarg; returnID = optarg;
actions++; actions++;
break; break;
case 'D': case 'd':
deleteID = optarg; deleteID = optarg;
actions++; actions++;
break; break;
@@ -432,9 +438,9 @@ int main(int argc, char** argv)
{ {
orig = *files[i]; orig = *files[i];
if (activationDir) if (adeptDir)
{ {
std::string path = std::string(activationDir) + std::string("/") + orig; std::string path = std::string(adeptDir) + std::string("/") + orig;
filename = strdup(path.c_str()); filename = strdup(path.c_str());
} }
else else
@@ -450,17 +456,17 @@ int main(int argc, char** argv)
if (hasErrors) if (hasErrors)
{ {
// In case of activation dir was provided by user // In case of adept dir was provided by user
activationDir = 0; adeptDir = 0;
goto end; goto end;
} }
if (activationDir) if (adeptDir)
activationDir = strdup(activationDir); // For below free adeptDir = strdup(adeptDir); // For below free
else else
{ {
activationDir = strdup(deviceFile); adeptDir = strdup(deviceFile);
activationDir = dirname(activationDir); adeptDir = dirname(adeptDir);
} }
ret = loanMGT.run(); ret = loanMGT.run();
@@ -472,8 +478,8 @@ end:
free((void*)*files[i]); free((void*)*files[i]);
} }
if (activationDir) if (adeptDir)
free(activationDir); free(adeptDir);
return ret; return ret;
} }

View File

@@ -27,6 +27,7 @@
*/ */
#include <getopt.h> #include <getopt.h>
#include <libgen.h>
#include <iostream> #include <iostream>
@@ -141,22 +142,30 @@ public:
static void usage(const char* cmd) static void usage(const char* cmd)
{ {
std::cout << "Remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl; std::cout << basename((char*)cmd) << " remove ADEPT DRM (from Adobe) of EPUB/PDF file" << 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|--input-file) file(.epub|pdf)" << std::endl << std::endl; std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file(.epub|pdf)" << std::endl << std::endl;
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl; std::cout << "Global Options:" << std::endl;
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl; std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl; std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl;
std::cout << " " << "-f|--input-file" << "\t" << "EPUB/PDF file to process" << std::endl; std::cout << " " << "-f|--input-file" << "\t" << "Backward compatibility: EPUB/PDF file to process" << std::endl;
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl; std::cout << " " << "-v|--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;
std::cout << "ADEPT Options:" << std::endl;
std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << std::endl;
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;
std::cout << std::endl; std::cout << std::endl;
std::cout << "Environment:" << std::endl;
std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl; std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
std::cout << " * $ADEPT_DIR environment variable" << std::endl;
std::cout << " * /home/<user>/.config/adept" << std::endl;
std::cout << " * Current directory" << std::endl; std::cout << " * Current directory" << std::endl;
std::cout << " * .adept" << std::endl; std::cout << " * .adept" << std::endl;
std::cout << " * adobe-digital-editions directory" << std::endl; std::cout << " * adobe-digital-editions directory" << std::endl;
@@ -169,10 +178,12 @@ int main(int argc, char** argv)
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile}; const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
int verbose = gourou::DRMProcessor::getLogLevel(); int verbose = gourou::DRMProcessor::getLogLevel();
std::string _deviceFile, _activationFile, _devicekeyFile;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"adept-directory", required_argument, 0, 'D' },
{"device-file", required_argument, 0, 'd' }, {"device-file", required_argument, 0, 'd' },
{"activation-file", required_argument, 0, 'a' }, {"activation-file", required_argument, 0, 'a' },
{"device-key-file", required_argument, 0, 'k' }, {"device-key-file", required_argument, 0, 'k' },
@@ -186,12 +197,20 @@ int main(int argc, char** argv)
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
c = getopt_long(argc, argv, "d:a:k:O:o:f:K:vVh", c = getopt_long(argc, argv, "D:d:a:k:O:o:f:K:vVh",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
switch (c) { switch (c) {
case 'D':
_deviceFile = std::string(optarg) + "/device.xml";
_activationFile = std::string(optarg) + "/activation.xml";
_devicekeyFile = std::string(optarg) + "/devicesalt";
deviceFile = _deviceFile.c_str();
activationFile = _activationFile.c_str();
devicekeyFile = _devicekeyFile.c_str();
break;
case 'd': case 'd':
deviceFile = optarg; deviceFile = optarg;
break; break;
@@ -230,6 +249,9 @@ int main(int argc, char** argv)
gourou::DRMProcessor::setLogLevel(verbose); gourou::DRMProcessor::setLogLevel(verbose);
if (optind == argc-1)
inputFile = argv[optind];
if (!inputFile || (outputDir && !outputDir[0]) || if (!inputFile || (outputDir && !outputDir[0]) ||
(outputFile && !outputFile[0])) (outputFile && !outputFile[0]))
{ {

View File

@@ -31,6 +31,8 @@
#include <cctype> #include <cctype>
#include <locale> #include <locale>
#define OPENSSL_NO_DEPRECATED 1
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/pkcs12.h> #include <openssl/pkcs12.h>
#include <openssl/evp.h> #include <openssl/evp.h>
@@ -44,7 +46,6 @@
#include <zip.h> #include <zip.h>
#include <libgourou_common.h> #include <libgourou_common.h>
#include <libgourou_log.h>
#include "drmprocessorclientimpl.h" #include "drmprocessorclientimpl.h"
DRMProcessorClientImpl::DRMProcessorClientImpl(): DRMProcessorClientImpl::DRMProcessorClientImpl():
@@ -81,32 +82,32 @@ void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
if (EVP_DigestInit(md_ctx, md) != 1) if (EVP_DigestInit(md_ctx, md) != 1)
{ {
EVP_MD_CTX_free(md_ctx); EVP_MD_CTX_free(md_ctx);
return 0; EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL));
} }
return md_ctx; 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); int res = EVP_DigestFinal((EVP_MD_CTX *)handler, digestOut, NULL);
EVP_MD_CTX_free((EVP_MD_CTX *)handler); EVP_MD_CTX_free((EVP_MD_CTX *)handler);
return (res == 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); void* handler = createDigest(digestName);
if (!handler) digestUpdate(handler, data, length);
return -1; digestFinalize(handler, digestOut);
if (digestUpdate(handler, data, length))
return -1;
return digestFinalize(handler, digestOut);
} }
/* Random interface */ /* Random interface */
@@ -124,7 +125,7 @@ static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow) curl_off_t ultotal, curl_off_t ulnow)
{ {
// For "big" files only // 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; int percent = 0;
if (dltotal) if (dltotal)
@@ -174,7 +175,7 @@ static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userda
(*responseHeaders)[key] = value; (*responseHeaders)[key] = value;
if (gourou::logLevel >= gourou::DEBUG) if (gourou::logLevel >= gourou::LG_LOG_DEBUG)
std::cout << key << " : " << value << std::endl; std::cout << key << " : " << value << std::endl;
} }
@@ -189,10 +190,10 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
if (!responseHeaders) if (!responseHeaders)
responseHeaders = &localHeaders; responseHeaders = &localHeaders;
GOUROU_LOG(gourou::INFO, "Send request to " << URL); GOUROU_LOG(INFO, "Send request to " << URL);
if (POSTData.size()) if (POSTData.size())
{ {
GOUROU_LOG(gourou::DEBUG, "<<< " << std::endl << POSTData); GOUROU_LOG(DEBUG, "<<< " << std::endl << POSTData);
} }
unsigned prevDownloadedBytes; unsigned prevDownloadedBytes;
@@ -202,11 +203,11 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
struct stat _stat; struct stat _stat;
if (!fstat(fd, &_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; downloadedBytes = _stat.st_size;
} }
else 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(); CURL *curl = curl_easy_init();
@@ -262,7 +263,7 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
// Connexion failed, wait & retry // Connexion failed, wait & retry
if (res == CURLE_COULDNT_CONNECT) 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 // Transfer failed but some data has been received
// --> try again without incrementing tries // --> try again without incrementing tries
@@ -270,11 +271,11 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
{ {
if (prevDownloadedBytes != downloadedBytes) 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--; i--;
} }
else 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 // Other error --> fail
else else
@@ -291,82 +292,116 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res)); EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));
if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) && if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&
gourou::logLevel >= gourou::WARN) gourou::logLevel >= gourou::LG_LOG_WARN)
std::cout << std::endl; std::cout << std::endl;
if ((*responseHeaders)["Content-Type"] == "application/vnd.adobe.adept+xml") 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()); 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, void DRMProcessorClientImpl::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,
const unsigned char* data, unsigned dataLength, const unsigned char* data, unsigned dataLength,
unsigned char* res) unsigned char* res)
{ {
PKCS12 * pkcs12; PKCS12 * pkcs12;
EVP_PKEY* pkey; EVP_PKEY_CTX *ctx;
X509* cert; EVP_PKEY* pkey = NULL;
STACK_OF(X509)* ca; size_t outlen;
RSA * rsa; unsigned char* tmp;
int ret;
pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength); pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);
if (!pkcs12) if (!pkcs12)
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
PKCS12_parse(pkcs12, password.c_str(), &pkey, &cert, &ca); if (PKCS12_parse(pkcs12, password.c_str(), &pkey, NULL, NULL) <= 0)
if (!pkey)
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
rsa = EVP_PKEY_get1_RSA(pkey); outlen = EVP_PKEY_get_size(pkey);
int ret = RSA_private_encrypt(dataLength, data, res, rsa, RSA_PKCS1_PADDING); ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (ret < 0) /* Use RSA private key */
if (EVP_PKEY_decrypt_init(ctx) <= 0)
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
if (gourou::logLevel >= gourou::DEBUG) if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)
{ EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
printf("Encrypted : ");
for(int i=0; i<ret; i++) tmp = (unsigned char*)malloc(outlen);
printf("%02x ", res[i]);
printf("\n"); /* 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, void DRMProcessorClientImpl::RSAPrivateDecrypt(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,
const unsigned char* data, unsigned dataLength, const unsigned char* data, unsigned dataLength,
unsigned char* res) unsigned char* res)
{ {
BIO* mem=BIO_new_mem_buf(RSAKey, RSAKeyLength); BIO* mem = BIO_new_mem_buf(RSAKey, RSAKeyLength);
PKCS8_PRIV_KEY_INFO* p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(mem, NULL); PKCS8_PRIV_KEY_INFO* p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(mem, NULL);
if (!p8inf) if (!p8inf)
EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_INVALID_PKCS8, ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_CTX *ctx;
EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf); EVP_PKEY* pkey = EVP_PKCS82PKEY(p8inf);
RSA * rsa; size_t outlen = dataLength;
int ret; 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)); EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
if (gourou::logLevel >= gourou::DEBUG) if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) <= 0)
{ EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
printf("Decrypted : ");
for(int i=0; i<ret; i++) ret = EVP_PKEY_decrypt(ctx, res, &outlen, data, dataLength);
printf("%02x ", res[i]);
printf("\n"); 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, void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
@@ -374,61 +409,79 @@ void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsig
const unsigned char* data, unsigned dataLength, const unsigned char* data, unsigned dataLength,
unsigned char* res) unsigned char* res)
{ {
size_t outlen;
X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength); X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength);
if (!x509) if (!x509)
EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate"); EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate");
EVP_PKEY_CTX *ctx;
EVP_PKEY * evpKey = X509_get_pubkey(x509); EVP_PKEY * evpKey = X509_get_pubkey(x509);
RSA* rsa = EVP_PKEY_get1_RSA(evpKey);
EVP_PKEY_free(evpKey);
if (!rsa) if (!evpKey)
EXCEPTION(gourou::CLIENT_NO_PRIV_KEY, "No private key in certificate"); EXCEPTION(gourou::CLIENT_NO_PUB_KEY, "No public key in certificate");
int ret = RSA_public_encrypt(dataLength, data, res, rsa, RSA_PKCS1_PADDING); 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);
if (ret < 0) if (ret < 0)
EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
EVP_PKEY_free(evpKey);
} }
void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits) void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits)
{ {
BIGNUM * bn = BN_new(); BIGNUM * bn = BN_new();
RSA * rsa = RSA_new(); EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
BN_set_word(bn, 0x10001); EVP_PKEY *key = NULL;
RSA_generate_key_ex(rsa, keyLengthBits, bn, 0);
BN_free(bn);
return rsa; BN_set_word(bn, 0x10001);
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 key;
} }
void DRMProcessorClientImpl::destroyRSAHandler(void* handler) void DRMProcessorClientImpl::destroyRSAHandler(void* handler)
{ {
RSA_free((RSA*)handler); free(handler);
} }
void DRMProcessorClientImpl::extractRSAPublicKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) 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 *x509_pubkey = 0;
X509_PUBKEY_set(&x509_pubkey, evpKey); X509_PUBKEY_set(&x509_pubkey, (EVP_PKEY*)handler);
*keyOutLength = i2d_X509_PUBKEY(x509_pubkey, keyOut); *keyOutLength = i2d_X509_PUBKEY(x509_pubkey, keyOut);
X509_PUBKEY_free(x509_pubkey); X509_PUBKEY_free(x509_pubkey);
EVP_PKEY_free(evpKey);
} }
void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)
{ {
EVP_PKEY * evpKey = EVP_PKEY_new(); PKCS8_PRIV_KEY_INFO * privKey = EVP_PKEY2PKCS8((EVP_PKEY*)handler);
EVP_PKEY_set1_RSA(evpKey, (RSA*)handler);
PKCS8_PRIV_KEY_INFO * privKey = EVP_PKEY2PKCS8(evpKey);
*keyOutLength = i2d_PKCS8_PRIV_KEY_INFO(privKey, keyOut); *keyOutLength = i2d_PKCS8_PRIV_KEY_INFO(privKey, keyOut);
PKCS8_PRIV_KEY_INFO_free(privKey); PKCS8_PRIV_KEY_INFO_free(privKey);
EVP_PKEY_free(evpKey);
} }
void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength, void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength,
@@ -438,12 +491,11 @@ void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, uns
PKCS12 * pkcs12; PKCS12 * pkcs12;
EVP_PKEY* pkey = 0; EVP_PKEY* pkey = 0;
X509* cert = 0; X509* cert = 0;
STACK_OF(X509)* ca;
pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength); pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);
if (!pkcs12) if (!pkcs12)
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL)); 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) if (!cert)
EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL)); EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
@@ -454,24 +506,27 @@ void DRMProcessorClientImpl::extractCertificate(const unsigned char* RSAKey, uns
} }
/* Crypto interface */ /* 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength, const unsigned char* iv, unsigned int ivLength,
const unsigned char* dataIn, unsigned int dataInLength, const unsigned char* dataIn, unsigned int dataInLength,
unsigned char* dataOut, unsigned int* dataOutLength) unsigned char* dataOut, unsigned int* dataOutLength)
{ {
void* handler = EncryptInit(algo, chaining, key, keyLength, iv, ivLength); void* handler = encryptInit(algo, chaining, key, keyLength, iv, ivLength);
EncryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength); encryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
EncryptFinalize(handler, dataOut+*dataOutLength, 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength) const unsigned char* iv, unsigned int ivLength)
{ {
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int ret = 0;
if (algo == ALGO_AES)
switch (algo)
{
case ALGO_AES:
{ {
switch(keyLength) switch(keyLength)
{ {
@@ -479,10 +534,10 @@ void* DRMProcessorClientImpl::EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaini
switch(chaining) switch(chaining)
{ {
case CHAIN_ECB: case CHAIN_ECB:
EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, iv); ret = EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, iv);
break; break;
case CHAIN_CBC: case CHAIN_CBC:
EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv); ret = EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv);
break; break;
default: default:
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining); EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
@@ -492,98 +547,134 @@ void* DRMProcessorClientImpl::EncryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaini
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);
} }
break;
} }
else if (algo == ALGO_RC4) case ALGO_RC4:
{ {
if (keyLength != 16) if (keyLength != 16)
{ {
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);
} }
EVP_DecryptInit(ctx, EVP_rc4(), key, iv); ret = EVP_DecryptInit(ctx, EVP_rc4(), key, iv);
break;
} }
return ctx;
}
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();
if (algo == ALGO_AES)
{
switch(keyLength)
{
case 16:
switch(chaining)
{
case CHAIN_ECB:
EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, iv);
break;
case CHAIN_CBC:
EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);
break;
default:
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
}
break;
default:
EVP_CIPHER_CTX_free(ctx);
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
}
} }
else if (algo == ALGO_RC4)
if (ret <= 0)
{ {
if (keyLength != 16) EVP_CIPHER_CTX_free(ctx);
{ EXCEPTION(gourou::CLIENT_CRYPT_ERROR, ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_free(ctx);
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
}
EVP_DecryptInit(ctx, EVP_rc4(), key, iv);
} }
return ctx; return ctx;
} }
void DRMProcessorClientImpl::EncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, 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;
switch(algo)
{
case ALGO_AES:
{
switch(keyLength)
{
case 16:
switch(chaining)
{
case CHAIN_ECB:
ret = EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, iv);
break;
case CHAIN_CBC:
ret = EVP_DecryptInit(ctx, EVP_aes_128_cbc(), key, iv);
break;
default:
EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
}
break;
default:
EVP_CIPHER_CTX_free(ctx);
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
}
break;
}
case ALGO_RC4:
{
if (keyLength != 16)
{
EVP_CIPHER_CTX_free(ctx);
EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
}
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,
unsigned char* dataOut, unsigned int* dataOutLength) 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) unsigned char* dataOut, unsigned int* dataOutLength)
{ {
int len; int len, ret;
EVP_EncryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
ret = EVP_EncryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
*dataOutLength += len; *dataOutLength += len;
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler); 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength, const unsigned char* iv, unsigned int ivLength,
const unsigned char* dataIn, unsigned int dataInLength, const unsigned char* dataIn, unsigned int dataInLength,
unsigned char* dataOut, unsigned int* dataOutLength) unsigned char* dataOut, unsigned int* dataOutLength)
{ {
void* handler = DecryptInit(algo, chaining, key, keyLength, iv, ivLength); void* handler = decryptInit(algo, chaining, key, keyLength, iv, ivLength);
DecryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength); decryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
DecryptFinalize(handler, dataOut+*dataOutLength, 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) 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; int len, ret;
EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
ret = EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
*dataOutLength += len; *dataOutLength += len;
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler); 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) void* DRMProcessorClientImpl::zipOpen(const std::string& path)

View File

@@ -45,9 +45,9 @@ public:
/* Digest interface */ /* Digest interface */
virtual void* createDigest(const std::string& digestName); virtual void* createDigest(const std::string& digestName);
virtual int digestUpdate(void* handler, unsigned char* data, unsigned int length); virtual void digestUpdate(void* handler, unsigned char* data, unsigned int length);
virtual int digestFinalize(void* handler,unsigned char* digestOut); virtual void digestFinalize(void* handler,unsigned char* digestOut);
virtual int digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut); virtual void digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut);
/* Random interface */ /* Random interface */
virtual void randBytes(unsigned char* bytesOut, unsigned int length); virtual void randBytes(unsigned char* bytesOut, unsigned int length);
@@ -80,34 +80,34 @@ public:
unsigned char** certOut, unsigned int* certOutLength); unsigned char** certOut, unsigned int* certOutLength);
/* Crypto interface */ /* 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength, const unsigned char* iv, unsigned int ivLength,
const unsigned char* dataIn, unsigned int dataInLength, const unsigned char* dataIn, unsigned int dataInLength,
unsigned char* dataOut, unsigned int* dataOutLength); unsigned char* dataOut, unsigned int* dataOutLength);
virtual void* 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* key, unsigned int keyLength,
const unsigned char* iv=0, unsigned int ivLength=0); 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); 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* key, unsigned int keyLength,
const unsigned char* iv, unsigned int ivLength, const unsigned char* iv, unsigned int ivLength,
const unsigned char* dataIn, unsigned int dataInLength, const unsigned char* dataIn, unsigned int dataInLength,
unsigned char* dataOut, unsigned int* dataOutLength); unsigned char* dataOut, unsigned int* dataOutLength);
virtual void* 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* key, unsigned int keyLength,
const unsigned char* iv=0, unsigned int ivLength=0); 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); 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 */ /* ZIP Interface */
virtual void* zipOpen(const std::string& path); virtual void* zipOpen(const std::string& path);
@@ -127,6 +127,10 @@ public:
int wbits=-15, int compressionLevel=8); int wbits=-15, int compressionLevel=8);
private: private:
void padWithPKCS1(unsigned char* out, unsigned int outLength,
const unsigned char* in, unsigned int inLength);
#if OPENSSL_VERSION_MAJOR >= 3 #if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER *legacy, *deflt; OSSL_PROVIDER *legacy, *deflt;
#else #else

40
utils/launcher.cpp Normal file
View 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;
}

View File

@@ -0,0 +1,61 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH ACSMDOWNLOADER "1" "January 2023" "acsmdownloader download EPUB file from ACSM request file" "User Commands"
.SH NAME
acsmdownloader \- download EPUB file from ACSM request file
.SH SYNOPSIS
.B acsmdownloader
[\fI\,OPTIONS\/\fR] \fI\,file.acsm\/\fR
.SH DESCRIPTION
Download EPUB file from ACSM request file
.SS "Global Options:"
.TP
\fB\-O\fR|\-\-output\-dir
Optional output directory were to put result (default ./)
.TP
\fB\-o\fR|\-\-output\-file
Optional output filename (default <title.(epub|pdf|der)>)
.TP
\fB\-f\fR|\-\-acsm\-file
Backward compatibility: ACSM request file for epub download
.TP
\fB\-e\fR|\-\-export\-private\-key
Export private key in DER format
.TP
\fB\-r\fR|\-\-resume
Try to resume download (in case of previous failure)
.TP
\fB\-v\fR|\-\-verbose
Increase verbosity, can be set multiple times
.TP
\fB\-V\fR|\-\-version
Display libgourou version
.TP
\fB\-h\fR|\-\-help
This help
.SS "ADEPT Options:"
.TP
\fB\-D\fR|\-\-adept\-directory
\&.adept directory that must contains device.xml, activation.xml and devicesalt
.TP
\fB\-d\fR|\-\-device\-file
device.xml file from eReader
.TP
\fB\-a\fR|\-\-activation\-file
activation.xml file from eReader
.TP
\fB\-k\fR|\-\-device\-key\-file
private device key file (eg devicesalt/devkey.bin) from eReader
.SH ENVIRONMENT
Device file, activation file and device key file are optionals. If not set, they are looked into :
.IP
* $ADEPT_DIR environment variable
.IP
* /home/<user>/.config/adept
.IP
* Current directory
.IP
* .adept
.IP
* adobe\-digital\-editions directory
.IP
* .adobe\-digital\-editions directory

View File

@@ -0,0 +1,67 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH ADEPT_ACTIVATE "1" "January 2023" "adept_activate create new device files used by ADEPT DRM" "User Commands"
.SH NAME
adept_activate create new device files used by ADEPT DRM
.SH SYNOPSIS
.B adept_activate
\fI\,OPTIONS\/\fR
.SH DESCRIPTION
Create new device files used by ADEPT DRM
.SS "Global Options:"
.TP
\fB\-a\fR|\-\-anonymous
Anonymous account, no need for username/password (Use it only with a DRM removal software)
.TP
\fB\-u\fR|\-\-username
AdobeID username (ie adobe.com email account)
.TP
\fB\-p\fR|\-\-password
AdobeID password (asked if not set via command line)
.TP
\fB\-O\fR|\-\-output\-dir
Optional output directory were to put result (default ./.adept). This directory must not already exists
.TP
\fB\-H\fR|\-\-hobbes\-version
Force RMSDK version to a specific value (default: version of current librmsdk)
.TP
\fB\-r\fR|\-\-random\-serial
Generate a random device serial (if not set, it will be dependent of your current configuration)
.TP
\fB\-v\fR|\-\-verbose
Increase verbosity, can be set multiple times
.TP
\fB\-V\fR|\-\-version
Display libgourou version
.TP
\fB\-h\fR|\-\-help
This help
.PP
Usage: adept_activate OPTIONS
.SS "Global Options:"
.TP
\fB\-a\fR|\-\-anonymous
Anonymous account, no need for username/password (Use it only with a DRM removal software)
.TP
\fB\-u\fR|\-\-username
AdobeID username (ie adobe.com email account)
.TP
\fB\-p\fR|\-\-password
AdobeID password (asked if not set via command line)
.TP
\fB\-O\fR|\-\-output\-dir
Optional output directory were to put result (default ./.adept). This directory must not already exists
.TP
\fB\-H\fR|\-\-hobbes\-version
Force RMSDK version to a specific value (default: version of current librmsdk)
.TP
\fB\-r\fR|\-\-random\-serial
Generate a random device serial (if not set, it will be dependent of your current configuration)
.TP
\fB\-v\fR|\-\-verbose
Increase verbosity, can be set multiple times
.TP
\fB\-V\fR|\-\-version
Display libgourou version
.TP
\fB\-h\fR|\-\-help
This help

View File

@@ -0,0 +1,46 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH ADEPT_LOAN_MGT "1" "January 2023" "adept_loan_mgt manage loaned books" "User Commands"
.SH NAME
adept_loan_mgt manage loaned books
.SH SYNOPSIS
.B adept_loan_mgt
[\fI\,OPTIONS\/\fR]
.SH DESCRIPTION
Manage ADEPT loaned books
.SS "Global Options:"
.TP
\fB\-l\fR|\-\-list
List all loaned books
.TP
\fB\-r\fR|\-\-return
Return a loaned book
.TP
\fB\-d\fR|\-\-delete
Delete a loan entry without returning it
.TP
\fB\-v\fR|\-\-verbose
Increase verbosity, can be set multiple times
.TP
\fB\-V\fR|\-\-version
Display libgourou version
.TP
\fB\-h\fR|\-\-help
This help
.SS "ADEPT Options:"
.TP
\fB\-D\fR|\-\-adept\-directory
\&.adept directory that must contains device.xml, activation.xml and devicesalt
.SH ENVIRONMENT
.SS "ADEPT directory is optional. If not set, it's looked into :"
.IP
* $ADEPT_DIR environment variable
.IP
* /home/<user>/.config/adept
.IP
* Current directory
.IP
* .adept
.IP
* adobe\-digital\-editions directory
.IP
* .adobe\-digital\-editions directory

55
utils/man/adept_remove.1 Normal file
View File

@@ -0,0 +1,55 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH ADEPT_REMOVE "1" "January 2023" "adept_remove remove ADEPT DRM (from Adobe) of EPUB/PDF file" "User Commands"
.SH NAME
adept_remove remove ADEPT DRM (from Adobe) of EPUB/PDF file
.SH SYNOPSIS
.B adept_remove
[\fI\,OPTIONS\/\fR] \fI\,file(.epub|pdf)\/\fR
.SH DESCRIPTION
Remove ADEPT DRM (from Adobe) of EPUB/PDF file
.SS "Global Options:"
.TP
\fB\-O\fR|\-\-output\-dir
Optional output directory were to put result (default ./)
.TP
\fB\-o\fR|\-\-output\-file
Optional output filename (default inplace DRM removal>)
.TP
\fB\-f\fR|\-\-input\-file
Backward compatibility: EPUB/PDF file to process
.TP
\fB\-v\fR|\-\-verbose
Increase verbosity, can be set multiple times
.TP
\fB\-V\fR|\-\-version
Display libgourou version
.TP
\fB\-h\fR|\-\-help
This help
.SS "ADEPT Options:"
.TP
\fB\-D\fR|\-\-adept\-directory
\&.adept directory that must contains device.xml, activation.xml and devicesalt
.TP
\fB\-d\fR|\-\-device\-file
device.xml file from eReader
.TP
\fB\-a\fR|\-\-activation\-file
activation.xml file from eReader
.TP
\fB\-k\fR|\-\-device\-key\-file
private device key file (eg devicesalt/devkey.bin) from eReader
.SH ENVIRONMENT
.SS "Device file, activation file and device key file are optionals. If not set, they are looked into :"
.IP
* $ADEPT_DIR environment variable
.IP
* /home/<user>/.config/adept
.IP
* Current directory
.IP
* .adept
.IP
* adobe\-digital\-editions directory
.IP
* .adobe\-digital\-editions directory

View File

@@ -61,6 +61,20 @@ bool fileExists(const char* filename)
const char* findFile(const char* filename, bool inDefaultDirs) const char* findFile(const char* filename, bool inDefaultDirs)
{ {
std::string path;
const char* adeptDir = getenv("ADEPT_DIR");
if (adeptDir && adeptDir[0])
{
path = adeptDir + std::string("/") + filename;
if (fileExists(path.c_str()))
return strdup(path.c_str());
}
path = gourou::DRMProcessor::getDefaultAdeptDir() + filename;
if (fileExists(path.c_str()))
return strdup(path.c_str());
if (fileExists(filename)) if (fileExists(filename))
return strdup(filename); return strdup(filename);
@@ -68,7 +82,7 @@ const char* findFile(const char* filename, bool inDefaultDirs)
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
{ {
std::string path = std::string(defaultDirs[i]) + filename; path = std::string(defaultDirs[i]) + filename;
if (fileExists(path.c_str())) if (fileExists(path.c_str()))
return strdup(path.c_str()); return strdup(path.c_str());
} }
@@ -98,8 +112,8 @@ void mkpath(const char *dir)
void fileCopy(const char* in, const char* out) void fileCopy(const char* in, const char* out)
{ {
char buffer[4096]; char buffer[4096], *_buffer;
int ret, fdIn, fdOut; int ret, ret2, fdIn, fdOut;
fdIn = open(in, O_RDONLY); fdIn = open(in, O_RDONLY);
@@ -119,7 +133,20 @@ void fileCopy(const char* in, const char* out)
ret = ::read(fdIn, buffer, sizeof(buffer)); ret = ::read(fdIn, buffer, sizeof(buffer));
if (ret <= 0) if (ret <= 0)
break; break;
::write(fdOut, buffer, ret); do
{
_buffer = buffer;
ret2 = ::write(fdOut, _buffer, ret);
if (ret2 >= 0)
{
ret -= ret2;
_buffer += ret2;
}
else
{
EXCEPTION(gourou::CLIENT_FILE_ERROR, "Error writing " << out);
}
} while (ret);
} }
close (fdIn); close (fdIn);