1/* 2 * File: keygen.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 <string> 15#include <vector> 16#include "options.h" 17#include "smart_ptr.h" 18#include "Logging.h" 19#include "AESKey.h" 20 21//! The tool's name. 22const char k_toolName[] = "keygen"; 23 24//! Current version number for the tool. 25const char k_version[] = "1.0"; 26 27//! Copyright string. 28const char k_copyright[] = "Copyright (c) 2006-2009 Freescale Semiconductor, Inc.\nAll rights reserved."; 29 30//! Definition of command line options. 31static const char * k_optionsDefinition[] = { 32 "?|help", 33 "v|version", 34 "q|quiet", 35 "V|verbose", 36 "n:number <int>", 37 NULL 38}; 39 40//! Help string. 41const char k_usageText[] = "\nOptions:\n\ 42 -?/--help Show this help\n\ 43 -v/--version Display tool version\n\ 44 -q/--quiet Output only warnings and errors\n\ 45 -V/--verbose Print extra detailed log information\n\ 46 -n/--number <int> Number of keys to generate per file (default=1)\n\n"; 47 48//! An array of strings. 49typedef std::vector<std::string> string_vector_t; 50 51// prototypes 52int main(int argc, char* argv[], char* envp[]); 53 54/*! 55 * \brief Class that encapsulates the keygen interface. 56 * 57 * A single global logger instance is created during object construction. It is 58 * never freed because we need it up to the last possible minute, when an 59 * exception could be thrown. 60 */ 61class keygen 62{ 63protected: 64 int m_argc; //!< Number of command line arguments. 65 char ** m_argv; //!< String value for each command line argument. 66 StdoutLogger * m_logger; //!< Singleton logger instance. 67 string_vector_t m_positionalArgs; //!< Arguments coming after explicit options. 68 bool m_isVerbose; //!< Whether the verbose flag was turned on. 69 int m_keyCount; //!< Number of keys to generate. 70 71public: 72 /*! 73 * Constructor. 74 * 75 * Creates the singleton logger instance. 76 */ 77 keygen(int argc, char * argv[]) 78 : m_argc(argc), 79 m_argv(argv), 80 m_logger(0), 81 m_positionalArgs(), 82 m_isVerbose(false), 83 m_keyCount(1) 84 { 85 // create logger instance 86 m_logger = new StdoutLogger(); 87 m_logger->setFilterLevel(Logger::INFO); 88 Log::setLogger(m_logger); 89 } 90 91 /*! 92 * Destructor. 93 */ 94 ~keygen() 95 { 96 } 97 98 /*! 99 * Reads the command line options passed into the constructor. 100 * 101 * This method can return a return code to its caller, which will cause the 102 * tool to exit immediately with that return code value. Normally, though, it 103 * will return -1 to signal that the tool should continue to execute and 104 * all options were processed successfully. 105 * 106 * The Options class is used to parse command line options. See 107 * #k_optionsDefinition for the list of options and #k_usageText for the 108 * descriptive help for each option. 109 * 110 * \retval -1 The options were processed successfully. Let the tool run normally. 111 * \return A zero or positive result is a return code value that should be 112 * returned from the tool as it exits immediately. 113 */ 114 int processOptions() 115 { 116 Options options(*m_argv, k_optionsDefinition); 117 OptArgvIter iter(--m_argc, ++m_argv); 118 119 // process command line options 120 int optchar; 121 const char * optarg; 122 while (optchar = options(iter, optarg)) 123 { 124 switch (optchar) 125 { 126 case '?': 127 printUsage(options); 128 return 0; 129 130 case 'v': 131 printf("%s %s\n%s\n", k_toolName, k_version, k_copyright); 132 return 0; 133 134 case 'd': 135 Log::getLogger()->setFilterLevel(Logger::DEBUG); 136 break; 137 138 case 'q': 139 Log::getLogger()->setFilterLevel(Logger::WARNING); 140 break; 141 142 case 'V': 143 m_isVerbose = true; 144 break; 145 146 case 'n': 147 m_keyCount = strtol(optarg, NULL, 0); 148 break; 149 150 default: 151 Log::log(Logger::ERROR, "error: unrecognized option\n\n"); 152 printUsage(options); 153 return 1; 154 } 155 } 156 157 // handle positional args 158 if (iter.index() < m_argc) 159 { 160// Log::SetOutputLevel leveler(Logger::DEBUG); 161// Log::log("positional args:\n"); 162 int i; 163 for (i = iter.index(); i < m_argc; ++i) 164 { 165// Log::log("%d: %s\n", i - iter.index(), m_argv[i]); 166 m_positionalArgs.push_back(m_argv[i]); 167 } 168 } 169 170 // all is well 171 return -1; 172 } 173 174 /*! 175 * Prints help for the tool. 176 */ 177 void printUsage(Options & options) 178 { 179 options.usage(std::cout, "key-files..."); 180 printf("%s", k_usageText); 181 } 182 183 /*! 184 * Core of the tool. Calls processOptions() to handle command line options 185 * before performing the real work the tool does. 186 */ 187 int run() 188 { 189 try 190 { 191 // read command line options 192 int result; 193 if ((result = processOptions()) != -1) 194 { 195 return result; 196 } 197 198 // set verbose logging 199 setVerboseLogging(); 200 201 // make sure a file was provided 202 if (m_positionalArgs.size() < 1) 203 { 204 throw std::runtime_error("no output file path was provided"); 205 } 206 207 // generate key files 208 string_vector_t::const_iterator it = m_positionalArgs.begin(); 209 for (; it != m_positionalArgs.end(); ++it) 210 { 211 generateKeyFile(*it); 212 } 213 } 214 catch (std::exception & e) 215 { 216 Log::log(Logger::ERROR, "error: %s\n", e.what()); 217 return 1; 218 } 219 catch (...) 220 { 221 Log::log(Logger::ERROR, "error: unexpected exception\n"); 222 return 1; 223 } 224 225 return 0; 226 } 227 228 /*! 229 * \brief Turns on verbose logging. 230 */ 231 void setVerboseLogging() 232 { 233 if (m_isVerbose) 234 { 235 // verbose only affects the INFO and DEBUG filter levels 236 // if the user has selected quiet mode, it overrides verbose 237 switch (Log::getLogger()->getFilterLevel()) 238 { 239 case Logger::INFO: 240 Log::getLogger()->setFilterLevel(Logger::INFO2); 241 break; 242 case Logger::DEBUG: 243 Log::getLogger()->setFilterLevel(Logger::DEBUG2); 244 break; 245 } 246 } 247 } 248 249 /*! 250 * \brief Opens the file at \a path and writes a random key file. 251 * 252 * Each key file will have #m_keyCount number of keys written into it, 253 * each on a line by itself. 254 */ 255 void generateKeyFile(const std::string & path) 256 { 257 std::ofstream outputStream(path.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); 258 if (outputStream.is_open()) 259 { 260 int i; 261 for (i = 0; i < m_keyCount; ++i) 262 { 263 AESKey<128> key; 264 key.randomize(); 265 key.writeToStream(outputStream); 266 267 // put a newline after the key 268 outputStream.write("\n", 1); 269 270 // dump it 271 dumpKey(key); 272 } 273 274 Log::log(Logger::INFO, "wrote key file %s\n", path.c_str()); 275 } 276 else 277 { 278 throw std::runtime_error("could not open output file"); 279 } 280 } 281 282 /*! 283 * \brief Write the value of each byte of the \a key to the log. 284 */ 285 void dumpKey(const AESKey<128> & key) 286 { 287 // dump key bytes 288 Log::log(Logger::INFO2, "key bytes: "); 289 AESKey<128>::key_t the_key; 290 key.getKey(&the_key); 291 int q; 292 for (q=0; q<16; q++) 293 { 294 Log::log(Logger::INFO2, "%02x ", the_key[q]); 295 } 296 Log::log(Logger::INFO2, "\n"); 297 } 298 299 /*! 300 * \brief Log an array of bytes as hex. 301 */ 302 void logHexArray(Logger::log_level_t level, const uint8_t * bytes, unsigned count) 303 { 304 Log::SetOutputLevel leveler(level); 305// Log::log(" "); 306 unsigned i; 307 for (i = 0; i < count; ++i, ++bytes) 308 { 309 if ((i % 16 == 0) && (i < count - 1)) 310 { 311 if (i != 0) 312 { 313 Log::log("\n"); 314 } 315 Log::log(" 0x%04x: ", i); 316 } 317 Log::log("%02x ", *bytes & 0xff); 318 } 319 320 Log::log("\n"); 321 } 322 323}; 324 325/*! 326 * Main application entry point. Creates an sbtool instance and lets it take over. 327 */ 328int main(int argc, char* argv[], char* envp[]) 329{ 330 try 331 { 332 return keygen(argc, argv).run(); 333 } 334 catch (...) 335 { 336 Log::log(Logger::ERROR, "error: unexpected exception\n"); 337 return 1; 338 } 339 340 return 0; 341} 342 343 344 345 346 347