Add a private option into adept_remove to provide encryption key

This commit is contained in:
Grégory Soutadé 2022-03-12 23:04:16 +01:00
parent 9b946a62b4
commit 0f475423c0
4 changed files with 100 additions and 21 deletions

View File

@ -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);
}; };
} }

View File

@ -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 {

View File

@ -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);
} }
} }

View File

@ -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;
} }