Fixed several ndncert-client bugs and use lower case Challenge ID
refs: #4962
Change-Id: Id10dcc15cb718d6a55f4657884b2c6be3f653867
diff --git a/src/ca-config.cpp b/src/ca-config.cpp
index b8b0d59..3165b2d 100644
--- a/src/ca-config.cpp
+++ b/src/ca-config.cpp
@@ -66,7 +66,7 @@
std::list<std::string> result;
auto it = section.begin();
for (; it != section.end(); it++) {
- result.push_back(it->second.get<std::string>("type"));
+ result.push_back(boost::algorithm::to_lower_copy(it->second.get<std::string>("type")));
}
return result;
}
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index 20761e0..89bf8a7 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -189,11 +189,9 @@
}
// generate salt for HKDF
auto saltInt = random::generateSecureWord64();
- uint8_t salt[sizeof(saltInt)];
- std::memcpy(salt, &saltInt, sizeof(saltInt));
// hkdf
hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
- salt, sizeof(saltInt), m_aesKey, 32);
+ (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
// parse certificate request
std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
@@ -319,7 +317,7 @@
// decrypt the parameters
Buffer paramJsonPayload;
try {
- paramJsonPayload = parseEncBlock(m_aesKey, 32,
+ paramJsonPayload = parseEncBlock(m_aesKey, sizeof(m_aesKey),
request.getApplicationParameters());
}
catch (const std::exception& e) {
@@ -403,7 +401,7 @@
std::stringstream ss2;
boost::property_tree::write_json(ss2, contentJson);
auto payload = ss2.str();
- auto contentBlock = genEncBlock(tlv::Content, m_aesKey, 32,
+ auto contentBlock = genEncBlock(tlv::Content, m_aesKey, sizeof(m_aesKey),
(const uint8_t*)payload.c_str(), payload.size());
result.setContent(contentBlock);
m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index e475b24..b22ec93 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -28,7 +28,7 @@
_LOG_INIT(ndncert.ChallengeEmail);
-NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "Email");
+NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "email");
const std::string ChallengeEmail::NEED_CODE = "need-code";
const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
@@ -39,7 +39,7 @@
ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
const size_t& maxAttemptTimes,
const time::seconds secretLifetime)
- : ChallengeModule("Email")
+ : ChallengeModule("email")
, m_sendEmailScript(scriptPath)
, m_maxAttemptTimes(maxAttemptTimes)
, m_secretLifetime(secretLifetime)
diff --git a/src/challenge-module/challenge-pin.cpp b/src/challenge-module/challenge-pin.cpp
index 7de32f0..9534b21 100644
--- a/src/challenge-module/challenge-pin.cpp
+++ b/src/challenge-module/challenge-pin.cpp
@@ -27,14 +27,14 @@
_LOG_INIT(ndncert.challenge-pin);
-NDNCERT_REGISTER_CHALLENGE(ChallengePin, "PIN");
+NDNCERT_REGISTER_CHALLENGE(ChallengePin, "pin");
const std::string ChallengePin::NEED_CODE = "need-code";
const std::string ChallengePin::WRONG_CODE = "wrong-code";
const std::string ChallengePin::JSON_PIN_CODE = "pin-code";
ChallengePin::ChallengePin(const size_t& maxAttemptTimes, const time::seconds& secretLifetime)
- : ChallengeModule("PIN")
+ : ChallengeModule("pin")
, m_secretLifetime(secretLifetime)
, m_maxAttemptTimes(maxAttemptTimes)
{
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 28e535b..12d835d 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -194,12 +194,11 @@
const auto& peerKeyBase64Str = contentJson.get<std::string>(JSON_CA_ECDH, "");
const auto& saltStr = contentJson.get<std::string>(JSON_CA_SALT, "");
uint64_t saltInt = std::stoull(saltStr);
- uint8_t salt[sizeof(saltInt)];
- std::memcpy(salt, &saltInt, sizeof(saltInt));
m_ecdh.deriveSecret(peerKeyBase64Str);
// HKDF
- hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen, salt, sizeof(saltInt), m_aesKey, 32);
+ hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
+ (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
// update state
m_status = contentJson.get<int>(JSON_CA_STATUS);
@@ -228,7 +227,7 @@
std::stringstream ss;
boost::property_tree::write_json(ss, paramJson);
auto payload = ss.str();
- auto paramBlock = genEncBlock(tlv::ApplicationParameters, m_aesKey, 32,
+ auto paramBlock = genEncBlock(tlv::ApplicationParameters, m_aesKey, sizeof(m_aesKey),
(const uint8_t*)payload.c_str(), payload.size());
interest->setApplicationParameters(paramBlock);
@@ -243,7 +242,7 @@
_LOG_ERROR("Cannot verify data signature from " << m_ca.m_caName.toUri());
return;
}
- auto result = parseEncBlock(m_aesKey, 32, reply.getContent());
+ auto result = parseEncBlock(m_aesKey, sizeof(m_aesKey), reply.getContent());
std::string payload((const char*)result.data(), result.size());
std::istringstream ss(payload);
JsonSection contentJson;
diff --git a/tests/unit-tests/challenge-email.t.cpp b/tests/unit-tests/challenge-email.t.cpp
index e67397e..398df97 100644
--- a/tests/unit-tests/challenge-email.t.cpp
+++ b/tests/unit-tests/challenge-email.t.cpp
@@ -31,7 +31,7 @@
BOOST_AUTO_TEST_CASE(ChallengeType)
{
ChallengeEmail challenge;
- BOOST_CHECK_EQUAL(challenge.CHALLENGE_TYPE, "Email");
+ BOOST_CHECK_EQUAL(challenge.CHALLENGE_TYPE, "email");
}
BOOST_AUTO_TEST_CASE(EmailAddressChecker)
@@ -60,7 +60,7 @@
BOOST_CHECK(request.m_remainingTime != 0);
BOOST_CHECK(request.m_remainingTries != 0);
BOOST_CHECK(request.m_challengeTp != "");
- BOOST_CHECK_EQUAL(request.m_challengeType, "Email");
+ BOOST_CHECK_EQUAL(request.m_challengeType, "email");
std::string line = "";
std::string delimiter = " ";
@@ -136,7 +136,7 @@
JsonSection json;
json.put(ChallengeEmail::JSON_CODE, "4567");
CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengeEmail::NEED_CODE,
- "Email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert);
+ "email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert);
JsonSection requestJson;
requestJson.put(ChallengeEmail::JSON_CODE, "7890");
diff --git a/tests/unit-tests/challenge-pin.t.cpp b/tests/unit-tests/challenge-pin.t.cpp
index 35466ff..adac6b9 100644
--- a/tests/unit-tests/challenge-pin.t.cpp
+++ b/tests/unit-tests/challenge-pin.t.cpp
@@ -31,7 +31,7 @@
BOOST_AUTO_TEST_CASE(ChallengeType)
{
ChallengePin challenge;
- BOOST_CHECK_EQUAL(challenge.CHALLENGE_TYPE, "PIN");
+ BOOST_CHECK_EQUAL(challenge.CHALLENGE_TYPE, "pin");
}
BOOST_AUTO_TEST_CASE(OnChallengeRequestWithEmptyInfo)
@@ -46,7 +46,7 @@
BOOST_CHECK_EQUAL(request.m_status, STATUS_CHALLENGE);
BOOST_CHECK_EQUAL(request.m_challengeStatus, ChallengePin::NEED_CODE);
- BOOST_CHECK_EQUAL(request.m_challengeType, "PIN");
+ BOOST_CHECK_EQUAL(request.m_challengeType, "pin");
}
BOOST_AUTO_TEST_CASE(OnChallengeRequestWithCode)
@@ -56,7 +56,7 @@
auto cert = key.getDefaultCertificate();
JsonSection secret;
secret.add(ChallengePin::JSON_PIN_CODE, "12345");
- CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengePin::NEED_CODE, "PIN",
+ CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengePin::NEED_CODE, "pin",
time::toIsoString(time::system_clock::now()), 3600, 3, secret, cert);
JsonSection paramJson;
@@ -77,7 +77,7 @@
auto cert = key.getDefaultCertificate();
JsonSection secret;
secret.add(ChallengePin::JSON_PIN_CODE, "12345");
- CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengePin::NEED_CODE, "PIN",
+ CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengePin::NEED_CODE, "pin",
time::toIsoString(time::system_clock::now()), 3600, 3, secret, cert);
JsonSection paramJson;
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index 66a81ef..2c47424 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -22,7 +22,6 @@
#include "challenge-module.hpp"
#include <iostream>
#include <string>
-#include <algorithm>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
@@ -37,6 +36,7 @@
Face face;
security::v2::KeyChain keyChain;
std::string challengeType;
+int validityPeriod = -1;
ClientModule client(keyChain);
static std::list<std::string>
@@ -79,6 +79,31 @@
}
static void
+captureValidityPeriod()
+{
+ if (validityPeriod <= 0) {
+ std::cerr << "Step " << nStep++
+ << ": Please type in your expected validity period of your certificate."
+ << " Type the number of hours (168 for week, 730 for month, 8760 for year). The CA may change the validity"
+ << " period if your expected period is too long." << std::endl;
+ std::string periodStr = "";
+ do {
+ getline(std::cin, periodStr);
+ try {
+ validityPeriod = std::stoi(periodStr);
+ }
+ catch (const std::exception& e) {
+ validityPeriod = -1;
+ }
+ if (validityPeriod > 0) {
+ break;
+ }
+ std::cerr << "Invalid period time. Please input the period again." << std::endl;
+ } while (true);
+ }
+}
+
+static void
onNackCb()
{
std::cerr << "Got NACK\n";
@@ -111,50 +136,70 @@
}
auto challenge = ChallengeModule::createChallengeModule(challengeType);
- auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(), client.getChallengeStatus());
+ auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
+ client.getChallengeStatus());
if (requirement.size() > 0) {
std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
std::string redo = "";
std::list<std::string> capturedParams;
do {
capturedParams = captureParams(requirement);
- std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
+ std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
getline(std::cin, redo);
- std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
- } while (redo == "REDO");
+ boost::algorithm::to_lower(redo);
+ } while (redo != "ok");
auto it1 = capturedParams.begin();
auto it2 = requirement.begin();
for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
it2->second.put("", *it1);
}
}
- face.expressInterest(*client.generateChallengeInterest(
- challenge->genChallengeRequestJson(
- client.getApplicationStatus(),
- client.getChallengeStatus(),
- requirement)),
- bind(&challengeCb, _2),
- bind(&onNackCb),
- bind(&timeoutCb));
+ face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestJson(client.getApplicationStatus(),
+ client.getChallengeStatus(),
+ requirement)),
+ bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
static void
newCb(const Data& reply)
{
+ int challengeIndex = 0;
auto challengeList = client.onNewResponse(reply);
- std::cerr << "Step " << nStep++ << ": Please type in the challenge ID from the following challenges\n";
- for (auto item : challengeList) {
- std::cerr << "\t" << item << std::endl;
+ if (challengeList.size() < 1) {
+ std::cerr << "There is no available challenge provided by the CA. Exit" << std::endl;
+ return;
}
- std::string choice;
- getline(std::cin, choice);
-
- auto challenge = ChallengeModule::createChallengeModule(choice);
+ else if (challengeList.size() > 1) {
+ int count = 0;
+ std::string choice = "";
+ std::cerr << "Step " << nStep++ << ": Please type in the challenge index that you want to perform\n";
+ do {
+ count = 0;
+ for (auto item : challengeList) {
+ std::cerr << "\t" << count++ << " : "<< item << std::endl;
+ }
+ getline(std::cin, choice);
+ try {
+ challengeIndex = std::stoi(choice);
+ }
+ catch (const std::exception& e) {
+ challengeIndex = -1;
+ }
+ if (challengeIndex >= 0 && challengeIndex < count) {
+ break;
+ }
+ std::cerr << "Your input index is out of range. Please type in again." << std::endl;
+ } while (true);
+ }
+ auto it = challengeList.begin();
+ std::advance(it, challengeIndex);
+ unique_ptr<ChallengeModule> challenge = ChallengeModule::createChallengeModule(*it);
if (challenge != nullptr) {
- challengeType = choice;
+ challengeType = *it;
+ std::cerr << "The challenge has been selected: " << challengeType << std::endl;
}
else {
- std::cerr << "Cannot recognize the specified challenge. Exit";
+ std::cerr << "Error. Cannot load selected Challenge Module. Exit." << std::endl;
return;
}
auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
@@ -165,24 +210,20 @@
std::list<std::string> capturedParams;
do {
capturedParams = captureParams(requirement);
- std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
+ std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
getline(std::cin, redo);
- std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
- } while (redo == "REDO");
+ boost::algorithm::to_lower(redo);
+ } while (redo != "ok");
auto it1 = capturedParams.begin();
auto it2 = requirement.begin();
for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
it2->second.put("", *it1);
}
}
- face.expressInterest(*client.generateChallengeInterest(
- challenge->genChallengeRequestJson(
- client.getApplicationStatus(),
- client.getChallengeStatus(),
- requirement)),
- bind(&challengeCb, _2),
- bind(&onNackCb),
- bind(&timeoutCb));
+ face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestJson(client.getApplicationStatus(),
+ client.getChallengeStatus(),
+ requirement)),
+ bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
static void
@@ -198,14 +239,14 @@
std::string answer;
getline(std::cin, answer);
- std::transform(answer.begin(), answer.end(), answer.begin(), ::toupper);
- if (answer == "YES") {
+ boost::algorithm::to_lower(answer);
+ if (answer == "yes") {
client.onProbeInfoResponse(reply);
- std::cerr << "You answered YES: new CA installed" << std::endl;
+ std::cerr << "You answered YES: new CA has been installed" << std::endl;
startApplication();
}
else {
- std::cerr << "New CA not installed" << std::endl;
+ std::cerr << "New CA will not be installed" << std::endl;
return;
}
}
@@ -214,31 +255,13 @@
probeCb(const Data& reply)
{
client.onProbeResponse(reply);
- std::cerr << "Step " << nStep++
- << ": Please type in your expected validity period of your certificate."
- << " Type in a number in unit of hour. The CA may change the validity"
- << " period if your expected period is too long." << std::endl;
- std::string periodStr;
- int hours = 0;
- getline(std::cin, periodStr);
- hours = std::stoi(periodStr);
- while (hours <= 0) {
- std::cerr << "Invalid period time: " << "Please input the period again." << std::endl;
- getline(std::cin, periodStr);
- try {
- hours = std::stoi(periodStr);
- }
- catch (const std::exception& e) {
- hours = -1;
- }
- }
+ captureValidityPeriod();
auto probeToken = make_shared<Data>(reply);
auto now = time::system_clock::now();
- face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
+ std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
+ face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
Name(), probeToken),
- bind(&newCb, _2),
- bind(&onNackCb),
- bind(&timeoutCb));
+ bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
static void
@@ -259,20 +282,28 @@
<< nStep++ << ": Please type in the CA INDEX that you want to apply"
<< " or type in NONE if your expected CA is not in the list\n";
- std::string caIndexS, caIndexSUpper;
+ std::string caIndexS, caIndexSLower;
getline(std::cin, caIndexS);
- caIndexSUpper = caIndexS;
- std::transform(caIndexSUpper.begin(), caIndexSUpper.end(), caIndexSUpper.begin(), ::toupper);
- if (caIndexSUpper == "NONE") {
+ caIndexSLower = caIndexS;
+ boost::algorithm::to_lower(caIndexSLower);
+ if (caIndexSLower == "none") {
std::cerr << "Step " << nStep << ": Please type in the CA Name\n";
face.expressInterest(*client.generateProbeInfoInterest(Name(caIndexS)),
- bind(&probeInfoCb, _2),
- bind(&onNackCb),
- bind(&timeoutCb));
+ bind(&probeInfoCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
else {
- int caIndex = std::stoi(caIndexS);
- BOOST_ASSERT(caIndex <= count);
+ int caIndex;
+ try {
+ caIndex = std::stoi(caIndexS);
+ }
+ catch (const std::exception& e) {
+ std::cerr << "Your input is neither NONE nor a valid index. Exit" << std::endl;
+ return;
+ }
+ if (caIndex < 0 || caIndex >= count) {
+ std::cerr << "Your input is not an existing index. Exit" << std::endl;
+ return;
+ }
auto targetCaItem = caVector[caIndex];
if (targetCaItem.m_probe != "") {
@@ -282,10 +313,10 @@
std::list<std::string> capturedParams;
do {
capturedParams = captureParams(probeFields);
- std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
+ std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
getline(std::cin, redo);
- std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
- } while (redo == "REDO");
+ boost::algorithm::to_lower(redo);
+ } while (redo != "ok");
std::string probeInfo;
for (const auto& item : capturedParams) {
probeInfo += item;
@@ -293,27 +324,18 @@
}
probeInfo = probeInfo.substr(0, probeInfo.size() - 1);
face.expressInterest(*client.generateProbeInterest(targetCaItem, probeInfo),
- bind(&probeCb, _2),
- bind(&onNackCb),
- bind(&timeoutCb));
+ bind(&probeCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
else {
std::cerr << "Step " << nStep++ << ": Please type in the identity name you want to get (with CA prefix)\n";
std::string identityNameStr;
getline(std::cin, identityNameStr);
- std::cerr << "Step "
- << nStep++ << ": Please type in your expected validity period of your certificate."
- << "Type in a number in unit of hour."
- << " The CA may change the validity period if your expected period is too long.\n";
- std::string periodStr;
- getline(std::cin, periodStr);
- int hours = std::stoi(periodStr);
+ captureValidityPeriod();
+ std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
auto now = time::system_clock::now();
- face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
+ face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
Name(identityNameStr)),
- bind(&newCb, _2),
- bind(&onNackCb),
- bind(&timeoutCb));
+ bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
}
}
@@ -324,10 +346,11 @@
{
namespace po = boost::program_options;
std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
- po::options_description description("General Usage\n ndncert-client [-h] [-f]\n");
+ po::options_description description("General Usage\n ndncert-client [-h] [-c] [-v]\n");
description.add_options()
("help,h", "produce help message")
- ("config-file,f", po::value<std::string>(&configFilePath), "config file name");
+ ("config-file,c", po::value<std::string>(&configFilePath), "config file name")
+ ("validity-period,v", po::value<int>(&validityPeriod)->default_value(-1), "the validity period of your certificate being requested");
po::positional_options_description p;
po::variables_map vm;