290 lines
9.1 KiB
C++
290 lines
9.1 KiB
C++
|
|
// Copyright (c) 2016 Sequence Logic, Inc. All rights reserved.
|
||
|
|
//
|
||
|
|
// Command line program for RSA key generation.
|
||
|
|
|
||
|
|
#include "eyering.h"
|
||
|
|
|
||
|
|
#include <iostream>
|
||
|
|
#include <sstream>
|
||
|
|
#include <string>
|
||
|
|
#include <algorithm>
|
||
|
|
|
||
|
|
using namespace sequencelogic;
|
||
|
|
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
void printUsage(const char *pExe)
|
||
|
|
{
|
||
|
|
std::cout << "Usage:\n"
|
||
|
|
<< pExe << " [-h] [-listcr] [-human] -user {username} -cr1 {CR 1 question}::{CR 1 answer} -cr2 {CR 2 question}::{CR 2 answer} -cr3 {CR 3 question}::{CR 3 answer} "
|
||
|
|
<< "[-nosalt] [-noac] [-ac {account code}] [-salt {salt}]\n"
|
||
|
|
<< "-h -- Display this usage message.\n"
|
||
|
|
<< "-listcr -- Display a list of all the possible CR questions.\n"
|
||
|
|
<< "-human -- Also print out the keys in a \"human readable\" format. Default is false, regular output is JSON.\n"
|
||
|
|
<< "-user -- The username of the account to generate RSA keys for.\n"
|
||
|
|
<< "-cr[1,2,3] -- Three challenge/response questions and answers.\n"
|
||
|
|
<< "-nosalt -- Optional, don't generate a salt.\n"
|
||
|
|
<< "-noac -- Optional, don't generate an account code.\n"
|
||
|
|
<< "-ac -- Optional, use the passed in account code.\n"
|
||
|
|
<< "-salt -- Optional, use the passed in salt.\n"
|
||
|
|
<< "\nExample:\n"
|
||
|
|
<< " " << pExe << " -user viggy -cr1 favbook::hello -cr2 flame::there -cr3 kiss::sequencelogic" << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
void printCRQuests()
|
||
|
|
{
|
||
|
|
std::cout << "Challenge response questions:\n"
|
||
|
|
<< " favbook\n"
|
||
|
|
<< " flame\n"
|
||
|
|
<< " kiss\n"
|
||
|
|
<< " kissloc\n"
|
||
|
|
<< " concert\n"
|
||
|
|
<< " memperson\n"
|
||
|
|
<< " favchar\n"
|
||
|
|
<< " favsay\n"
|
||
|
|
<< " neverinv\n"
|
||
|
|
<< " bestcostume\n"
|
||
|
|
<< " growndiff\n"
|
||
|
|
<< " wallpic\n"
|
||
|
|
<< " childnames\n"
|
||
|
|
<< " songdesc\n"
|
||
|
|
<< " erasure\n"
|
||
|
|
<< " passphrase" << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
enum
|
||
|
|
{
|
||
|
|
E_HUMANREADABLE = 0x01,
|
||
|
|
E_HELP = 0x02,
|
||
|
|
E_LISTCRS = 0x04,
|
||
|
|
E_NOSALT = 0x08,
|
||
|
|
E_NOAC = 0x10
|
||
|
|
};
|
||
|
|
|
||
|
|
struct CmdLineData
|
||
|
|
{
|
||
|
|
std::string _user;
|
||
|
|
std::string _cr1;
|
||
|
|
std::string _cr2;
|
||
|
|
std::string _cr3;
|
||
|
|
std::string _accountCode;
|
||
|
|
std::string _salt;
|
||
|
|
unsigned short _nFlags;
|
||
|
|
|
||
|
|
CmdLineData() : _nFlags(0) {}
|
||
|
|
bool isHumanReadable() const { return (_nFlags & E_HUMANREADABLE) == E_HUMANREADABLE; }
|
||
|
|
bool isHelp() const { return (_nFlags & E_HELP) == E_HELP; }
|
||
|
|
bool isListCRs() const { return (_nFlags & E_LISTCRS) == E_LISTCRS; }
|
||
|
|
bool isNoSalt() const { return (_nFlags & E_NOSALT) == E_NOSALT; }
|
||
|
|
bool isNoAC() const { return (_nFlags & E_NOAC) == E_NOAC; }
|
||
|
|
};
|
||
|
|
bool parseCmdLine (int argc, const char *argv[], CmdLineData &opt);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, const char *argv[])
|
||
|
|
{
|
||
|
|
int nRetVal = 0;
|
||
|
|
|
||
|
|
CmdLineData cmdOpts;
|
||
|
|
if (parseCmdLine(argc, argv, cmdOpts))
|
||
|
|
{
|
||
|
|
if (cmdOpts.isHelp())
|
||
|
|
{
|
||
|
|
printUsage(argv[0]);
|
||
|
|
return nRetVal;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cmdOpts.isListCRs())
|
||
|
|
{
|
||
|
|
printCRQuests();
|
||
|
|
return nRetVal;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reformat for libeye. Just a JSON array...
|
||
|
|
std::stringstream jsonCRsStream;
|
||
|
|
jsonCRsStream << "["
|
||
|
|
<< "\"" << cmdOpts._user << "\","
|
||
|
|
<< "\"" << cmdOpts._cr1 << "\","
|
||
|
|
<< "\"" << cmdOpts._cr2 << "\","
|
||
|
|
<< "\"" << cmdOpts._cr3 << "\"]";
|
||
|
|
std::string jsonCRs = jsonCRsStream.str();
|
||
|
|
|
||
|
|
// Create the keys!
|
||
|
|
std::string salt = cmdOpts._salt, accountCode = cmdOpts._accountCode;
|
||
|
|
Keyring tmpKeyring ("Temp", "Temp keyring for key generation.");
|
||
|
|
if (cmdOpts.isNoSalt() && cmdOpts.isNoAC())
|
||
|
|
tmpKeyring.GenerateRSAKey("Key1", "Dummy desc", jsonCRs);
|
||
|
|
else if (cmdOpts.isNoAC())
|
||
|
|
tmpKeyring.ProvisionChallengeRSAKey("Key1", jsonCRs, salt);
|
||
|
|
else
|
||
|
|
tmpKeyring.ProvisionAccountRSAKey("Key1", accountCode, salt, jsonCRs);
|
||
|
|
|
||
|
|
Key tmpKey;
|
||
|
|
tmpKeyring.GetKey ("Key1", tmpKey);
|
||
|
|
char *pPubKey = tmpKey.GetPubKey();
|
||
|
|
|
||
|
|
// Dump them!
|
||
|
|
if (cmdOpts.isHumanReadable())
|
||
|
|
{
|
||
|
|
std::cout << "Keys for user '" << cmdOpts._user << "', with challenges:\n"
|
||
|
|
<< " CR1: " << cmdOpts._cr1 << "\n"
|
||
|
|
<< " CR2: " << cmdOpts._cr2 << "\n"
|
||
|
|
<< " CR3: " << cmdOpts._cr3 << "\n";
|
||
|
|
std::cout << "\n" << "Public:\n-------\n";
|
||
|
|
std::cout << pPubKey << "\n";
|
||
|
|
|
||
|
|
std::cout << "\nPrivate:\n--------\n";
|
||
|
|
std::cout << tmpKey.GetStringKey() << "\n";
|
||
|
|
|
||
|
|
std::cout << "Salt:\n-----\n" << salt << "\n";
|
||
|
|
|
||
|
|
std::cout << "Account Code:\n-------------\n" << accountCode << "\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
// Always dump JSON. Convert '\n' to "\\n"
|
||
|
|
std::stringstream tmpStream;
|
||
|
|
std::string pubKeyStr;
|
||
|
|
std::string tmpString;
|
||
|
|
|
||
|
|
tmpStream.str(pPubKey);
|
||
|
|
while(std::getline(tmpStream, tmpString))
|
||
|
|
pubKeyStr += (tmpString + "\\n");
|
||
|
|
pubKeyStr.pop_back();
|
||
|
|
pubKeyStr.pop_back();
|
||
|
|
|
||
|
|
std::string privKeyStr;
|
||
|
|
std::stringstream tmpStream2;
|
||
|
|
tmpStream2.str(reinterpret_cast<const char *>(tmpKey.GetKey()));
|
||
|
|
while (std::getline(tmpStream2, tmpString))
|
||
|
|
privKeyStr += (tmpString + "\\n");
|
||
|
|
privKeyStr.pop_back();
|
||
|
|
privKeyStr.pop_back();
|
||
|
|
|
||
|
|
std::cout << "{\n"
|
||
|
|
<< " \"user\": \"" << cmdOpts._user << "\",\n"
|
||
|
|
<< " \"public\": \"" << pubKeyStr << "\",\n"
|
||
|
|
<< " \"private\": \"" << privKeyStr << "\"\n"
|
||
|
|
<< " \"salt\": \"" << salt << "\"\n"
|
||
|
|
<< " \"acccode\": \"" << accountCode << "\"\n"
|
||
|
|
<< "}\n";
|
||
|
|
|
||
|
|
delete [] pPubKey;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
nRetVal = -1;
|
||
|
|
std::cerr << "Error parsing cmd line args." << std::endl;
|
||
|
|
printUsage(argv[0]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return nRetVal;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
bool parseCmdLine (int argc, const char *argv[], CmdLineData &opt)
|
||
|
|
{
|
||
|
|
bool bRetVal = true;
|
||
|
|
for (int i = 0; i < argc; ++i)
|
||
|
|
{
|
||
|
|
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
|
||
|
|
{
|
||
|
|
std::string arg = argv[i];
|
||
|
|
arg.erase(0, 1);
|
||
|
|
std::transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
|
||
|
|
switch (argv[i][1])
|
||
|
|
{
|
||
|
|
case 'a':
|
||
|
|
if (arg == "ac")
|
||
|
|
{
|
||
|
|
if (++i < argc)
|
||
|
|
{
|
||
|
|
opt._accountCode = argv[i];
|
||
|
|
std::cout << argv[i] << std::endl;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
std::cerr << "No account code specified." << std::endl;
|
||
|
|
bRetVal = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 'c':
|
||
|
|
if ((arg == "cr1") || (arg == "cr2") || (arg == "cr3"))
|
||
|
|
{
|
||
|
|
if (i+1 < argc)
|
||
|
|
{
|
||
|
|
switch (argv[i][3])
|
||
|
|
{
|
||
|
|
case '1':
|
||
|
|
opt._cr1 = argv[++i];
|
||
|
|
break;
|
||
|
|
case '2':
|
||
|
|
opt._cr2 = argv[++i];
|
||
|
|
break;
|
||
|
|
case '3':
|
||
|
|
opt._cr3 = argv[++i];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 'h':
|
||
|
|
if (arg == "human")
|
||
|
|
opt._nFlags |= E_HUMANREADABLE;
|
||
|
|
else if (arg == "help")
|
||
|
|
opt._nFlags |= E_HELP;
|
||
|
|
break;
|
||
|
|
case 'l':
|
||
|
|
if (arg == "listcr")
|
||
|
|
opt._nFlags |= E_LISTCRS;
|
||
|
|
break;
|
||
|
|
case 'n':
|
||
|
|
if (arg == "nosalt")
|
||
|
|
opt._nFlags |= E_NOSALT;
|
||
|
|
else if (arg == "noac")
|
||
|
|
opt._nFlags |= E_NOAC;
|
||
|
|
break;
|
||
|
|
case 's':
|
||
|
|
if (arg =="salt")
|
||
|
|
{
|
||
|
|
if (++i < argc)
|
||
|
|
opt._salt = argv[i];
|
||
|
|
else
|
||
|
|
{
|
||
|
|
std::cerr << "No salt specified." << std::endl;
|
||
|
|
bRetVal = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case 'u':
|
||
|
|
if (arg == "user")
|
||
|
|
{
|
||
|
|
if (++i < argc)
|
||
|
|
opt._user = argv[i];
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Need at least user, and 3 CRs.
|
||
|
|
if (!opt.isHelp() && !opt.isListCRs() && (opt._user == ""))
|
||
|
|
{
|
||
|
|
std::cerr << "Error: no user specified.\n";
|
||
|
|
bRetVal = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((!opt.isHelp() && !opt.isListCRs()) && ((opt._cr1 == "") || (opt._cr2 == "") || (opt._cr3 == "")))
|
||
|
|
{
|
||
|
|
std::cerr << "Error: missing challenge/response input.\n";
|
||
|
|
bRetVal = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return bRetVal;
|
||
|
|
}
|
||
|
|
}
|