]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/elftosb/encryptgpk/encryptgpk.cpp
merged tx6dl-devel into denx master branch
[karo-tx-uboot.git] / tools / elftosb / encryptgpk / encryptgpk.cpp
1 /*
2  * File:        encryptgpk.cpp
3  *
4  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5  * See included license file for license details.
6  */
7
8 #include "stdafx.h"
9 #include <iostream>
10 #include <fstream>
11 #include <sstream>
12 #include <stdlib.h>
13 #include <stdexcept>
14 #include <stdio.h>
15 #include "options.h"
16 #include "EncoreBootImage.h"
17 #include "smart_ptr.h"
18 #include "Logging.h"
19 #include "format_string.h"
20 #include "Blob.h"
21 #include "Random.h"
22 #include "rijndael.h"
23
24 using namespace elftosb;
25
26 //! Size in bytes of the unencrypted group private key.
27 #define GPK_LENGTH (40)
28
29 //! Size in bytes of the encrypted output data. This size must be modulo 16, the chunk size for the
30 //! AES-128 crypto algorithm. The group private key is inserted at offset 16.
31 #define OUTPUT_DATA_LENGTH (64)
32
33 //! Position in the output data of the first byte of the group private key.
34 #define OUTPUT_DATA_GPK_OFFSET (16)
35
36 //! The tool's name.
37 const char k_toolName[] = "encryptgpk";
38
39 //! Current version number for the tool.
40 const char k_version[] = "1.0.2";
41
42 //! Copyright string.
43 const char k_copyright[] = "Copyright (c) 2008 Freescale Semiconductor. All rights reserved.";
44
45 //! Default output array name.
46 const char k_defaultArrayName[] = "_endDisplay";
47
48 //! Definition of command line options.
49 static const char * k_optionsDefinition[] = {
50         "?|help",
51         "v|version",
52         "k:key <file>",
53         "z|zero-key",
54         "o:output",
55         "p:prefix",
56         "a:array",
57         "d|debug",
58         "q|quiet",
59         "V|verbose",
60         NULL
61 };
62
63 //! Help string.
64 const char k_usageText[] = "\nOptions:\n\
65   -?/--help                    Show this help\n\
66   -v/--version                 Display tool version\n\
67   -k/--key <file>              Add OTP key used for decryption\n\
68   -z/--zero-key                Add default key of all zeroes\n\
69   -o/--output <file>           Write output to this file\n\
70   -p/--prefix <prefix>         Set the output array prefix\n\
71   -a/--array <name>            Specify the output array name\n\
72   -d/--debug                   Enable debug output\n\
73   -q/--quiet                   Output only warnings and errors\n\
74   -V/--verbose                 Print extra detailed log information\n\n";
75
76 //! Init vector used for CBC encrypting the output data.
77 static const uint8_t kInitVector[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
78
79 //! An array of strings.
80 typedef std::vector<std::string> string_vector_t;
81
82 // prototypes
83 int main(int argc, char* argv[], char* envp[]);
84
85 /*!
86  * \brief Class that encapsulates the sbtool interface.
87  *
88  * A single global logger instance is created during object construction. It is
89  * never freed because we need it up to the last possible minute, when an
90  * exception could be thrown.
91  */
92 class encryptgpk
93 {
94 protected:
95         int m_argc;                                                     //!< Number of command line arguments.
96         char ** m_argv;                                         //!< String value for each command line argument.
97         StdoutLogger * m_logger;                        //!< Singleton logger instance.
98         string_vector_t m_keyFilePaths;         //!< Paths to OTP key files.
99         string_vector_t m_positionalArgs;       //!< Arguments coming after explicit options.
100         bool m_isVerbose;                                       //!< Whether the verbose flag was turned on.
101         bool m_useDefaultKey;                                   //!< Include a default (zero) crypto key.
102         std::string m_outputPath;                       //!< Path to output file.
103         std::string m_gpkPath;                          //!< Path to input group private key file.
104         std::string m_outputPrefix;                     //!< Prefix to the output array.
105         std::string m_arrayName;                        //!< Output array's name.
106         
107 public:
108         /*!
109          * Constructor.
110          *
111          * Creates the singleton logger instance.
112          */
113         encryptgpk(int argc, char * argv[])
114         :       m_argc(argc),
115                 m_argv(argv),
116                 m_logger(0),
117                 m_keyFilePaths(),
118                 m_positionalArgs(),
119                 m_isVerbose(false),
120                 m_useDefaultKey(false),
121                 m_outputPath(),
122                 m_gpkPath(),
123                 m_outputPrefix(),
124                 m_arrayName(k_defaultArrayName)
125         {
126                 // create logger instance
127                 m_logger = new StdoutLogger();
128                 m_logger->setFilterLevel(Logger::INFO);
129                 Log::setLogger(m_logger);
130         }
131         
132         /*!
133          * Destructor.
134          */
135         ~encryptgpk()
136         {
137         }
138         
139         /*!
140          * Reads the command line options passed into the constructor.
141          *
142          * This method can return a return code to its caller, which will cause the
143          * tool to exit immediately with that return code value. Normally, though, it
144          * will return -1 to signal that the tool should continue to execute and
145          * all options were processed successfully.
146          *
147          * The Options class is used to parse command line options. See
148          * #k_optionsDefinition for the list of options and #k_usageText for the
149          * descriptive help for each option.
150          *
151          * \retval -1 The options were processed successfully. Let the tool run normally.
152          * \return A zero or positive result is a return code value that should be
153          *              returned from the tool as it exits immediately.
154          */
155         int processOptions()
156         {
157                 Options options(*m_argv, k_optionsDefinition);
158                 OptArgvIter iter(--m_argc, ++m_argv);
159                 
160                 // process command line options
161                 int optchar;
162                 const char * optarg;
163                 while (optchar = options(iter, optarg))
164                 {
165                         switch (optchar)
166                         {
167                                 case '?':
168                                         printUsage(options);
169                                         return 0;
170                                 
171                                 case 'v':
172                                         printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
173                                         return 0;
174                                         
175                                 case 'k':
176                                         m_keyFilePaths.push_back(optarg);
177                                         break;
178                                 
179                                 case 'z':
180                                         m_useDefaultKey = true;
181                                         break;
182                                 
183                                 case 'o':
184                                         m_outputPath = optarg;
185                                         break;
186
187                                 case 'p':
188                                         m_outputPrefix = optarg;
189                                         break;
190
191                                 case 'a':
192                                         m_arrayName = optarg;
193                                         break;
194                                 
195                                 case 'd':
196                                         Log::getLogger()->setFilterLevel(Logger::DEBUG);
197                                         break;
198                                         
199                                 case 'q':
200                                         Log::getLogger()->setFilterLevel(Logger::WARNING);
201                                         break;
202                                         
203                                 case 'V':
204                                         m_isVerbose = true;
205                                         break;
206                                 
207                                 default:
208                                         Log::log(Logger::ERROR, "error: unrecognized option\n\n");
209                                         printUsage(options);
210                                         return 1;
211                         }
212                 }
213                 
214                 // handle positional args
215                 if (iter.index() < m_argc)
216                 {
217 //                      Log::SetOutputLevel leveler(Logger::DEBUG);
218 //                      Log::log("positional args:\n");
219                         int i;
220                         for (i = iter.index(); i < m_argc; ++i)
221                         {
222 //                              Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
223                                 m_positionalArgs.push_back(m_argv[i]);
224                         }
225                 }
226                 
227                 // all is well
228                 return -1;
229         }
230
231         /*!
232          * Prints help for the tool.
233          */
234         void printUsage(Options & options)
235         {
236                 options.usage(std::cout, "gpk-file");
237                 printf(k_usageText, k_toolName);
238         }
239         
240         /*!
241          * Core of the tool. Calls processOptions() to handle command line options
242          * before performing the real work the tool does.
243          */
244         int run()
245         {
246                 try
247                 {
248                         // read command line options
249                         int result;
250                         if ((result = processOptions()) != -1)
251                         {
252                                 return result;
253                         }
254                         
255                         // set verbose logging
256                         setVerboseLogging();
257                         
258                         // make sure a file was provided
259                         if (m_positionalArgs.size() < 1)
260                         {
261                                 throw std::runtime_error("no input file path was provided");
262                         }
263
264                         // Make sure at least one key was specified.
265                         if (m_keyFilePaths.size() == 0 && m_useDefaultKey == false)
266                         {
267                                 throw std::runtime_error("no crypto key was specified");
268                         }
269                         
270                         // Do the work.
271                         generateOutput();
272                 }
273                 catch (std::exception & e)
274                 {
275                         Log::log(Logger::ERROR, "error: %s\n", e.what());
276                         return 1;
277                 }
278                 catch (...)
279                 {
280                         Log::log(Logger::ERROR, "error: unexpected exception\n");
281                         return 1;
282                 }
283                 
284                 return 0;
285         }
286
287         /*!
288          * \brief Builds the output data blob, encrypts it, and writes it to the output file.
289          */
290         void generateOutput()
291         {
292                 // Create the output data blob and set it to the correct size.
293                 Blob data;
294                 data.setLength(OUTPUT_DATA_LENGTH);
295                 
296                 // Fill it with random values.
297                 RandomNumberGenerator rng;
298                 rng.generateBlock(data.getData(), OUTPUT_DATA_LENGTH);
299
300                 // Read the GPK and overlay it into the output data.
301                 // The first positional arg is the GPK file path.
302                 Blob gpk = readGPK(m_positionalArgs[0]);
303                 memcpy(data.getData() + OUTPUT_DATA_GPK_OFFSET, gpk.getData(), GPK_LENGTH);
304
305                 // This is the key object for our crypto key.
306                 AESKey<128> cryptoKey = readKeyFile();
307
308                 // Read the key file.
309                 // Encrypt the output data block.
310                 Rijndael cipher;
311                 cipher.init(Rijndael::CBC, Rijndael::Encrypt, cryptoKey, Rijndael::Key16Bytes, (uint8_t *)&kInitVector);
312                 cipher.blockEncrypt(data.getData(), OUTPUT_DATA_LENGTH * 8, data.getData());
313
314                 // Open the output file.
315                 std::ofstream outputStream(m_outputPath.c_str(), std::ios_base::out | std::ios_base::trunc);
316                 if (!outputStream.is_open())
317                 {
318                         throw std::runtime_error(format_string("could not open output file %s", m_outputPath.c_str()));
319                 }
320                 
321                 writeCArray(outputStream, data);
322         }
323
324         /*!
325          * \brief Reads the group private key binary data.
326          */
327         Blob readGPK(std::string & path)
328         {
329                 std::ifstream stream(path.c_str(), std::ios_base::in | std::ios_base::binary);
330                 if (!stream.is_open())
331                 {
332                         throw std::runtime_error("could not open group private key file");
333                 }
334
335                 Blob gpk;
336                 gpk.setLength(GPK_LENGTH);
337
338                 stream.read((char *)gpk.getData(), GPK_LENGTH);
339
340                 return gpk;
341         }
342
343         /*!
344          * \brief Returns a key object based on the user's specified key.
345          */
346         AESKey<128> readKeyFile()
347         {
348                 if (m_keyFilePaths.size() > 0)
349                 {
350                         // Open the key file.
351                         std::string & keyPath = m_keyFilePaths[0];
352                         std::ifstream keyStream(keyPath.c_str(), std::ios_base::in);
353                         if (!keyStream.is_open())
354                         {
355                                 throw std::runtime_error(format_string("unable to read key file %s\n", keyPath.c_str()));
356                         }
357                         keyStream.seekg(0);
358                         
359                         // Read the first key in the file.
360                         AESKey<128> key(keyStream);
361                         return key;
362                 }
363
364                 // Otherwise, create a zero key and return it.
365                 AESKey<128> defaultKey;
366                 return defaultKey;
367
368         }
369
370         /*!
371          * \brief Writes the given data blob as an array in a C source file.
372          */
373         void writeCArray(std::ofstream & stream, const Blob & data)
374         {
375                 const uint8_t * dataPtr = data.getData();
376                 unsigned length = data.getLength();
377
378                 // Write first line.
379                 std::string text = format_string("%s%sunsigned char %s[%d] = {", m_outputPrefix.c_str(), m_outputPrefix.size() > 0 ? " " : "", m_arrayName.c_str(), length);
380                 stream.write(text.c_str(), text.size());
381                 
382                 // Write each word of the array.
383                 unsigned i = 0;
384                 while (i < length)
385                 {
386                         // Insert a comma at the end of the previous line unless this is the first word we're outputting.
387                         text = format_string("%s\n    0x%02x", i == 0 ? "" : ",", (*dataPtr++) & 0xff);
388                         stream.write(text.c_str(), text.size());
389
390                         i++;
391                 }
392
393                 // Write last line, terminating the array.
394                 text = "\n};\n\n";
395                 stream.write(text.c_str(), text.size());
396         }
397         
398         /*!
399          * \brief Turns on verbose logging.
400          */
401         void setVerboseLogging()
402         {
403                 if (m_isVerbose)
404                 {
405                         // verbose only affects the INFO and DEBUG filter levels
406                         // if the user has selected quiet mode, it overrides verbose
407                         switch (Log::getLogger()->getFilterLevel())
408                         {
409                                 case Logger::INFO:
410                                         Log::getLogger()->setFilterLevel(Logger::INFO2);
411                                         break;
412                                 case Logger::DEBUG:
413                                         Log::getLogger()->setFilterLevel(Logger::DEBUG2);
414                                         break;
415                         }
416                 }
417         }
418
419 };
420
421 /*!
422  * Main application entry point. Creates an sbtool instance and lets it take over.
423  */
424 int main(int argc, char* argv[], char* envp[])
425 {
426         try
427         {
428                 return encryptgpk(argc, argv).run();
429         }
430         catch (...)
431         {
432                 Log::log(Logger::ERROR, "error: unexpected exception\n");
433                 return 1;
434         }
435
436         return 0;
437 }
438
439
440
441
442