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 443