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 */
encryptgpk(int argc,char * argv[])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 */
~encryptgpk()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 */
processOptions()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 */
printUsage(Options & options)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 */
run()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 */
generateOutput()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 */
readGPK(std::string & path)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 */
readKeyFile()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 */
writeCArray(std::ofstream & stream,const Blob & data)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 */
setVerboseLogging()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 */
main(int argc,char * argv[],char * envp[])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
443