forked from soutade/libgourou
		
	Add a private option into adept_remove to provide encryption key
This commit is contained in:
		| @@ -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); | ||||
|     }; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -92,6 +92,7 @@ namespace gourou | ||||
| 	USER_INVALID_ACTIVATION_FILE, | ||||
| 	USER_NO_AUTHENTICATION_URL, | ||||
| 	USER_NO_PROPERTY, | ||||
| 	USER_INVALID_INPUT, | ||||
|     }; | ||||
|  | ||||
|     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; | ||||
| 	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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<size; i+=2) | ||||
| 	{ | ||||
| 	    encryptionKey[i/2]  = htoi(encryptionKeyUser[i]) << 4; | ||||
| 	    encryptionKey[i/2] |= htoi(encryptionKeyUser[i+1]); | ||||
| 	} | ||||
|  | ||||
| 	encryptionKeySize = size/2; | ||||
|     } | ||||
|      | ||||
|     if (hasErrors) | ||||
| 	goto end; | ||||
|         | ||||
| @@ -300,5 +346,8 @@ end: | ||||
| 	    free((void*)*files[i]); | ||||
|     } | ||||
|  | ||||
|     if (encryptionKey) | ||||
| 	free(encryptionKey); | ||||
|      | ||||
|     return ret; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user