From 2ac917619ed2187f48a7c7bf467250929ef047aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Wed, 25 Aug 2021 21:53:54 +0200 Subject: [PATCH] Add "export private key" feature --- include/libgourou.h | 2 + src/libgourou.cpp | 13 +++++ utils/acsmdownloader.cpp | 118 ++++++++++++++++++++++++++------------- 3 files changed, 94 insertions(+), 39 deletions(-) diff --git a/include/libgourou.h b/include/libgourou.h index f949215..ce9892e 100644 --- a/include/libgourou.h +++ b/include/libgourou.h @@ -164,6 +164,8 @@ namespace gourou */ std::string serializeRSAPrivateKey(void* rsa); + void exportPrivateLicenseKey(std::string path); + /** * @brief Get current user */ diff --git a/src/libgourou.cpp b/src/libgourou.cpp index 97cd166..4d41649 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -899,6 +899,19 @@ namespace gourou return res.toBase64(); } + void DRMProcessor::exportPrivateLicenseKey(std::string path) + { + int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); + if (fd <= 0) + EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path); + + ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey()); + /* In adobekey.py, we get base64 decoded data [26:] */ + write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-1-26); + + close(fd); + } + int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;} void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;} } diff --git a/utils/acsmdownloader.cpp b/utils/acsmdownloader.cpp index 3c67567..c6e6ff3 100644 --- a/utils/acsmdownloader.cpp +++ b/utils/acsmdownloader.cpp @@ -46,6 +46,7 @@ static const char* deviceFile = "device.xml"; static const char* activationFile = "activation.xml"; static const char* devicekeyFile = "devicesalt"; static const char* acsmFile = 0; +static bool exportPrivateKey = false; static const char* outputFile = 0; static const char* outputDir = 0; static const char* defaultDirs[] = { @@ -71,42 +72,65 @@ public: { DRMProcessorClientImpl client; gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); - - gourou::FulfillmentItem* item = processor.fulfill(acsmFile); - - std::string filename; - if (!outputFile) + gourou::User* user = processor.getUser(); + + if (exportPrivateKey) { - filename = item->getMetadata("title"); - if (filename == "") - filename = "output"; + std::string filename; + if (!outputFile) + filename = std::string("Adobe_PrivateLicenseKey--") + user->getUsername() + ".der"; + + if (outputDir) + { + QDir dir(outputDir); + if (!dir.exists(outputDir)) + dir.mkpath(outputDir); + + filename = std::string(outputDir) + "/" + filename; + } + + processor.exportPrivateLicenseKey(filename); + + std::cout << "Private license key exported to " << filename << std::endl; } else - filename = outputFile; - - if (outputDir) { - QDir dir(outputDir); - if (!dir.exists(outputDir)) - dir.mkpath(outputDir); + gourou::FulfillmentItem* item = processor.fulfill(acsmFile); - filename = std::string(outputDir) + "/" + filename; - } - - gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename); - - if (!outputFile) - { - std::string finalName = filename; - if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) - finalName += ".pdf"; + std::string filename; + if (!outputFile) + { + filename = item->getMetadata("title"); + if (filename == "") + filename = "output"; + } else - finalName += ".epub"; - QDir dir; - dir.rename(filename.c_str(), finalName.c_str()); - filename = finalName; + filename = outputFile; + + if (outputDir) + { + QDir dir(outputDir); + if (!dir.exists(outputDir)) + dir.mkpath(outputDir); + + filename = std::string(outputDir) + "/" + filename; + } + + gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename); + + if (!outputFile) + { + std::string finalName = filename; + if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) + finalName += ".pdf"; + else + finalName += ".epub"; + QDir dir; + dir.rename(filename.c_str(), finalName.c_str()); + filename = finalName; + } + std::cout << "Created " << filename << std::endl; } - std::cout << "Created " << filename << std::endl; } catch(std::exception& e) { std::cout << e.what() << std::endl; @@ -149,14 +173,15 @@ static void usage(const char* cmd) { std::cout << "Download EPUB file from ACSM request file" << std::endl; - std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-s|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output.epub] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm" << std::endl << std::endl; + std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-s|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << 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-file" << "\t" << "Optional output epub filename (default )" << std::endl; + std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default )" << std::endl; std::cout << " " << "-f|--acsm-file" << "\t" << "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 << " " << "-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 << " " << "-h|--help" << "\t\t" << "This help" << std::endl; @@ -185,13 +210,14 @@ int main(int argc, char** argv) {"output-dir", required_argument, 0, 'O' }, {"output-file", required_argument, 0, 'o' }, {"acsm-file", required_argument, 0, 'f' }, + {"export-private-key",no_argument, 0, 'e' }, {"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:vVh", + c = getopt_long(argc, argv, "d:a:k:O:o:f:evVh", long_options, &option_index); if (c == -1) break; @@ -215,6 +241,9 @@ int main(int argc, char** argv) case 'o': outputFile = optarg; break; + case 'e': + exportPrivateKey = true; + break; case 'v': verbose++; break; @@ -232,7 +261,7 @@ int main(int argc, char** argv) gourou::DRMProcessor::setLogLevel(verbose); - if (!acsmFile || (outputDir && !outputDir[0]) || + if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) || (outputFile && !outputFile[0])) { usage(argv[0]); @@ -253,13 +282,24 @@ int main(int argc, char** argv) goto end; } } - - QFile file(acsmFile); - if (!file.exists()) + + if (exportPrivateKey) { - std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl; - ret = -1; - goto end; + if (acsmFile) + { + usage(argv[0]); + return -1; + } + } + else + { + QFile file(acsmFile); + if (!file.exists()) + { + std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl; + ret = -1; + goto end; + } } QThreadPool::globalInstance()->start(&downloader);