//! Help string.
const char k_usageText[] = "\nOptions:\n\
-?/--help Show this help\n\
-v/--version Display tool version\n\
-q/--quiet Output only warnings and errors\n\
-V/--verbose Print extra detailed log information\n\
-n/--number <int> Number of keys to generate per file (default=1)\n\n";
//! An array of strings.
typedef std::vector<std::string> string_vector_t;
// prototypes
int main(int argc, char* argv[], char* envp[]);
/*!
* \brief Class that encapsulates the keygen interface.
*
* A single global logger instance is created during object construction. It is
* never freed because we need it up to the last possible minute, when an
* exception could be thrown.
*/
class keygen
{
protected:
int m_argc; //!< Number of command line arguments.
char ** m_argv; //!< String value for each command line argument.
StdoutLogger * m_logger; //!< Singleton logger instance.
string_vector_t m_positionalArgs; //!< Arguments coming after explicit options.
bool m_isVerbose; //!< Whether the verbose flag was turned on.
int m_keyCount; //!< Number of keys to generate.
/*!
* Reads the command line options passed into the constructor.
*
* This method can return a return code to its caller, which will cause the
* tool to exit immediately with that return code value. Normally, though, it
* will return -1 to signal that the tool should continue to execute and
* all options were processed successfully.
*
* The Options class is used to parse command line options. See
* #k_optionsDefinition for the list of options and #k_usageText for the
* descriptive help for each option.
*
* \retval -1 The options were processed successfully. Let the tool run normally.
* \return A zero or positive result is a return code value that should be
* returned from the tool as it exits immediately.
*/
int processOptions()
{
Options options(*m_argv, k_optionsDefinition);
OptArgvIter iter(--m_argc, ++m_argv);
// process command line options
int optchar;
const char * optarg;
while (optchar = options(iter, optarg))
{
switch (optchar)
{
case '?':
printUsage(options);
return 0;
case 'v':
printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
return 0;
case 'd':
Log::getLogger()->setFilterLevel(Logger::DEBUG);
break;
case 'q':
Log::getLogger()->setFilterLevel(Logger::WARNING);
break;
case 'V':
m_isVerbose = true;
break;
case 'n':
m_keyCount = strtol(optarg, NULL, 0);
break;
// handle positional args
if (iter.index() < m_argc)
{
// Log::SetOutputLevel leveler(Logger::DEBUG);
// Log::log("positional args:\n");
int i;
for (i = iter.index(); i < m_argc; ++i)
{
// Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
m_positionalArgs.push_back(m_argv[i]);
}
}
// all is well
return -1;
}
/*!
* Prints help for the tool.
*/
void printUsage(Options & options)
{
options.usage(std::cout, "key-files...");
printf("%s", k_usageText);
}
/*!
* Core of the tool. Calls processOptions() to handle command line options
* before performing the real work the tool does.
*/
int run()
{
try
{
// read command line options
int result;
if ((result = processOptions()) != -1)
{
return result;
}
// set verbose logging
setVerboseLogging();
// make sure a file was provided
if (m_positionalArgs.size() < 1)
{
throw std::runtime_error("no output file path was provided");
}
/*!
* \brief Turns on verbose logging.
*/
void setVerboseLogging()
{
if (m_isVerbose)
{
// verbose only affects the INFO and DEBUG filter levels
// if the user has selected quiet mode, it overrides verbose
switch (Log::getLogger()->getFilterLevel())
{
case Logger::INFO:
Log::getLogger()->setFilterLevel(Logger::INFO2);
break;
case Logger::DEBUG:
Log::getLogger()->setFilterLevel(Logger::DEBUG2);
break;
}
}
}
/*!
* \brief Opens the file at \a path and writes a random key file.
*
* Each key file will have #m_keyCount number of keys written into it,
* each on a line by itself.
*/
void generateKeyFile(const std::string & path)
{
std::ofstream outputStream(path.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
if (outputStream.is_open())
{
int i;
for (i = 0; i < m_keyCount; ++i)
{
AESKey<128> key;
key.randomize();
key.writeToStream(outputStream);
// put a newline after the key
outputStream.write("\n", 1);
// dump it
dumpKey(key);
}
Log::log(Logger::INFO, "wrote key file %s\n", path.c_str());
}
else
{
throw std::runtime_error("could not open output file");
}
}
/*!
* \brief Write the value of each byte of the \a key to the log.
*/
void dumpKey(const AESKey<128> & key)
{
// dump key bytes
Log::log(Logger::INFO2, "key bytes: ");
AESKey<128>::key_t the_key;
key.getKey(&the_key);
int q;
for (q=0; q<16; q++)
{
Log::log(Logger::INFO2, "%02x ", the_key[q]);
}
Log::log(Logger::INFO2, "\n");
}
/*!
* \brief Log an array of bytes as hex.
*/
void logHexArray(Logger::log_level_t level, const uint8_t * bytes, unsigned count)
{
Log::SetOutputLevel leveler(level);
// Log::log(" ");
unsigned i;
for (i = 0; i < count; ++i, ++bytes)
{
if ((i % 16 == 0) && (i < count - 1))
{
if (i != 0)
{
Log::log("\n");
}
Log::log(" 0x%04x: ", i);
}
Log::log("%02x ", *bytes & 0xff);
}
Log::log("\n");
}
};
/*!
* Main application entry point. Creates an sbtool instance and lets it take over.
*/
int main(int argc, char* argv[], char* envp[])
{
try
{
return keygen(argc, argv).run();
}
catch (...)
{
Log::log(Logger::ERROR, "error: unexpected exception\n");
return 1;
}