diff --git a/include/libgourou.h b/include/libgourou.h index 1715a7c..9f9a2d8 100644 --- a/include/libgourou.h +++ b/include/libgourou.h @@ -185,10 +185,16 @@ namespace gourou DRMProcessorClient* getClient() { return client; } /** - * @brief Remove ADEPT DRM. + * @brief Remove ADEPT DRM * 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: gourou::DRMProcessorClient* client; @@ -214,12 +220,12 @@ namespace gourou void fetchLicenseServiceCertificate(const std::string& licenseURL, const std::string& operatorURL); 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, const unsigned char* masterKey, unsigned int masterKeyLength, int objectId, int objectGenerationNumber, 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); }; } diff --git a/include/libgourou_common.h b/include/libgourou_common.h index d9747c7..ad3c3eb 100644 --- a/include/libgourou_common.h +++ b/include/libgourou_common.h @@ -92,6 +92,7 @@ namespace gourou USER_INVALID_ACTIVATION_FILE, USER_NO_AUTHENTICATION_URL, USER_NO_PROPERTY, + USER_INVALID_INPUT, }; enum FULFILL_ITEM_ERROR { diff --git a/src/libgourou.cpp b/src/libgourou.cpp index 0397045..cee9d9e 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -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; bool removeEncryptionXML = true; @@ -962,7 +963,16 @@ namespace gourou std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey"); 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); pugi::xml_document encryptionDoc; @@ -1001,7 +1011,7 @@ namespace gourou unsigned int dataOutLength; 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], zipData.length()-16, _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; bool EBXHandlerFound = false; - + 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 @@ -1134,7 +1145,17 @@ namespace gourou 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(); break; @@ -1149,7 +1170,7 @@ namespace gourou for(it = objects.begin(); it != objects.end(); it++) { uPDFParser::Object* object = *it; - + if (object->objectId() == ebxId) { // object->deleteKey("Filter"); @@ -1168,7 +1189,7 @@ namespace gourou unsigned char tmpKey[16]; generatePDFObjectKey(ebxVersion->value(), - decryptedKey+RSA_KEY_SIZE-16, 16, + decryptedKey+sizeof(decryptedKey)-16, 16, object->objectId(), object->generationNumber(), tmpKey); @@ -1233,6 +1254,8 @@ namespace gourou clearData, &dataOutLength); 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, - ITEM_TYPE type) + ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize) { if (type == PDF) - removePDFDRM(filenameIn, filenameOut); + removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize); else - removeEPubDRM(filenameIn, filenameOut); + removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize); } } diff --git a/utils/adept_remove.cpp b/utils/adept_remove.cpp index ed857da..6a1a838 100644 --- a/utils/adept_remove.cpp +++ b/utils/adept_remove.cpp @@ -55,6 +55,23 @@ static const char* defaultDirs[] = { "./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) { @@ -110,7 +127,7 @@ public: { 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; } else @@ -121,7 +138,7 @@ public: QTemporaryFile tempFile; tempFile.open(); 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... */ QFile origFile(inputFile); origFile.remove(); @@ -132,7 +149,7 @@ public: tempFile.setAutoRemove(true); } else - processor.removeDRM(inputFile, filename, type); + processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); std::cout << "DRM removed from " << filename << std::endl; } } catch(std::exception& e) @@ -213,14 +230,14 @@ int main(int argc, char** argv) {"output-dir", required_argument, 0, 'O' }, {"output-file", required_argument, 0, 'o' }, {"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' }, {"version", no_argument, 0, 'V' }, {"help", no_argument, 0, 'h' }, {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); if (c == -1) break; @@ -244,6 +261,9 @@ int main(int argc, char** argv) case 'o': outputFile = optarg; break; + case 'K': + encryptionKeyUser = optarg; + break; case 'v': verbose++; 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