forked from soutade/libgourou
Add a private option into adept_remove to provide encryption key
This commit is contained in:
parent
9b946a62b4
commit
0f475423c0
|
@ -185,10 +185,16 @@ namespace gourou
|
||||||
DRMProcessorClient* getClient() { return client; }
|
DRMProcessorClient* getClient() { return client; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove ADEPT DRM.
|
* @brief Remove ADEPT DRM
|
||||||
* Warning: for PDF format, filenameIn must be different than filenameOut
|
* Warning: for PDF format, filenameIn must be different than filenameOut
|
||||||
|
*
|
||||||
|
* @param filenameIn Input file (with ADEPT DRM)
|
||||||
|
* @param filenameOut Output file (without ADEPT DRM)
|
||||||
|
* @param type Type of file (ePub or PDF)
|
||||||
|
* @param encryptionKey Optional encryption key, do not try to decrypt the one inside input file
|
||||||
|
* @param encryptionKeySize Size of encryption key (if provided)
|
||||||
*/
|
*/
|
||||||
void removeDRM(const std::string& filenameIn, const std::string& filenameOut, ITEM_TYPE type);
|
void removeDRM(const std::string& filenameIn, const std::string& filenameOut, ITEM_TYPE type, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
gourou::DRMProcessorClient* client;
|
gourou::DRMProcessorClient* client;
|
||||||
|
@ -214,12 +220,12 @@ namespace gourou
|
||||||
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
||||||
const std::string& operatorURL);
|
const std::string& operatorURL);
|
||||||
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey);
|
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey);
|
||||||
void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut);
|
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,
|
||||||
int objectId, int objectGenerationNumber,
|
int objectId, int objectGenerationNumber,
|
||||||
unsigned char* keyOut);
|
unsigned char* keyOut);
|
||||||
void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut);
|
void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@ namespace gourou
|
||||||
USER_INVALID_ACTIVATION_FILE,
|
USER_INVALID_ACTIVATION_FILE,
|
||||||
USER_NO_AUTHENTICATION_URL,
|
USER_NO_AUTHENTICATION_URL,
|
||||||
USER_NO_PROPERTY,
|
USER_NO_PROPERTY,
|
||||||
|
USER_INVALID_INPUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FULFILL_ITEM_ERROR {
|
enum FULFILL_ITEM_ERROR {
|
||||||
|
|
|
@ -949,7 +949,8 @@ namespace gourou
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
ByteArray zipData;
|
ByteArray zipData;
|
||||||
bool removeEncryptionXML = true;
|
bool removeEncryptionXML = true;
|
||||||
|
@ -962,7 +963,16 @@ namespace gourou
|
||||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||||
unsigned char decryptedKey[RSA_KEY_SIZE];
|
unsigned char decryptedKey[RSA_KEY_SIZE];
|
||||||
|
|
||||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
if (!encryptionKey)
|
||||||
|
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
||||||
|
if (encryptionKeySize != 16)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
|
||||||
|
|
||||||
|
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
|
||||||
|
}
|
||||||
|
|
||||||
client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData);
|
client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData);
|
||||||
pugi::xml_document encryptionDoc;
|
pugi::xml_document encryptionDoc;
|
||||||
|
@ -1001,7 +1011,7 @@ namespace gourou
|
||||||
unsigned int dataOutLength;
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
client->Decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||||
decryptedKey+RSA_KEY_SIZE-16, 16, /* Key */
|
decryptedKey+sizeof(decryptedKey)-16, 16, /* Key */
|
||||||
_data, 16, /* IV */
|
_data, 16, /* IV */
|
||||||
&_data[16], zipData.length()-16,
|
&_data[16], zipData.length()-16,
|
||||||
_clearData, &dataOutLength);
|
_clearData, &dataOutLength);
|
||||||
|
@ -1068,14 +1078,15 @@ namespace gourou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut)
|
void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
|
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
{
|
{
|
||||||
uPDFParser::Parser parser;
|
uPDFParser::Parser parser;
|
||||||
bool EBXHandlerFound = false;
|
bool EBXHandlerFound = false;
|
||||||
|
|
||||||
if (filenameIn == filenameOut)
|
if (filenameIn == filenameOut)
|
||||||
{
|
{
|
||||||
EXCEPTION(DRM_IN_OUT_EQUALS, "PDF IN must be different of PDF OUT");
|
EXCEPTION(DRM_IN_OUT_EQUALS, "PDF IN must be different of PDF OUT");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -1134,7 +1145,17 @@ namespace gourou
|
||||||
|
|
||||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||||
|
|
||||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
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();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1149,7 +1170,7 @@ namespace gourou
|
||||||
for(it = objects.begin(); it != objects.end(); it++)
|
for(it = objects.begin(); it != objects.end(); it++)
|
||||||
{
|
{
|
||||||
uPDFParser::Object* object = *it;
|
uPDFParser::Object* object = *it;
|
||||||
|
|
||||||
if (object->objectId() == ebxId)
|
if (object->objectId() == ebxId)
|
||||||
{
|
{
|
||||||
// object->deleteKey("Filter");
|
// object->deleteKey("Filter");
|
||||||
|
@ -1168,7 +1189,7 @@ namespace gourou
|
||||||
unsigned char tmpKey[16];
|
unsigned char tmpKey[16];
|
||||||
|
|
||||||
generatePDFObjectKey(ebxVersion->value(),
|
generatePDFObjectKey(ebxVersion->value(),
|
||||||
decryptedKey+RSA_KEY_SIZE-16, 16,
|
decryptedKey+sizeof(decryptedKey)-16, 16,
|
||||||
object->objectId(), object->generationNumber(),
|
object->objectId(), object->generationNumber(),
|
||||||
tmpKey);
|
tmpKey);
|
||||||
|
|
||||||
|
@ -1233,6 +1254,8 @@ namespace gourou
|
||||||
clearData, &dataOutLength);
|
clearData, &dataOutLength);
|
||||||
|
|
||||||
stream->setData(clearData, dataOutLength, true);
|
stream->setData(clearData, dataOutLength, true);
|
||||||
|
if (dataOutLength != dataLength)
|
||||||
|
GOUROU_LOG(DEBUG, "New size " << dataOutLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1243,11 +1266,11 @@ namespace gourou
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut,
|
void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
ITEM_TYPE type)
|
ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
{
|
{
|
||||||
if (type == PDF)
|
if (type == PDF)
|
||||||
removePDFDRM(filenameIn, filenameOut);
|
removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);
|
||||||
else
|
else
|
||||||
removeEPubDRM(filenameIn, filenameOut);
|
removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,23 @@ static const char* defaultDirs[] = {
|
||||||
"./adobe-digital-editions/",
|
"./adobe-digital-editions/",
|
||||||
"./.adobe-digital-editions/"
|
"./.adobe-digital-editions/"
|
||||||
};
|
};
|
||||||
|
static char* encryptionKeyUser = 0;
|
||||||
|
static unsigned char* encryptionKey = 0;
|
||||||
|
static unsigned encryptionKeySize = 0;
|
||||||
|
|
||||||
|
static inline unsigned char htoi(unsigned char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
c -= '0';
|
||||||
|
else if (c >= 'a' && c <= 'f')
|
||||||
|
c -= 'a' - 10;
|
||||||
|
else if (c >= 'A' && c <= 'F')
|
||||||
|
c -= 'A' - 10;
|
||||||
|
else
|
||||||
|
EXCEPTION(gourou::USER_INVALID_INPUT, "Invalid character " << c << " in encryption key");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool endsWith(const std::string& s, const std::string& suffix)
|
static inline bool endsWith(const std::string& s, const std::string& suffix)
|
||||||
{
|
{
|
||||||
|
@ -110,7 +127,7 @@ public:
|
||||||
{
|
{
|
||||||
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << inputFile << " into " << filename);
|
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << inputFile << " into " << filename);
|
||||||
}
|
}
|
||||||
processor.removeDRM(inputFile, filename, type);
|
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||||
std::cout << "DRM removed into new file " << filename << std::endl;
|
std::cout << "DRM removed into new file " << filename << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -121,7 +138,7 @@ public:
|
||||||
QTemporaryFile tempFile;
|
QTemporaryFile tempFile;
|
||||||
tempFile.open();
|
tempFile.open();
|
||||||
tempFile.setAutoRemove(false); // In case of failure
|
tempFile.setAutoRemove(false); // In case of failure
|
||||||
processor.removeDRM(inputFile, tempFile.fileName().toStdString(), type);
|
processor.removeDRM(inputFile, tempFile.fileName().toStdString(), type, encryptionKey, encryptionKeySize);
|
||||||
/* Original file must be removed before doing a copy... */
|
/* Original file must be removed before doing a copy... */
|
||||||
QFile origFile(inputFile);
|
QFile origFile(inputFile);
|
||||||
origFile.remove();
|
origFile.remove();
|
||||||
|
@ -132,7 +149,7 @@ public:
|
||||||
tempFile.setAutoRemove(true);
|
tempFile.setAutoRemove(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
processor.removeDRM(inputFile, filename, type);
|
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||||
std::cout << "DRM removed from " << filename << std::endl;
|
std::cout << "DRM removed from " << filename << std::endl;
|
||||||
}
|
}
|
||||||
} catch(std::exception& e)
|
} catch(std::exception& e)
|
||||||
|
@ -213,14 +230,14 @@ int main(int argc, char** argv)
|
||||||
{"output-dir", required_argument, 0, 'O' },
|
{"output-dir", required_argument, 0, 'O' },
|
||||||
{"output-file", required_argument, 0, 'o' },
|
{"output-file", required_argument, 0, 'o' },
|
||||||
{"input-file", required_argument, 0, 'f' },
|
{"input-file", required_argument, 0, 'f' },
|
||||||
{"export-private-key",no_argument, 0, 'e' },
|
{"encryption-key", required_argument, 0, 'K' }, // Private option
|
||||||
{"verbose", no_argument, 0, 'v' },
|
{"verbose", no_argument, 0, 'v' },
|
||||||
{"version", no_argument, 0, 'V' },
|
{"version", no_argument, 0, 'V' },
|
||||||
{"help", no_argument, 0, 'h' },
|
{"help", no_argument, 0, 'h' },
|
||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "d:a:k:O:o:f:evVh",
|
c = getopt_long(argc, argv, "d:a:k:O:o:f:K:vVh",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -244,6 +261,9 @@ int main(int argc, char** argv)
|
||||||
case 'o':
|
case 'o':
|
||||||
outputFile = optarg;
|
outputFile = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'K':
|
||||||
|
encryptionKeyUser = optarg;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
break;
|
break;
|
||||||
|
@ -286,6 +306,32 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyUser)
|
||||||
|
{
|
||||||
|
int size = std::string(encryptionKeyUser).size();
|
||||||
|
if ((size % 2))
|
||||||
|
{
|
||||||
|
std::cout << "Error : Encryption key must be odd length" << std::endl;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyUser[0] == '0' && encryptionKeyUser[1] == 'x')
|
||||||
|
{
|
||||||
|
encryptionKeyUser += 2;
|
||||||
|
size -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKey = new unsigned char[size/2];
|
||||||
|
|
||||||
|
for(i=0; i<size; i+=2)
|
||||||
|
{
|
||||||
|
encryptionKey[i/2] = htoi(encryptionKeyUser[i]) << 4;
|
||||||
|
encryptionKey[i/2] |= htoi(encryptionKeyUser[i+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKeySize = size/2;
|
||||||
|
}
|
||||||
|
|
||||||
if (hasErrors)
|
if (hasErrors)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
@ -300,5 +346,8 @@ end:
|
||||||
free((void*)*files[i]);
|
free((void*)*files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encryptionKey)
|
||||||
|
free(encryptionKey);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user