forked from soutade/libgourou
		
	Initial commit
This commit is contained in:
		
							
								
								
									
										26
									
								
								utils/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								utils/LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
Copyright (c) 2021, Grégory Soutadé
 | 
			
		||||
 | 
			
		||||
All rights reserved.
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
* Redistributions of source code must retain the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer.
 | 
			
		||||
* Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
  notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
  documentation and/or other materials provided with the distribution.
 | 
			
		||||
* Neither the name of the copyright holder nor the
 | 
			
		||||
  names of its contributors may be used to endorse or promote products
 | 
			
		||||
  derived from this software without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 | 
			
		||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								utils/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								utils/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
 | 
			
		||||
TARGETS=acsmdownloader activate
 | 
			
		||||
 | 
			
		||||
CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
 | 
			
		||||
LDFLAGS=`pkg-config --libs Qt5Core Qt5Network` -L$(ROOT) -lgourou -lcrypto -lzip
 | 
			
		||||
 | 
			
		||||
ifneq ($(DEBUG),)
 | 
			
		||||
CXXFLAGS += -ggdb -O0
 | 
			
		||||
else
 | 
			
		||||
CXXFLAGS += -O2
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
all: $(TARGETS)
 | 
			
		||||
 | 
			
		||||
acsmdownloader: drmprocessorclientimpl.cpp acsmdownloader.cpp 
 | 
			
		||||
	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
			
		||||
 | 
			
		||||
activate: drmprocessorclientimpl.cpp activate.cpp 
 | 
			
		||||
	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f $(TARGETS)
 | 
			
		||||
 | 
			
		||||
ultraclean: clean
 | 
			
		||||
							
								
								
									
										255
									
								
								utils/acsmdownloader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								utils/acsmdownloader.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright (c) 2021, Grégory Soutadé
 | 
			
		||||
 | 
			
		||||
  All rights reserved.
 | 
			
		||||
  Redistribution and use in source and binary forms, with or without
 | 
			
		||||
  modification, are permitted provided that the following conditions are met:
 | 
			
		||||
  
 | 
			
		||||
  * Redistributions of source code must retain the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
  * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
    documentation and/or other materials provided with the distribution.
 | 
			
		||||
  * Neither the name of the copyright holder nor the
 | 
			
		||||
    names of its contributors may be used to endorse or promote products
 | 
			
		||||
    derived from this software without specific prior written permission.
 | 
			
		||||
  
 | 
			
		||||
  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 | 
			
		||||
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <QCoreApplication>
 | 
			
		||||
#include <QRunnable>
 | 
			
		||||
#include <QThreadPool>
 | 
			
		||||
 | 
			
		||||
#include <libgourou.h>
 | 
			
		||||
#include "drmprocessorclientimpl.h"
 | 
			
		||||
 | 
			
		||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
 | 
			
		||||
 | 
			
		||||
static const char* deviceFile     = "device.xml";
 | 
			
		||||
static const char* activationFile = "activation.xml";
 | 
			
		||||
static const char* devicekeyFile  = "devicesalt";
 | 
			
		||||
static const char* acsmFile       = 0;
 | 
			
		||||
static const char* outputFile     = 0;
 | 
			
		||||
static const char* outputDir      = 0;
 | 
			
		||||
static const char* defaultDirs[]  = {
 | 
			
		||||
    ".adept/",
 | 
			
		||||
    "./adobe-digital-editions/",
 | 
			
		||||
    "./.adobe-digital-editions/"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ACSMDownloader: public QRunnable
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ACSMDownloader(QCoreApplication* app):
 | 
			
		||||
	app(app)
 | 
			
		||||
    {
 | 
			
		||||
	setAutoDelete(false);
 | 
			
		||||
    }
 | 
			
		||||
   
 | 
			
		||||
    void run()
 | 
			
		||||
    {
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
	    DRMProcessorClientImpl client;
 | 
			
		||||
	    gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
 | 
			
		||||
 | 
			
		||||
	    gourou::FulfillmentItem* item = processor.fulfill(acsmFile);
 | 
			
		||||
 | 
			
		||||
	    std::string filename;
 | 
			
		||||
	    if (!outputFile)
 | 
			
		||||
	    {
 | 
			
		||||
		filename = item->getMetadata("title");
 | 
			
		||||
		if (filename == "")
 | 
			
		||||
		    filename = "output.epub";
 | 
			
		||||
		else
 | 
			
		||||
		    filename += ".epub";
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	    if (outputDir)
 | 
			
		||||
	    {
 | 
			
		||||
		QDir dir(outputDir);
 | 
			
		||||
		if (!dir.exists(outputDir))
 | 
			
		||||
		    dir.mkpath(outputDir);
 | 
			
		||||
 | 
			
		||||
		filename = std::string(outputDir) + "/" + filename;
 | 
			
		||||
	    }
 | 
			
		||||
	    
 | 
			
		||||
	    processor.download(item, filename);
 | 
			
		||||
	    std::cout << "Created " << filename << std::endl;
 | 
			
		||||
	} catch(std::exception& e)
 | 
			
		||||
	{
 | 
			
		||||
	    std::cout << e.what() << std::endl;
 | 
			
		||||
	    this->app->exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->app->exit(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QCoreApplication* app;
 | 
			
		||||
};	      
 | 
			
		||||
 | 
			
		||||
static const char* findFile(const char* filename, bool inDefaultDirs=true)
 | 
			
		||||
{
 | 
			
		||||
    QFile file(filename);
 | 
			
		||||
 | 
			
		||||
    if (file.exists())
 | 
			
		||||
	return strdup(filename);
 | 
			
		||||
 | 
			
		||||
    if (!inDefaultDirs) return 0;
 | 
			
		||||
    
 | 
			
		||||
    for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
 | 
			
		||||
    {
 | 
			
		||||
	QString path = QString(defaultDirs[i]) + QString(filename);
 | 
			
		||||
	file.setFileName(path);
 | 
			
		||||
	if (file.exists())
 | 
			
		||||
	    return strdup(path.toStdString().c_str());
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 << "  " << "-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 <title.epub>)" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-f|--acsm-file"       << "\t"   << "ACSM request file for epub download" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-v|--verbose"         << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-h|--help"            << "\t\t" << "This help" << std::endl;
 | 
			
		||||
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
    std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
 | 
			
		||||
    std::cout << "  * Current directory" << std::endl;
 | 
			
		||||
    std::cout << "  * .adept" << std::endl;
 | 
			
		||||
    std::cout << "  * adobe-digital-editions directory" << std::endl;
 | 
			
		||||
    std::cout << "  * .adobe-digital-editions directory" << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    int c, ret = -1;
 | 
			
		||||
 | 
			
		||||
    const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
 | 
			
		||||
    int verbose = gourou::DRMProcessor::getLogLevel();
 | 
			
		||||
 | 
			
		||||
    while (1) {
 | 
			
		||||
	int option_index = 0;
 | 
			
		||||
	static struct option long_options[] = {
 | 
			
		||||
	    {"device-file",      required_argument, 0,  'd' },
 | 
			
		||||
	    {"activation-file",  required_argument, 0,  'a' },
 | 
			
		||||
	    {"device-key-file",  required_argument, 0,  'k' },
 | 
			
		||||
	    {"output-dir",       required_argument, 0,  'O' },
 | 
			
		||||
	    {"output-file",      required_argument, 0,  'o' },
 | 
			
		||||
	    {"acsm-file",        required_argument, 0,  'f' },
 | 
			
		||||
	    {"verbose",          no_argument,       0,  'v' },
 | 
			
		||||
	    {"help",             no_argument,       0,  'h' },
 | 
			
		||||
	    {0,                  0,                 0,  0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	c = getopt_long(argc, argv, "d:a:k:O:o:f:vh",
 | 
			
		||||
                        long_options, &option_index);
 | 
			
		||||
	if (c == -1)
 | 
			
		||||
	    break;
 | 
			
		||||
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case 'd':
 | 
			
		||||
	    deviceFile = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'a':
 | 
			
		||||
	    activationFile = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'k':
 | 
			
		||||
	    devicekeyFile = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'f':
 | 
			
		||||
	    acsmFile = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'O':
 | 
			
		||||
	    outputDir = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'o':
 | 
			
		||||
	    outputFile = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'v':
 | 
			
		||||
	    verbose++;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'h':
 | 
			
		||||
	    usage(argv[0]);
 | 
			
		||||
	    return 0;
 | 
			
		||||
	    break;
 | 
			
		||||
	default:
 | 
			
		||||
	    usage(argv[0]);
 | 
			
		||||
	    return -1;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
   
 | 
			
		||||
    gourou::DRMProcessor::setLogLevel(verbose);
 | 
			
		||||
 | 
			
		||||
    if (!acsmFile || (outputDir && !outputDir[0]) ||
 | 
			
		||||
	(outputFile && !outputFile[0]))
 | 
			
		||||
    {
 | 
			
		||||
	usage(argv[0]);
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QCoreApplication app(argc, argv);
 | 
			
		||||
    ACSMDownloader downloader(&app);
 | 
			
		||||
 | 
			
		||||
    int i;
 | 
			
		||||
    for (i=0; i<(int)ARRAY_SIZE(files); i++)
 | 
			
		||||
    {
 | 
			
		||||
	*files[i] = findFile(*files[i]);
 | 
			
		||||
	if (!*files[i])
 | 
			
		||||
	{
 | 
			
		||||
	    std::cout << "Error : " << *files[i] << " doesn't exists" << std::endl;
 | 
			
		||||
	    ret = -1;
 | 
			
		||||
	    goto end;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    QFile file(acsmFile);
 | 
			
		||||
    if (!file.exists())
 | 
			
		||||
    {
 | 
			
		||||
	std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl;
 | 
			
		||||
	ret = -1;
 | 
			
		||||
	goto end;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    QThreadPool::globalInstance()->start(&downloader);
 | 
			
		||||
 | 
			
		||||
    ret = app.exec();
 | 
			
		||||
 | 
			
		||||
end:
 | 
			
		||||
    for (i=0; i<(int)ARRAY_SIZE(files); i++)
 | 
			
		||||
    {
 | 
			
		||||
	if (*files[i])
 | 
			
		||||
	    free((void*)*files[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										263
									
								
								utils/activate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								utils/activate.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,263 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright (c) 2021, Grégory Soutadé
 | 
			
		||||
 | 
			
		||||
  All rights reserved.
 | 
			
		||||
  Redistribution and use in source and binary forms, with or without
 | 
			
		||||
  modification, are permitted provided that the following conditions are met:
 | 
			
		||||
  
 | 
			
		||||
  * Redistributions of source code must retain the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
  * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
    documentation and/or other materials provided with the distribution.
 | 
			
		||||
  * Neither the name of the copyright holder nor the
 | 
			
		||||
    names of its contributors may be used to endorse or promote products
 | 
			
		||||
    derived from this software without specific prior written permission.
 | 
			
		||||
  
 | 
			
		||||
  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 | 
			
		||||
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ostream>
 | 
			
		||||
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <QCoreApplication>
 | 
			
		||||
#include <QRunnable>
 | 
			
		||||
#include <QThreadPool>
 | 
			
		||||
 | 
			
		||||
#include <libgourou.h>
 | 
			
		||||
#include "drmprocessorclientimpl.h"
 | 
			
		||||
 | 
			
		||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
 | 
			
		||||
 | 
			
		||||
static const char* username      = 0;
 | 
			
		||||
static const char* password      = 0;
 | 
			
		||||
static const char* outputDir     = 0;
 | 
			
		||||
static const char* hobbesVersion = HOBBES_DEFAULT_VERSION;
 | 
			
		||||
static bool        randomSerial  = false;
 | 
			
		||||
 | 
			
		||||
// From http://www.cplusplus.com/articles/E6vU7k9E/
 | 
			
		||||
static int getch() {
 | 
			
		||||
    int ch;
 | 
			
		||||
    struct termios t_old, t_new;
 | 
			
		||||
 | 
			
		||||
    tcgetattr(STDIN_FILENO, &t_old);
 | 
			
		||||
    t_new = t_old;
 | 
			
		||||
    t_new.c_lflag &= ~(ICANON | ECHO);
 | 
			
		||||
    tcsetattr(STDIN_FILENO, TCSANOW, &t_new);
 | 
			
		||||
 | 
			
		||||
    ch = getchar();
 | 
			
		||||
 | 
			
		||||
    tcsetattr(STDIN_FILENO, TCSANOW, &t_old);
 | 
			
		||||
    return ch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::string getpass(const char *prompt, bool show_asterisk=false)
 | 
			
		||||
{
 | 
			
		||||
  const char BACKSPACE=127;
 | 
			
		||||
  const char RETURN=10;
 | 
			
		||||
 | 
			
		||||
  std::string password;
 | 
			
		||||
  unsigned char ch=0;
 | 
			
		||||
 | 
			
		||||
  std::cout <<prompt;
 | 
			
		||||
 | 
			
		||||
  while((ch=getch())!= RETURN)
 | 
			
		||||
    {
 | 
			
		||||
	if(ch==BACKSPACE)
 | 
			
		||||
         {
 | 
			
		||||
            if(password.length()!=0)
 | 
			
		||||
              {
 | 
			
		||||
                 if(show_asterisk)
 | 
			
		||||
                 std::cout <<"\b \b";
 | 
			
		||||
                 password.resize(password.length()-1);
 | 
			
		||||
              }
 | 
			
		||||
         }
 | 
			
		||||
       else
 | 
			
		||||
         {
 | 
			
		||||
             password+=ch;
 | 
			
		||||
             if(show_asterisk)
 | 
			
		||||
                 std::cout <<'*';
 | 
			
		||||
         }
 | 
			
		||||
    }
 | 
			
		||||
  std::cout <<std::endl;
 | 
			
		||||
  return password;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Activate: public QRunnable
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Activate(QCoreApplication* app):
 | 
			
		||||
	app(app)
 | 
			
		||||
    {
 | 
			
		||||
	setAutoDelete(false);
 | 
			
		||||
    }
 | 
			
		||||
   
 | 
			
		||||
    void run()
 | 
			
		||||
    {
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
	    DRMProcessorClientImpl client;
 | 
			
		||||
	    gourou::DRMProcessor* processor = gourou::DRMProcessor::createDRMProcessor(
 | 
			
		||||
		&client, randomSerial, outputDir, hobbesVersion);
 | 
			
		||||
 | 
			
		||||
	    processor->signIn(username, password);
 | 
			
		||||
	    processor->activateDevice();
 | 
			
		||||
 | 
			
		||||
	    std::cout << username << " fully signed and device activated in " << outputDir << std::endl;
 | 
			
		||||
	} catch(std::exception& e)
 | 
			
		||||
	{
 | 
			
		||||
	    std::cout << e.what() << std::endl;
 | 
			
		||||
	    this->app->exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->app->exit(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QCoreApplication* app;
 | 
			
		||||
};	      
 | 
			
		||||
 | 
			
		||||
static void usage(const char* cmd)
 | 
			
		||||
{
 | 
			
		||||
    std::cout << "Create new device files used by ADEPT DRM" << std::endl;
 | 
			
		||||
    
 | 
			
		||||
    std::cout << "Usage: " << cmd << " (-u|--username) username [(-p|--password) password] [(-O|--output-dir) dir] [(-r|--random-serial)] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
 | 
			
		||||
    
 | 
			
		||||
    std::cout << "  " << "-u|--username"   << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-p|--password"   << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl;
 | 
			
		||||
    std::cout << "  " << "-O|--output-dir" << "\t"   << "Optional output directory were to put result (default ./.adept). This directory must not already exists" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-H|--hobbes-version" << "\t"<< "Force RMSDK version to a specific value (default: version of current librmsdk)" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-r|--random-serial" << "\t"<< "Generate a random device serial (if not set, it will be dependent of your current configuration)" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-v|--verbose"    << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
 | 
			
		||||
    std::cout << "  " << "-h|--help"       << "\t\t" << "This help" << std::endl;
 | 
			
		||||
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char* abspath(const char* filename)
 | 
			
		||||
{
 | 
			
		||||
    const char* root = getcwd(0, PATH_MAX);
 | 
			
		||||
    QString fullPath = QString(root) + QString("/") + QString(filename);
 | 
			
		||||
    const char* res = strdup(fullPath.toStdString().c_str());
 | 
			
		||||
 | 
			
		||||
    free((void*)root);
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    int c, ret = -1;
 | 
			
		||||
    const char* _outputDir = outputDir;
 | 
			
		||||
    int verbose = gourou::DRMProcessor::getLogLevel();
 | 
			
		||||
    
 | 
			
		||||
    while (1) {
 | 
			
		||||
	int option_index = 0;
 | 
			
		||||
	static struct option long_options[] = {
 | 
			
		||||
	    {"username",      required_argument, 0,  'u' },
 | 
			
		||||
	    {"password",      required_argument, 0,  'p' },
 | 
			
		||||
	    {"output-dir",    required_argument, 0,  'O' },
 | 
			
		||||
	    {"hobbes-version",required_argument, 0,  'H' },
 | 
			
		||||
	    {"random-serial", no_argument,       0,  'r' },
 | 
			
		||||
	    {"verbose",       no_argument,       0,  'v' },
 | 
			
		||||
	    {"help",          no_argument,       0,  'h' },
 | 
			
		||||
	    {0,               0,                 0,  0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	c = getopt_long(argc, argv, "u:p:O:H:rvh",
 | 
			
		||||
                        long_options, &option_index);
 | 
			
		||||
	if (c == -1)
 | 
			
		||||
	    break;
 | 
			
		||||
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case 'u':
 | 
			
		||||
	    username = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'p':
 | 
			
		||||
	    password = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'O':
 | 
			
		||||
	    _outputDir = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'H':
 | 
			
		||||
	    hobbesVersion = optarg;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'v':
 | 
			
		||||
	    verbose++;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'h':
 | 
			
		||||
	    usage(argv[0]);
 | 
			
		||||
	    return 0;
 | 
			
		||||
	    break;
 | 
			
		||||
	case 'r':
 | 
			
		||||
	    randomSerial = true;
 | 
			
		||||
	    break;
 | 
			
		||||
	default:
 | 
			
		||||
	    usage(argv[0]);
 | 
			
		||||
	    return -1;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
   
 | 
			
		||||
    gourou::DRMProcessor::setLogLevel(verbose);
 | 
			
		||||
 | 
			
		||||
    if (!username)
 | 
			
		||||
    {
 | 
			
		||||
	usage(argv[0]);
 | 
			
		||||
	return -1;
 | 
			
		||||
    }
 | 
			
		||||
   
 | 
			
		||||
    if (!_outputDir || _outputDir[0] == 0)
 | 
			
		||||
    {
 | 
			
		||||
	outputDir = abspath(DEFAULT_ADEPT_DIR);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
	// Relative path
 | 
			
		||||
	if (_outputDir[0] == '.' || _outputDir[0] != '/')
 | 
			
		||||
	{
 | 
			
		||||
	    QFile file(_outputDir);
 | 
			
		||||
	    // realpath doesn't works if file/dir doesn't exists
 | 
			
		||||
	    if (file.exists())
 | 
			
		||||
		outputDir = realpath(_outputDir, 0);
 | 
			
		||||
	    else
 | 
			
		||||
		outputDir = abspath(_outputDir);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	    outputDir = strdup(_outputDir);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!password)
 | 
			
		||||
    {
 | 
			
		||||
	char prompt[128];
 | 
			
		||||
	std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username);
 | 
			
		||||
	std::string pass = getpass((const char*)prompt, false);
 | 
			
		||||
	password = pass.c_str();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    QCoreApplication app(argc, argv);
 | 
			
		||||
    
 | 
			
		||||
    Activate activate(&app);
 | 
			
		||||
    QThreadPool::globalInstance()->start(&activate);
 | 
			
		||||
 | 
			
		||||
    ret = app.exec();
 | 
			
		||||
 | 
			
		||||
    free((void*)outputDir);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										385
									
								
								utils/drmprocessorclientimpl.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								utils/drmprocessorclientimpl.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,385 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright (c) 2021, Grégory Soutadé
 | 
			
		||||
 | 
			
		||||
  All rights reserved.
 | 
			
		||||
  Redistribution and use in source and binary forms, with or without
 | 
			
		||||
  modification, are permitted provided that the following conditions are met:
 | 
			
		||||
  
 | 
			
		||||
  * Redistributions of source code must retain the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
  * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
    documentation and/or other materials provided with the distribution.
 | 
			
		||||
  * Neither the name of the copyright holder nor the
 | 
			
		||||
    names of its contributors may be used to endorse or promote products
 | 
			
		||||
    derived from this software without specific prior written permission.
 | 
			
		||||
  
 | 
			
		||||
  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 | 
			
		||||
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <openssl/rand.h>
 | 
			
		||||
#include <openssl/pkcs12.h>
 | 
			
		||||
#include <openssl/evp.h>
 | 
			
		||||
#include <openssl/err.h>
 | 
			
		||||
 | 
			
		||||
#include <QCoreApplication>
 | 
			
		||||
#include <QNetworkReply>
 | 
			
		||||
#include <QNetworkRequest>
 | 
			
		||||
#include <QNetworkAccessManager>
 | 
			
		||||
#include <QFile>
 | 
			
		||||
 | 
			
		||||
#include <zip.h>
 | 
			
		||||
 | 
			
		||||
#include <libgourou_common.h>
 | 
			
		||||
#include <libgourou_log.h>
 | 
			
		||||
#include "drmprocessorclientimpl.h"
 | 
			
		||||
 | 
			
		||||
/* Digest interface */
 | 
			
		||||
void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
 | 
			
		||||
{
 | 
			
		||||
    EVP_MD_CTX *sha_ctx = EVP_MD_CTX_new();
 | 
			
		||||
    const EVP_MD* md = EVP_get_digestbyname(digestName.c_str());
 | 
			
		||||
    EVP_DigestInit(sha_ctx, md);
 | 
			
		||||
 | 
			
		||||
    return sha_ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length)
 | 
			
		||||
{
 | 
			
		||||
    return EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut)
 | 
			
		||||
{
 | 
			
		||||
    int res = EVP_DigestFinal((EVP_MD_CTX *)handler, digestOut, NULL);
 | 
			
		||||
    EVP_MD_CTX_free((EVP_MD_CTX *)handler);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut)
 | 
			
		||||
{
 | 
			
		||||
    void* handler = createDigest(digestName);
 | 
			
		||||
    if (!handler)
 | 
			
		||||
	return -1;
 | 
			
		||||
    if (digestUpdate(handler, data, length))
 | 
			
		||||
	return -1;
 | 
			
		||||
    return digestFinalize(handler, digestOut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Random interface */
 | 
			
		||||
void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int length)
 | 
			
		||||
{
 | 
			
		||||
    RAND_bytes(bytesOut, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* HTTP interface */
 | 
			
		||||
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType)
 | 
			
		||||
{
 | 
			
		||||
    QNetworkRequest request(QUrl(URL.c_str()));
 | 
			
		||||
    QNetworkAccessManager networkManager;
 | 
			
		||||
    QByteArray replyData;
 | 
			
		||||
 | 
			
		||||
    GOUROU_LOG(gourou::INFO, "Send request to " << URL);
 | 
			
		||||
    if (POSTData.size())
 | 
			
		||||
    {
 | 
			
		||||
	GOUROU_LOG(gourou::DEBUG, "<<< " << std::endl << POSTData);
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    request.setRawHeader("Accept", "*/*");
 | 
			
		||||
    request.setRawHeader("User-Agent", "book2png");
 | 
			
		||||
    if (contentType.size())
 | 
			
		||||
	request.setRawHeader("Content-Type", contentType.c_str());
 | 
			
		||||
 | 
			
		||||
    QNetworkReply* reply;
 | 
			
		||||
 | 
			
		||||
    if (POSTData.size())
 | 
			
		||||
	reply = networkManager.post(request, POSTData.c_str());
 | 
			
		||||
    else
 | 
			
		||||
	reply = networkManager.get(request);
 | 
			
		||||
 | 
			
		||||
    QCoreApplication* app = QCoreApplication::instance();
 | 
			
		||||
    networkManager.moveToThread(app->thread());
 | 
			
		||||
    while (!reply->isFinished())
 | 
			
		||||
	app->processEvents();
 | 
			
		||||
 | 
			
		||||
    replyData = reply->readAll();
 | 
			
		||||
    if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
 | 
			
		||||
    {
 | 
			
		||||
	GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data());
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    return std::string(replyData.data(), replyData.length());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
 | 
			
		||||
					       const RSA_KEY_TYPE keyType, const std::string& password,
 | 
			
		||||
					       const unsigned char* data, unsigned dataLength,
 | 
			
		||||
					       unsigned char* res)
 | 
			
		||||
{
 | 
			
		||||
    PKCS12 * pkcs12;
 | 
			
		||||
    EVP_PKEY* pkey;
 | 
			
		||||
    X509* cert;
 | 
			
		||||
    STACK_OF(X509)* ca;
 | 
			
		||||
    RSA * rsa;
 | 
			
		||||
 | 
			
		||||
    pkcs12 = d2i_PKCS12(NULL, &RSAKey, RSAKeyLength);
 | 
			
		||||
    if (!pkcs12)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_INVALID_PKCS12, ERR_error_string(ERR_get_error(), NULL));
 | 
			
		||||
    PKCS12_parse(pkcs12, password.c_str(), &pkey, &cert, &ca);
 | 
			
		||||
    rsa = EVP_PKEY_get1_RSA(pkey);
 | 
			
		||||
 | 
			
		||||
    int ret = RSA_private_encrypt(dataLength, data, res, rsa, RSA_PKCS1_PADDING);
 | 
			
		||||
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
 | 
			
		||||
 | 
			
		||||
    if (gourou::logLevel >= gourou::DEBUG)
 | 
			
		||||
    {
 | 
			
		||||
	printf("Sig : ");
 | 
			
		||||
	for(int i=0; i<(int)sizeof(res); i++)
 | 
			
		||||
	    printf("%02x ", res[i]);
 | 
			
		||||
	printf("\n");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
			    
 | 
			
		||||
void DRMProcessorClientImpl::RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
 | 
			
		||||
					      const RSA_KEY_TYPE keyType,
 | 
			
		||||
					      const unsigned char* data, unsigned dataLength,
 | 
			
		||||
					      unsigned char* res)
 | 
			
		||||
{
 | 
			
		||||
    X509 * x509 = d2i_X509(0, &RSAKey, RSAKeyLength);
 | 
			
		||||
    if (!x509)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_INVALID_CERTIFICATE, "Invalid certificate");
 | 
			
		||||
	
 | 
			
		||||
    EVP_PKEY * evpKey = X509_get_pubkey(x509);
 | 
			
		||||
    RSA* rsa = EVP_PKEY_get1_RSA(evpKey);
 | 
			
		||||
    EVP_PKEY_free(evpKey);
 | 
			
		||||
 | 
			
		||||
    if (!rsa)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_NO_PRIV_KEY, "No private key in certificate");
 | 
			
		||||
 | 
			
		||||
    int ret = RSA_public_encrypt(dataLength, data, res, rsa, RSA_PKCS1_PADDING);
 | 
			
		||||
    if (ret < 0)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_RSA_ERROR, ERR_error_string(ERR_get_error(), NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* DRMProcessorClientImpl::generateRSAKey(int keyLengthBits)
 | 
			
		||||
{
 | 
			
		||||
    BIGNUM * bn = BN_new();
 | 
			
		||||
    RSA * rsa = RSA_new();
 | 
			
		||||
    BN_set_word(bn, 0x10001);
 | 
			
		||||
    RSA_generate_key_ex(rsa, keyLengthBits, bn, 0);
 | 
			
		||||
    BN_free(bn);
 | 
			
		||||
 | 
			
		||||
    return rsa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::destroyRSAHandler(void* handler)
 | 
			
		||||
{
 | 
			
		||||
    RSA_free((RSA*)handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::extractRSAPublicKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)
 | 
			
		||||
{
 | 
			
		||||
    EVP_PKEY * evpKey = EVP_PKEY_new();
 | 
			
		||||
    EVP_PKEY_set1_RSA(evpKey, (RSA*)handler);
 | 
			
		||||
    X509_PUBKEY *x509_pubkey = 0;
 | 
			
		||||
    X509_PUBKEY_set(&x509_pubkey, evpKey);
 | 
			
		||||
 | 
			
		||||
    *keyOutLength = i2d_X509_PUBKEY(x509_pubkey, keyOut);
 | 
			
		||||
 | 
			
		||||
    X509_PUBKEY_free(x509_pubkey);
 | 
			
		||||
    EVP_PKEY_free(evpKey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength)
 | 
			
		||||
{
 | 
			
		||||
    EVP_PKEY * evpKey = EVP_PKEY_new();
 | 
			
		||||
    EVP_PKEY_set1_RSA(evpKey, (RSA*)handler);
 | 
			
		||||
    PKCS8_PRIV_KEY_INFO * privKey = EVP_PKEY2PKCS8(evpKey);
 | 
			
		||||
 | 
			
		||||
    *keyOutLength = i2d_PKCS8_PRIV_KEY_INFO(privKey, keyOut);
 | 
			
		||||
 | 
			
		||||
    PKCS8_PRIV_KEY_INFO_free(privKey);
 | 
			
		||||
    EVP_PKEY_free(evpKey);
 | 
			
		||||
}
 | 
			
		||||
				 
 | 
			
		||||
/* Crypto interface */
 | 
			
		||||
void DRMProcessorClientImpl::AESEncrypt(CHAINING_MODE chaining,
 | 
			
		||||
					const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
					const unsigned char* iv, unsigned int ivLength,
 | 
			
		||||
					const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
					unsigned char* dataOut, unsigned int* dataOutLength)
 | 
			
		||||
{
 | 
			
		||||
    void* handler = AESEncryptInit(chaining, key, keyLength, iv, ivLength);
 | 
			
		||||
    AESEncryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
 | 
			
		||||
    AESEncryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* DRMProcessorClientImpl::AESEncryptInit(CHAINING_MODE chaining,
 | 
			
		||||
					     const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
					     const unsigned char* iv, unsigned int ivLength)
 | 
			
		||||
{
 | 
			
		||||
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
 | 
			
		||||
 | 
			
		||||
    switch(keyLength)
 | 
			
		||||
    {
 | 
			
		||||
    case 16:
 | 
			
		||||
	switch(chaining)
 | 
			
		||||
	{
 | 
			
		||||
	case CHAIN_ECB:
 | 
			
		||||
	    EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
 | 
			
		||||
	    break;
 | 
			
		||||
	case CHAIN_CBC:
 | 
			
		||||
	    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
 | 
			
		||||
	    break;
 | 
			
		||||
	default:
 | 
			
		||||
	    EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
 | 
			
		||||
	break;
 | 
			
		||||
	}
 | 
			
		||||
    default:
 | 
			
		||||
	EVP_CIPHER_CTX_free(ctx);
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* DRMProcessorClientImpl::AESDecryptInit(CHAINING_MODE chaining,
 | 
			
		||||
					     const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
					     const unsigned char* iv, unsigned int ivLength)
 | 
			
		||||
{
 | 
			
		||||
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
 | 
			
		||||
 | 
			
		||||
    switch(keyLength)
 | 
			
		||||
    {
 | 
			
		||||
    case 16:
 | 
			
		||||
	switch(chaining)
 | 
			
		||||
	{
 | 
			
		||||
	case CHAIN_ECB:
 | 
			
		||||
	    EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
 | 
			
		||||
	    break;
 | 
			
		||||
	case CHAIN_CBC:
 | 
			
		||||
	    EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
 | 
			
		||||
	    break;
 | 
			
		||||
	default:
 | 
			
		||||
	    EXCEPTION(gourou::CLIENT_BAD_CHAINING, "Unknown chaining mode " << chaining);
 | 
			
		||||
	}
 | 
			
		||||
	break;
 | 
			
		||||
    default:
 | 
			
		||||
	EVP_CIPHER_CTX_free(ctx);
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_BAD_KEY_SIZE, "Invalid key size " << keyLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::AESEncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
		 unsigned char* dataOut, unsigned int* dataOutLength)
 | 
			
		||||
{
 | 
			
		||||
    EVP_EncryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::AESEncryptFinalize(void* handler,
 | 
			
		||||
						unsigned char* dataOut, unsigned int* dataOutLength)
 | 
			
		||||
{
 | 
			
		||||
    int len;
 | 
			
		||||
    EVP_EncryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
 | 
			
		||||
    *dataOutLength += len;
 | 
			
		||||
    EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::AESDecrypt(CHAINING_MODE chaining,
 | 
			
		||||
					const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
					const unsigned char* iv, unsigned int ivLength,
 | 
			
		||||
					const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
					unsigned char* dataOut, unsigned int* dataOutLength)
 | 
			
		||||
{
 | 
			
		||||
    void* handler = AESDecryptInit(chaining, key, keyLength, iv, ivLength);
 | 
			
		||||
    AESDecryptUpdate(handler, dataIn, dataInLength, dataOut, dataOutLength);
 | 
			
		||||
    AESDecryptFinalize(handler, dataOut+*dataOutLength, dataOutLength);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::AESDecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
					       unsigned char* dataOut, unsigned int* dataOutLength)
 | 
			
		||||
{
 | 
			
		||||
    EVP_DecryptUpdate((EVP_CIPHER_CTX*)handler, dataOut, (int*)dataOutLength, dataIn, dataInLength);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::AESDecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength)
 | 
			
		||||
{
 | 
			
		||||
    int len;
 | 
			
		||||
    EVP_DecryptFinal_ex((EVP_CIPHER_CTX*)handler, dataOut, &len);
 | 
			
		||||
    *dataOutLength += len;
 | 
			
		||||
    EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void* DRMProcessorClientImpl::zipOpen(const std::string& path)
 | 
			
		||||
{
 | 
			
		||||
    zip_t* handler = zip_open(path.c_str(), 0, 0);
 | 
			
		||||
 | 
			
		||||
    if (!handler)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_BAD_ZIP_FILE, "Invalid zip file " << path);
 | 
			
		||||
 | 
			
		||||
    return handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string DRMProcessorClientImpl::zipReadFile(void* handler, const std::string& path)
 | 
			
		||||
{
 | 
			
		||||
    std::string res;
 | 
			
		||||
    unsigned char* buffer;
 | 
			
		||||
    zip_stat_t sb;
 | 
			
		||||
    
 | 
			
		||||
    if (zip_stat((zip_t *)handler, path.c_str(), 0, &sb) < 0)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));
 | 
			
		||||
 | 
			
		||||
    if (!(sb.valid & (ZIP_STAT_INDEX|ZIP_STAT_SIZE)))
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Required fields missing");
 | 
			
		||||
    
 | 
			
		||||
    buffer = new unsigned char[sb.size];
 | 
			
		||||
    
 | 
			
		||||
    zip_file_t *f = zip_fopen_index((zip_t *)handler, sb.index, ZIP_FL_COMPRESSED);
 | 
			
		||||
 | 
			
		||||
    zip_fread(f, buffer, sb.size);
 | 
			
		||||
    zip_fclose(f);
 | 
			
		||||
 | 
			
		||||
    res = std::string((char*)buffer, sb.size);
 | 
			
		||||
    delete[] buffer;
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::zipWriteFile(void* handler, const std::string& path, const std::string& content)
 | 
			
		||||
{
 | 
			
		||||
    zip_source_t* s = zip_source_buffer((zip_t*)handler, content.c_str(), content.length(), 0);
 | 
			
		||||
    if (zip_file_add((zip_t*)handler, path.c_str(), s, ZIP_FL_OVERWRITE|ZIP_FL_ENC_UTF_8) < 0)
 | 
			
		||||
    {
 | 
			
		||||
	zip_source_free(s);
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::zipDeleteFile(void* handler, const std::string& path)
 | 
			
		||||
{
 | 
			
		||||
    zip_int64_t idx = zip_name_locate((zip_t*)handler, path.c_str(), 0);
 | 
			
		||||
 | 
			
		||||
    if (idx < 0)
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_ZIP_ERROR, "No such file " << path.c_str());
 | 
			
		||||
    
 | 
			
		||||
    if (zip_delete((zip_t*)handler, idx))
 | 
			
		||||
	EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Zip error " << zip_strerror((zip_t *)handler));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DRMProcessorClientImpl::zipClose(void* handler)
 | 
			
		||||
{
 | 
			
		||||
    zip_close((zip_t*)handler);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								utils/drmprocessorclientimpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								utils/drmprocessorclientimpl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright (c) 2021, Grégory Soutadé
 | 
			
		||||
 | 
			
		||||
  All rights reserved.
 | 
			
		||||
  Redistribution and use in source and binary forms, with or without
 | 
			
		||||
  modification, are permitted provided that the following conditions are met:
 | 
			
		||||
  
 | 
			
		||||
  * Redistributions of source code must retain the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
  * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
    documentation and/or other materials provided with the distribution.
 | 
			
		||||
  * Neither the name of the copyright holder nor the
 | 
			
		||||
    names of its contributors may be used to endorse or promote products
 | 
			
		||||
    derived from this software without specific prior written permission.
 | 
			
		||||
  
 | 
			
		||||
  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 | 
			
		||||
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
			
		||||
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
			
		||||
  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 | 
			
		||||
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
			
		||||
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
			
		||||
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
			
		||||
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef _DRMPROCESSORCLIENTIMPL_H_
 | 
			
		||||
#define _DRMPROCESSORCLIENTIMPL_H_
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <drmprocessorclient.h>
 | 
			
		||||
 | 
			
		||||
class DRMProcessorClientImpl : public gourou::DRMProcessorClient
 | 
			
		||||
{
 | 
			
		||||
    public:
 | 
			
		||||
    /* Digest interface */
 | 
			
		||||
    virtual void* createDigest(const std::string& digestName);
 | 
			
		||||
    virtual int digestUpdate(void* handler, unsigned char* data, unsigned int length);
 | 
			
		||||
    virtual int digestFinalize(void* handler,unsigned char* digestOut);
 | 
			
		||||
    virtual int digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut);
 | 
			
		||||
 | 
			
		||||
    /* Random interface */
 | 
			
		||||
    virtual void randBytes(unsigned char* bytesOut, unsigned int length);
 | 
			
		||||
 | 
			
		||||
    /* HTTP interface */
 | 
			
		||||
    virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""));
 | 
			
		||||
 | 
			
		||||
    virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
 | 
			
		||||
				   const RSA_KEY_TYPE keyType, const std::string& password,
 | 
			
		||||
				   const unsigned char* data, unsigned dataLength,
 | 
			
		||||
				   unsigned char* res);
 | 
			
		||||
			    
 | 
			
		||||
    virtual void RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
 | 
			
		||||
				  const RSA_KEY_TYPE keyType,
 | 
			
		||||
				  const unsigned char* data, unsigned dataLength,
 | 
			
		||||
				  unsigned char* res);
 | 
			
		||||
 | 
			
		||||
    virtual void* generateRSAKey(int keyLengthBits);
 | 
			
		||||
    virtual void destroyRSAHandler(void* handler);
 | 
			
		||||
    
 | 
			
		||||
    virtual void extractRSAPublicKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
 | 
			
		||||
    virtual void extractRSAPrivateKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength);
 | 
			
		||||
				 
 | 
			
		||||
    /* Crypto interface */
 | 
			
		||||
    virtual void AESEncrypt(CHAINING_MODE chaining,
 | 
			
		||||
			    const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
			    const unsigned char* iv, unsigned int ivLength,
 | 
			
		||||
			    const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
			    unsigned char* dataOut, unsigned int* dataOutLength);
 | 
			
		||||
 | 
			
		||||
    virtual void* AESEncryptInit(CHAINING_MODE chaining,
 | 
			
		||||
				 const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
				 const unsigned char* iv=0, unsigned int ivLength=0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    virtual void AESEncryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
				   unsigned char* dataOut, unsigned int* dataOutLength);
 | 
			
		||||
    virtual void AESEncryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
 | 
			
		||||
 | 
			
		||||
    virtual void AESDecrypt(CHAINING_MODE chaining,
 | 
			
		||||
			    const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
			    const unsigned char* iv, unsigned int ivLength,
 | 
			
		||||
			    const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
			    unsigned char* dataOut, unsigned int* dataOutLength);
 | 
			
		||||
 | 
			
		||||
    virtual void* AESDecryptInit(CHAINING_MODE chaining,
 | 
			
		||||
				 const unsigned char* key, unsigned int keyLength,
 | 
			
		||||
				 const unsigned char* iv=0, unsigned int ivLength=0);
 | 
			
		||||
 | 
			
		||||
    virtual void AESDecryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength,
 | 
			
		||||
				   unsigned char* dataOut, unsigned int* dataOutLength);
 | 
			
		||||
    virtual void AESDecryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength);
 | 
			
		||||
 | 
			
		||||
    /* ZIP Interface */
 | 
			
		||||
    virtual void* zipOpen(const std::string& path);
 | 
			
		||||
    
 | 
			
		||||
    virtual std::string zipReadFile(void* handler, const std::string& path);
 | 
			
		||||
    
 | 
			
		||||
    virtual void zipWriteFile(void* handler, const std::string& path, const std::string& content);
 | 
			
		||||
    
 | 
			
		||||
    virtual void zipDeleteFile(void* handler, const std::string& path);
 | 
			
		||||
    
 | 
			
		||||
    virtual void zipClose(void* handler);
 | 
			
		||||
    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Reference in New Issue
	
	Block a user