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.
22 const char k_toolName[] = "keygen";
23
24 //! Current version number for the tool.
25 const char k_version[] = "1.0";
26
27 //! Copyright string.
28 const char k_copyright[] = "Copyright (c) 2006-2009 Freescale Semiconductor, Inc.\nAll rights reserved.";
29
30 //! Definition of command line options.
31 static 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.
41 const 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.
49 typedef std::vector<std::string> string_vector_t;
50
51 // prototypes
52 int 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 */
61 class keygen
62 {
63 protected:
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
71 public:
72 /*!
73 * Constructor.
74 *
75 * Creates the singleton logger instance.
76 */
keygen(int argc,char * argv[])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 */
~keygen()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 */
processOptions()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 */
printUsage(Options & options)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 */
run()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 */
setVerboseLogging()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 */
generateKeyFile(const std::string & path)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 */
dumpKey(const AESKey<128> & key)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 */
logHexArray(Logger::log_level_t level,const uint8_t * bytes,unsigned count)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 */
main(int argc,char * argv[],char * envp[])328 int 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