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