refactor client module to requester
Change-Id: I2b9835af7f03942bfdb6a886c95cfb4b907e2068
diff --git a/src/challenge-modules/challenge-email.cpp b/src/challenge-modules/challenge-email.cpp
index 327fb79..c12ba88 100644
--- a/src/challenge-modules/challenge-email.cpp
+++ b/src/challenge-modules/challenge-email.cpp
@@ -19,7 +19,6 @@
*/
#include "challenge-email.hpp"
-#include "../ca-module.hpp"
#include <regex>
namespace ndn {
diff --git a/src/challenge-modules/challenge-pin.cpp b/src/challenge-modules/challenge-pin.cpp
index 4853eed..fd5b5a5 100644
--- a/src/challenge-modules/challenge-pin.cpp
+++ b/src/challenge-modules/challenge-pin.cpp
@@ -19,7 +19,6 @@
*/
#include "challenge-pin.hpp"
-
#include <ndn-cxx/util/random.hpp>
namespace ndn {
diff --git a/src/client-module.cpp b/src/client-module.cpp
deleted file mode 100644
index ffda468..0000000
--- a/src/client-module.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2017-2020, Regents of the University of California.
- *
- * This file is part of ndncert, a certificate management system based on NDN.
- *
- * ndncert is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndncert authors and contributors.
- */
-
-#include "client-module.hpp"
-
-#include <ndn-cxx/security/signing-helpers.hpp>
-#include <ndn-cxx/security/transform/base64-encode.hpp>
-#include <ndn-cxx/security/transform/buffer-source.hpp>
-#include <ndn-cxx/security/transform/stream-sink.hpp>
-#include <ndn-cxx/security/verification-helpers.hpp>
-#include <ndn-cxx/util/io.hpp>
-#include <ndn-cxx/util/random.hpp>
-
-#include "challenge-module.hpp"
-#include "crypto-support/enc-tlv.hpp"
-#include "protocol-detail/challenge.hpp"
-#include "protocol-detail/info.hpp"
-#include "protocol-detail/new-renew-revoke.hpp"
-#include "protocol-detail/probe.hpp"
-#include "protocol-detail/error.hpp"
-#include "ndncert-common.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-_LOG_INIT(ndncert.client);
-
-ClientModule::ClientModule(security::v2::KeyChain& keyChain)
- : m_keyChain(keyChain)
-{
-}
-
-ClientModule::~ClientModule()
-{
- endSession();
-}
-
-shared_ptr<Interest>
-ClientModule::generateInfoInterest(const Name& caName)
-{
- Name interestName = caName;
- if (readString(caName.at(-1)) != "CA")
- interestName.append("CA");
- interestName.append("INFO");
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
- return interest;
-}
-
-bool
-ClientModule::verifyInfoResponse(const Data& reply)
-{
- // parse the ca item
- auto caItem = INFO::decodeDataContent(reply.getContent());
-
- // verify the probe Data's sig
- if (!security::verifySignature(reply, *caItem.m_cert)) {
- _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
- return false;
- }
- return true;
-}
-
-void
-ClientModule::addCaFromInfoResponse(const Data& reply)
-{
- const Block& contentBlock = reply.getContent();
-
- // parse the ca item
- auto caItem = INFO::decodeDataContent(contentBlock);
-
- // update the local config
- bool findItem = false;
- for (auto& item : m_config.m_caItems) {
- if (item.m_caPrefix == caItem.m_caPrefix) {
- findItem = true;
- item = caItem;
- }
- }
- if (!findItem) {
- m_config.m_caItems.push_back(caItem);
- }
-}
-
-shared_ptr<Interest>
-ClientModule::generateProbeInterest(const CaConfigItem& ca,
- std::vector<std::tuple<std::string, std::string>>&& probeInfo)
-{
- Name interestName = ca.m_caPrefix;
- interestName.append("CA").append("PROBE");
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
- interest->setApplicationParameters(PROBE::encodeApplicationParameters(std::move(probeInfo)));
-
- // update local state
- m_ca = ca;
- return interest;
-}
-
-void
-ClientModule::onProbeResponse(const Data& reply)
-{
- if (!security::verifySignature(reply, *m_ca.m_cert)) {
- _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
- return;
- }
-
- // error handling
- processIfError(reply);
-
- auto contentTLV = reply.getContent();
- contentTLV.parse();
-
- // read the available name and put it into the state
- if (contentTLV.get(tlv_probe_response).hasValue()) {
- Block probeResponseBlock = contentTLV.get(tlv_probe_response);
- probeResponseBlock.parse();
- m_identityName.wireDecode(probeResponseBlock.get(tlv::Name));
- }
- else {
- NDN_LOG_TRACE("The JSON_CA_NAME is empty.");
- }
-}
-
-shared_ptr<Interest>
-ClientModule::generateNewInterest(const time::system_clock::TimePoint& notBefore,
- const time::system_clock::TimePoint& notAfter,
- const Name& identityName)
-{
- // Name requestedName = identityName;
- if (!identityName.empty()) { // if identityName is not empty, find the corresponding CA
- bool findCa = false;
- for (const auto& caItem : m_config.m_caItems) {
- if (caItem.m_caPrefix.isPrefixOf(identityName)) {
- m_ca = caItem;
- findCa = true;
- }
- }
- if (!findCa) { // if cannot find, cannot proceed
- return nullptr;
- }
- m_identityName = identityName;
- }
- else { // if identityName is empty, check m_identityName or generate a random name
- if (!m_identityName.empty()) {
- // do nothing
- }
- else {
- NDN_LOG_TRACE("Randomly create a new name because m_identityName is empty and the param is empty.");
- auto id = std::to_string(random::generateSecureWord64());
- m_identityName = m_ca.m_caPrefix;
- m_identityName.append(id);
- }
- }
-
- // generate a newly key pair or use an existing key
- const auto& pib = m_keyChain.getPib();
- security::pib::Identity identity;
- try {
- identity = pib.getIdentity(m_identityName);
- }
- catch (const security::Pib::Error& e) {
- identity = m_keyChain.createIdentity(m_identityName);
- m_isNewlyCreatedIdentity = true;
- m_isNewlyCreatedKey = true;
- }
- try {
- m_key = identity.getDefaultKey();
- }
- catch (const security::Pib::Error& e) {
- m_key = m_keyChain.createKey(identity);
- m_isNewlyCreatedKey = true;
- }
-
- // generate certificate request
- security::v2::Certificate certRequest;
- certRequest.setName(Name(m_key.getName()).append("cert-request").appendVersion());
- certRequest.setContentType(tlv::ContentType_Key);
- certRequest.setContent(m_key.getPublicKey().data(), m_key.getPublicKey().size());
- SignatureInfo signatureInfo;
- signatureInfo.setValidityPeriod(security::ValidityPeriod(notBefore, notAfter));
- m_keyChain.sign(certRequest, signingByKey(m_key.getName()).setSignatureInfo(signatureInfo));
-
- // generate Interest packet
- Name interestName = m_ca.m_caPrefix;
- interestName.append("CA").append("NEW");
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
- interest->setApplicationParameters(
- NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::NEW, m_ecdh.getBase64PubKey(), certRequest));
-
- // sign the Interest packet
- m_keyChain.sign(*interest, signingByKey(m_key.getName()));
- return interest;
-}
-
-std::list<std::string>
-ClientModule::onNewRenewRevokeResponse(const Data& reply)
-{
- if (!security::verifySignature(reply, *m_ca.m_cert)) {
- _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
- return std::list<std::string>();
- }
-
- // error handling
- processIfError(reply);
-
- auto contentTLV = reply.getContent();
- contentTLV.parse();
-
- // ECDH
- const auto& peerKeyBase64Str = readString(contentTLV.get(tlv_ecdh_pub));
- const auto& saltStr = readString(contentTLV.get(tlv_salt));
- uint64_t saltInt = std::stoull(saltStr);
- m_ecdh.deriveSecret(peerKeyBase64Str);
-
- // HKDF
- hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
- (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
-
- // update state
- m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
- m_requestId = readString(contentTLV.get(tlv_request_id));
- m_challengeList.clear();
- for (auto const& element : contentTLV.elements()) {
- if (element.type() == tlv_challenge) {
- m_challengeList.push_back(readString(element));
- }
- }
- return m_challengeList;
-}
-
-shared_ptr<Interest>
-ClientModule::generateRevokeInterest(const security::v2::Certificate& certificate)
-{
- // Name requestedName = identityName;
- bool findCa = false;
- for (const auto& caItem : m_config.m_caItems) {
- if (caItem.m_caPrefix.isPrefixOf(certificate.getName())) {
- m_ca = caItem;
- findCa = true;
- }
- }
- if (!findCa) { // if cannot find, cannot proceed
- _LOG_TRACE("Cannot find corresponding CA for the certificate.");
- return nullptr;
- }
-
- // generate Interest packet
- Name interestName = m_ca.m_caPrefix;
- interestName.append("CA").append("REVOKE");
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
- interest->setApplicationParameters(
- NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::REVOKE, m_ecdh.getBase64PubKey(), certificate));
-
- // return the Interest packet
- return interest;
-}
-
-shared_ptr<Interest>
-ClientModule::generateChallengeInterest(const Block& challengeRequest)
-{
- challengeRequest.parse();
- m_challengeType = readString(challengeRequest.get(tlv_selected_challenge));
-
- Name interestName = m_ca.m_caPrefix;
- interestName.append("CA").append("CHALLENGE").append(m_requestId);
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
-
- // encrypt the Interest parameters
- auto paramBlock = encodeBlockWithAesGcm128(tlv::ApplicationParameters, m_aesKey,
- challengeRequest.value(), challengeRequest.value_size(),
- (const uint8_t*)"test", strlen("test"));
- interest->setApplicationParameters(paramBlock);
-
- m_keyChain.sign(*interest, signingByKey(m_key.getName()));
- return interest;
-}
-
-void
-ClientModule::onChallengeResponse(const Data& reply)
-{
- if (!security::verifySignature(reply, *m_ca.m_cert)) {
- _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
- return;
- }
-
- // error handling
- processIfError(reply);
-
- auto result = decodeBlockWithAesGcm128(reply.getContent(), m_aesKey, (const uint8_t*)"test", strlen("test"));
-
- Block contentTLV = makeBinaryBlock(tlv_encrypted_payload, result.data(), result.size());
- contentTLV.parse();
-
- // update state
- m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
- m_challengeStatus = readString(contentTLV.get(tlv_challenge_status));
- m_remainingTries = readNonNegativeInteger(contentTLV.get(tlv_remaining_tries));
- m_freshBefore = time::system_clock::now() +
- time::seconds(readNonNegativeInteger(contentTLV.get(tlv_remaining_time)));
-
- if (contentTLV.find(tlv_issued_cert_name) != contentTLV.elements_end()) {
- Block issuedCertNameBlock = contentTLV.get(tlv_issued_cert_name);
- issuedCertNameBlock.parse();
- m_issuedCertName.wireDecode(issuedCertNameBlock.get(tlv::Name));
- }
-}
-
-shared_ptr<Interest>
-ClientModule::generateDownloadInterest()
-{
- Name interestName = m_ca.m_caPrefix;
- interestName.append("CA").append("DOWNLOAD").append(m_requestId);
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
- return interest;
-}
-
-shared_ptr<Interest>
-ClientModule::generateCertFetchInterest()
-{
- Name interestName = m_issuedCertName;
- auto interest = make_shared<Interest>(interestName);
- interest->setMustBeFresh(true);
- interest->setCanBePrefix(false);
- return interest;
-}
-
-void
-ClientModule::onCertFetchResponse(const Data& reply)
-{
- try {
- security::v2::Certificate cert(reply.getContent().blockFromValue());
- m_keyChain.addCertificate(m_key, cert);
- _LOG_TRACE("Fetched and installed the cert " << cert.getName());
- }
- catch (const std::exception& e) {
- _LOG_ERROR("Cannot add replied certificate into the keychain " << e.what());
- return;
- }
-}
-
-void
-ClientModule::endSession()
-{
- if (getApplicationStatus() == Status::SUCCESS || getApplicationStatus() == Status::ENDED) {
- return;
- }
- if (m_isNewlyCreatedIdentity) {
- // put the identity into the if scope is because it may cause an error
- // outside since when endSession is called, identity may not have been created yet.
- auto identity = m_keyChain.getPib().getIdentity(m_identityName);
- m_keyChain.deleteIdentity(identity);
- }
- else if (m_isNewlyCreatedKey) {
- auto identity = m_keyChain.getPib().getIdentity(m_identityName);
- m_keyChain.deleteKey(identity, m_key);
- }
- m_status = Status::ENDED;
-}
-
-void
-ClientModule::processIfError(const Data& data)
-{
- auto contentTLV = data.getContent();
- if (ErrorTLV::isErrorContent(contentTLV)) {
- try {
- auto error = ErrorTLV::decodefromDataContent(contentTLV);
- _LOG_ERROR("Error data returned for " << data.getName() << ": " << std::endl <<
- "Code: " << errorCodeToString(std::get<0>(error)) << std::endl <<
- "Info: " << std::get<1>(error) << std::endl);
- } catch (const std::exception& e) {
- _LOG_ERROR("Cannot parse error data content for " << data.getName());
- return;
- }
- }
-}
-
-} // namespace ndncert
-} // namespace ndn
diff --git a/src/client-module.hpp b/src/client-module.hpp
deleted file mode 100644
index b9c0f35..0000000
--- a/src/client-module.hpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2017-2020, Regents of the University of California.
- *
- * This file is part of ndncert, a certificate management system based on NDN.
- *
- * ndncert is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndncert authors and contributors.
- */
-
-#ifndef NDNCERT_CLIENT_MODULE_HPP
-#define NDNCERT_CLIENT_MODULE_HPP
-
-#include "configuration.hpp"
-#include "request-state.hpp"
-#include "crypto-support/crypto-helper.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-// TODO
-// For each CA item in Client.Conf, create a validator instance and initialize it with CA's cert
-// The validator instance should be in CaConfigItem
-
-class ClientModule : noncopyable
-{
-public:
- /**
- * @brief Error that can be thrown from ClientModule
- */
- class Error : public std::runtime_error
- {
- public:
- using std::runtime_error::runtime_error;
- };
-
-public:
- ClientModule(security::v2::KeyChain& keyChain);
-
- ~ClientModule();
-
- ClientConfig&
- getClientConf()
- {
- return m_config;
- }
-
- Status
- getApplicationStatus() const
- {
- return m_status;
- }
-
- std::string
- getChallengeStatus() const
- {
- return m_challengeStatus;
- }
-
- shared_ptr<Interest>
- generateInfoInterest(const Name& caName);
-
- bool
- verifyInfoResponse(const Data& reply);
-
- /**
- * @brief Process the replied PROBE INFO Data packet
- * Warning: this function will add a new trust anchor into the application.
- * Please invoke this function only when reply can be fully trusted or the CA
- * can be verified in later challenge phase.
- */
- void
- addCaFromInfoResponse(const Data& reply);
-
- shared_ptr<Interest>
- generateProbeInterest(const CaConfigItem& ca, std::vector<std::tuple<std::string, std::string>>&& probeInfo);
-
- void
- onProbeResponse(const Data& reply);
-
- shared_ptr<Interest>
- generateNewInterest(const time::system_clock::TimePoint& notBefore,
- const time::system_clock::TimePoint& notAfter,
- const Name& identityName = Name());
-
- shared_ptr<Interest>
- generateRevokeInterest(const security::v2::Certificate& certificate);
-
- std::list<std::string>
- onNewRenewRevokeResponse(const Data& reply);
-
- shared_ptr<Interest>
- generateChallengeInterest(const Block& paramTLV);
-
- void
- onChallengeResponse(const Data& reply);
-
- shared_ptr<Interest>
- generateDownloadInterest();
-
- shared_ptr<Interest>
- generateCertFetchInterest();
-
- void
- onCertFetchResponse(const Data& reply);
-
- void
- endSession();
-
- static void
- processIfError(const Data& data);
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
- ClientConfig m_config;
- security::v2::KeyChain& m_keyChain;
-
- CaConfigItem m_ca;
- security::Key m_key;
- Name m_identityName;
-
- std::string m_requestId = "";
- Status m_status = Status::NOT_STARTED;
- std::string m_challengeStatus = "";
- std::string m_challengeType = "";
- Name m_issuedCertName;
- std::list<std::string> m_challengeList;
- bool m_isCertInstalled = false;
- bool m_isNewlyCreatedIdentity = false;
- bool m_isNewlyCreatedKey = false;
-
- int m_remainingTries = 0;
- time::system_clock::TimePoint m_freshBefore;
-
- ECDHState m_ecdh;
- uint8_t m_aesKey[16] = {0};
-};
-
-} // namespace ndncert
-} // namespace ndn
-
-#endif // NDNCERT_CLIENT_MODULE_HPP
diff --git a/src/configuration.cpp b/src/configuration.cpp
index 8d1d397..10fe352 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -27,7 +27,7 @@
namespace ndncert {
void
-CaConfigItem::parse(const JsonSection& configJson)
+CaProfile::parse(const JsonSection& configJson)
{
// CA prefix
m_caPrefix = Name(configJson.get(CONFIG_CA_PREFIX, ""));
@@ -79,7 +79,7 @@
}
JsonSection
-CaConfigItem::toJson() const
+CaProfile::toJson() const
{
JsonSection caItem;
caItem.put(CONFIG_CA_PREFIX, m_caPrefix.toUri());
@@ -150,7 +150,7 @@
}
void
-ClientConfig::load(const std::string& fileName)
+RequesterCaCache::load(const std::string& fileName)
{
JsonSection configJson;
try {
@@ -166,12 +166,12 @@
}
void
-ClientConfig::load(const JsonSection& configSection)
+RequesterCaCache::load(const JsonSection& configSection)
{
m_caItems.clear();
auto caList = configSection.get_child("ca-list");
for (auto item : caList) {
- CaConfigItem caItem;
+ CaProfile caItem;
caItem.parse(item.second);
if (caItem.m_cert == nullptr) {
BOOST_THROW_EXCEPTION(std::runtime_error("No CA certificate is loaded from JSON configuration."));
@@ -181,7 +181,7 @@
}
void
-ClientConfig::save(const std::string& fileName) const
+RequesterCaCache::save(const std::string& fileName) const
{
JsonSection configJson;
for (const auto& caItem : m_caItems) {
@@ -196,9 +196,21 @@
}
void
-ClientConfig::removeCaItem(const Name& caName)
+RequesterCaCache::removeCaProfile(const Name& caName)
{
- m_caItems.remove_if([&](const CaConfigItem& item) { return item.m_caPrefix == caName; });
+ m_caItems.remove_if([&](const CaProfile& item) { return item.m_caPrefix == caName; });
+}
+
+void
+RequesterCaCache::addCaProfile(const CaProfile& profile)
+{
+ for (auto& item : m_caItems) {
+ if (item.m_caPrefix == profile.m_caPrefix) {
+ item = profile;
+ return;
+ }
+ }
+ m_caItems.push_back(profile);
}
} // namespace ndncert
diff --git a/src/configuration.hpp b/src/configuration.hpp
index 892c056..6b9edeb 100644
--- a/src/configuration.hpp
+++ b/src/configuration.hpp
@@ -26,7 +26,7 @@
namespace ndn {
namespace ndncert {
-struct CaConfigItem {
+struct CaProfile {
/**
* CA Name prefix (without /CA suffix).
*/
@@ -131,7 +131,7 @@
void
load(const std::string& fileName);
- CaConfigItem m_caItem;
+ CaProfile m_caItem;
/**
* Used for CA redirection as specified in
* https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3-PROBE-Extensions#probe-extension-for-redirection
@@ -153,7 +153,7 @@
* For Client configuration format, please refer to:
* https://github.com/named-data/ndncert/wiki/Client-Configuration-Sample
*/
-class ClientConfig {
+class RequesterCaCache {
public:
/**
* @throw std::runtime_error when config file cannot be correctly parsed.
@@ -171,9 +171,15 @@
save(const std::string& fileName) const;
void
- removeCaItem(const Name& caName);
+ removeCaProfile(const Name& caName);
- std::list<CaConfigItem> m_caItems;
+ /**
+ * Be cautious. This will add a new trust anchor for requesters.
+ */
+ void
+ addCaProfile(const CaProfile& profile);
+
+ std::list<CaProfile> m_caItems;
};
} // namespace ndncert
diff --git a/src/protocol-detail/challenge.cpp b/src/protocol-detail/challenge.cpp
index 61a3519..85875fd 100644
--- a/src/protocol-detail/challenge.cpp
+++ b/src/protocol-detail/challenge.cpp
@@ -19,8 +19,6 @@
*/
#include "challenge.hpp"
-#include "../ndncert-common.hpp"
-#include "../request-state.hpp"
namespace ndn {
namespace ndncert {
diff --git a/src/protocol-detail/error.cpp b/src/protocol-detail/error.cpp
index b74180b..dabfa6a 100644
--- a/src/protocol-detail/error.cpp
+++ b/src/protocol-detail/error.cpp
@@ -37,16 +37,11 @@
ErrorTLV::decodefromDataContent(const Block& block)
{
block.parse();
+ if (block.find(tlv_error_code) == block.elements_end()) {
+ return std::make_tuple(ErrorCode::NO_ERROR, "");
+ }
ErrorCode error = static_cast<ErrorCode>(readNonNegativeInteger(block.get(tlv_error_code)));
- auto description = readString(block.get(tlv_error_info));
- return std::make_tuple(error, description);
-}
-
-bool
-ErrorTLV::isErrorContent(const Block& block)
-{
- block.parse();
- return block.find(tlv_error_code) != block.elements_end();
+ return std::make_tuple(error, readString(block.get(tlv_error_info)));
}
} // namespace ndncert
diff --git a/src/protocol-detail/error.hpp b/src/protocol-detail/error.hpp
index d39fe1a..8a2cda0 100644
--- a/src/protocol-detail/error.hpp
+++ b/src/protocol-detail/error.hpp
@@ -39,9 +39,6 @@
*/
static std::tuple<ErrorCode, std::string>
decodefromDataContent(const Block& block);
-
- static bool
- isErrorContent(const Block& block);
};
} // namespace ndncert
diff --git a/src/protocol-detail/info.cpp b/src/protocol-detail/info.cpp
index 06eba0e..7c0e449 100644
--- a/src/protocol-detail/info.cpp
+++ b/src/protocol-detail/info.cpp
@@ -24,7 +24,7 @@
namespace ndncert {
Block
-INFO::encodeDataContent(const CaConfigItem& caConfig, const security::v2::Certificate& certificate)
+INFO::encodeDataContent(const CaProfile& caConfig, const security::v2::Certificate& certificate)
{
auto content = makeEmptyBlock(tlv::Content);
content.push_back(makeNestedBlock(tlv_ca_prefix, caConfig.m_caPrefix));
@@ -48,10 +48,10 @@
return content;
}
-CaConfigItem
+CaProfile
INFO::decodeDataContent(const Block& block)
{
- CaConfigItem result;
+ CaProfile result;
block.parse();
for (auto const& item : block.elements()) {
switch (item.type()) {
diff --git a/src/protocol-detail/info.hpp b/src/protocol-detail/info.hpp
index 03f0d61..571d120 100644
--- a/src/protocol-detail/info.hpp
+++ b/src/protocol-detail/info.hpp
@@ -32,12 +32,12 @@
* Encode CA configuration and its certificate into a TLV block as INFO Data packet content.
*/
static Block
- encodeDataContent(const CaConfigItem& caConfig, const security::v2::Certificate& certificate);
+ encodeDataContent(const CaProfile& caConfig, const security::v2::Certificate& certificate);
/**
* Decode CA configuration from the TLV block of INFO Data packet content.
*/
- static CaConfigItem
+ static CaProfile
decodeDataContent(const Block& block);
};
diff --git a/src/requester.cpp b/src/requester.cpp
new file mode 100644
index 0000000..7bf9594
--- /dev/null
+++ b/src/requester.cpp
@@ -0,0 +1,327 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "requester.hpp"
+#include "challenge-module.hpp"
+#include "crypto-support/enc-tlv.hpp"
+#include "protocol-detail/challenge.hpp"
+#include "protocol-detail/error.hpp"
+#include "protocol-detail/info.hpp"
+#include "protocol-detail/new-renew-revoke.hpp"
+#include "protocol-detail/probe.hpp"
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <ndn-cxx/security/transform/base64-encode.hpp>
+#include <ndn-cxx/security/transform/buffer-source.hpp>
+#include <ndn-cxx/security/transform/stream-sink.hpp>
+#include <ndn-cxx/security/verification-helpers.hpp>
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/util/random.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.client);
+
+RequesterState::RequesterState(security::v2::KeyChain& keyChain, const CaProfile& caItem, RequestType requestType)
+ : m_caItem(caItem)
+ , m_keyChain(keyChain)
+ , m_type(requestType)
+{
+}
+
+shared_ptr<Interest>
+Requester::genCaProfileInterest(const Name& caName)
+{
+ Name interestName = caName;
+ if (readString(caName.at(-1)) != "CA")
+ interestName.append("CA");
+ interestName.append("INFO");
+ auto interest = make_shared<Interest>(interestName);
+ interest->setMustBeFresh(true);
+ interest->setCanBePrefix(false);
+ return interest;
+}
+
+boost::optional<CaProfile>
+Requester::onCaProfileResponse(const Data& reply)
+{
+ auto caItem = INFO::decodeDataContent(reply.getContent());
+ if (!security::verifySignature(reply, *caItem.m_cert)) {
+ _LOG_ERROR("Cannot verify replied Data packet signature.");
+ return boost::none;
+ }
+ return caItem;
+}
+
+shared_ptr<Interest>
+Requester::genProbeInterest(const CaProfile& ca, std::vector<std::tuple<std::string, std::string>>&& probeInfo)
+{
+ Name interestName = ca.m_caPrefix;
+ interestName.append("CA").append("PROBE");
+ auto interest = make_shared<Interest>(interestName);
+ interest->setMustBeFresh(true);
+ interest->setCanBePrefix(false);
+ interest->setApplicationParameters(PROBE::encodeApplicationParameters(std::move(probeInfo)));
+ return interest;
+}
+
+void
+Requester::onProbeResponse(const Data& reply, const CaProfile& ca,
+ std::vector<Name>& identityNames, std::vector<Name>& otherCas)
+{
+ if (!security::verifySignature(reply, *ca.m_cert)) {
+ _LOG_ERROR("Cannot verify replied Data packet signature.");
+ return;
+ }
+ processIfError(reply);
+ PROBE::decodeDataContent(reply.getContent(), identityNames, otherCas);
+}
+
+shared_ptr<Interest>
+Requester::genNewInterest(RequesterState& state, const Name& identityName,
+ const time::system_clock::TimePoint& notBefore,
+ const time::system_clock::TimePoint& notAfter)
+{
+ if (!state.m_caItem.m_caPrefix.isPrefixOf(identityName)) {
+ return nullptr;
+ }
+ if (identityName.empty()) {
+ NDN_LOG_TRACE("Randomly create a new name because identityName is empty and the param is empty.");
+ state.m_identityName = state.m_caItem.m_caPrefix;
+ state.m_identityName.append(std::to_string(random::generateSecureWord64()));
+ }
+ else {
+ state.m_identityName = identityName;
+ }
+
+ // generate a newly key pair or use an existing key
+ const auto& pib = state.m_keyChain.getPib();
+ security::pib::Identity identity;
+ try {
+ identity = pib.getIdentity(state.m_identityName);
+ }
+ catch (const security::Pib::Error& e) {
+ identity = state.m_keyChain.createIdentity(state.m_identityName);
+ state.m_isNewlyCreatedIdentity = true;
+ state.m_isNewlyCreatedKey = true;
+ }
+ try {
+ state.m_keyPair = identity.getDefaultKey();
+ }
+ catch (const security::Pib::Error& e) {
+ state.m_keyPair = state.m_keyChain.createKey(identity);
+ state.m_isNewlyCreatedKey = true;
+ }
+ auto& keyName = state.m_keyPair.getName();
+
+ // generate certificate request
+ security::v2::Certificate certRequest;
+ certRequest.setName(Name(keyName).append("cert-request").appendVersion());
+ certRequest.setContentType(tlv::ContentType_Key);
+ certRequest.setContent(state.m_keyPair.getPublicKey().data(), state.m_keyPair.getPublicKey().size());
+ SignatureInfo signatureInfo;
+ signatureInfo.setValidityPeriod(security::ValidityPeriod(notBefore, notAfter));
+ state.m_keyChain.sign(certRequest, signingByKey(keyName).setSignatureInfo(signatureInfo));
+
+ // generate Interest packet
+ Name interestName = state.m_caItem.m_caPrefix;
+ interestName.append("CA").append("NEW");
+ auto interest = make_shared<Interest>(interestName);
+ interest->setMustBeFresh(true);
+ interest->setCanBePrefix(false);
+ interest->setApplicationParameters(
+ NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::NEW, state.m_ecdh.getBase64PubKey(), certRequest));
+
+ // sign the Interest packet
+ state.m_keyChain.sign(*interest, signingByKey(keyName));
+ return interest;
+}
+
+shared_ptr<Interest>
+Requester::genRevokeInterest(RequesterState& state, const security::v2::Certificate& certificate)
+{
+ if (!state.m_caItem.m_caPrefix.isPrefixOf(certificate.getName())) {
+ return nullptr;
+ }
+ // generate Interest packet
+ Name interestName = state.m_caItem.m_caPrefix;
+ interestName.append("CA").append("REVOKE");
+ auto interest = make_shared<Interest>(interestName);
+ interest->setMustBeFresh(true);
+ interest->setCanBePrefix(false);
+ interest->setApplicationParameters(
+ NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::REVOKE, state.m_ecdh.getBase64PubKey(), certificate));
+ return interest;
+}
+
+std::list<std::string>
+Requester::onNewRenewRevokeResponse(RequesterState& state, const Data& reply)
+{
+ if (!security::verifySignature(reply, *state.m_caItem.m_cert)) {
+ _LOG_ERROR("Cannot verify replied Data packet signature.");
+ return std::list<std::string>();
+ }
+ processIfError(reply);
+
+ auto contentTLV = reply.getContent();
+ contentTLV.parse();
+
+ // ECDH
+ const auto& peerKeyBase64Str = readString(contentTLV.get(tlv_ecdh_pub));
+ const auto& saltStr = readString(contentTLV.get(tlv_salt));
+ uint64_t saltInt = std::stoull(saltStr);
+ state.m_ecdh.deriveSecret(peerKeyBase64Str);
+
+ // HKDF
+ hkdf(state.m_ecdh.context->sharedSecret, state.m_ecdh.context->sharedSecretLen,
+ (uint8_t*)&saltInt, sizeof(saltInt), state.m_aesKey, sizeof(state.m_aesKey));
+
+ // update state
+ state.m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
+ state.m_requestId = readString(contentTLV.get(tlv_request_id));
+ std::list<std::string> challengeList;
+ for (auto const& element : contentTLV.elements()) {
+ if (element.type() == tlv_challenge) {
+ challengeList.push_back(readString(element));
+ }
+ }
+ return challengeList;
+}
+
+std::vector<std::tuple<std::string, std::string>>
+Requester::selectOrContinueChallenge(RequesterState& state, const std::string& challengeSelected)
+{
+ auto challenge = ChallengeModule::createChallengeModule(challengeSelected);
+ if (challenge == nullptr) {
+ BOOST_THROW_EXCEPTION(std::runtime_error("The challenge selected is not supported by your current version of NDNCERT."));
+ }
+ state.m_challengeType = challengeSelected;
+ return challenge->getRequestedParameterList(state.m_status, state.m_challengeStatus);
+}
+
+shared_ptr<Interest>
+Requester::genChallengeInterest(const RequesterState& state,
+ std::vector<std::tuple<std::string, std::string>>&& parameters)
+{
+ if (state.m_challengeType == "") {
+ BOOST_THROW_EXCEPTION(std::runtime_error("The challenge has not been selected."));
+ }
+ auto challenge = ChallengeModule::createChallengeModule(state.m_challengeType);
+ if (challenge == nullptr) {
+ BOOST_THROW_EXCEPTION(std::runtime_error("The challenge selected is not supported by your current version of NDNCERT."));
+ }
+ auto challengeParams = challenge->genChallengeRequestTLV(state.m_status, state.m_challengeStatus, std::move(parameters));
+
+ Name interestName = state.m_caItem.m_caPrefix;
+ interestName.append("CA").append("CHALLENGE").append(state.m_requestId);
+ auto interest = make_shared<Interest>(interestName);
+ interest->setMustBeFresh(true);
+ interest->setCanBePrefix(false);
+
+ // encrypt the Interest parameters
+ auto paramBlock = encodeBlockWithAesGcm128(tlv::ApplicationParameters, state.m_aesKey,
+ challengeParams.value(), challengeParams.value_size(),
+ (const uint8_t*)"test", strlen("test"));
+ interest->setApplicationParameters(paramBlock);
+ state.m_keyChain.sign(*interest, signingByKey(state.m_keyPair.getName()));
+ return interest;
+}
+
+void
+Requester::onChallengeResponse(RequesterState& state, const Data& reply)
+{
+ if (!security::verifySignature(reply, *state.m_caItem.m_cert)) {
+ _LOG_ERROR("Cannot verify replied Data packet signature.");
+ return;
+ }
+ processIfError(reply);
+ auto result = decodeBlockWithAesGcm128(reply.getContent(), state.m_aesKey, (const uint8_t*)"test", strlen("test"));
+ Block contentTLV = makeBinaryBlock(tlv_encrypted_payload, result.data(), result.size());
+ contentTLV.parse();
+
+ // update state
+ state.m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
+ state.m_challengeStatus = readString(contentTLV.get(tlv_challenge_status));
+ state.m_remainingTries = readNonNegativeInteger(contentTLV.get(tlv_remaining_tries));
+ state.m_freshBefore = time::system_clock::now() +
+ time::seconds(readNonNegativeInteger(contentTLV.get(tlv_remaining_time)));
+
+ if (contentTLV.find(tlv_issued_cert_name) != contentTLV.elements_end()) {
+ Block issuedCertNameBlock = contentTLV.get(tlv_issued_cert_name);
+ issuedCertNameBlock.parse();
+ state.m_issuedCertName.wireDecode(issuedCertNameBlock.get(tlv::Name));
+ }
+}
+
+shared_ptr<Interest>
+Requester::genCertFetchInterest(const RequesterState& state)
+{
+ Name interestName = state.m_issuedCertName;
+ auto interest = make_shared<Interest>(interestName);
+ interest->setMustBeFresh(false);
+ interest->setCanBePrefix(false);
+ return interest;
+}
+
+shared_ptr<security::v2::Certificate>
+Requester::onCertFetchResponse(const Data& reply)
+{
+ try {
+ return std::make_shared<security::v2::Certificate>(reply.getContent().blockFromValue());
+ }
+ catch (const std::exception& e) {
+ _LOG_ERROR("Cannot parse replied certificate ");
+ return nullptr;
+ }
+}
+
+void
+Requester::endSession(RequesterState& state)
+{
+ if (state.m_status == Status::SUCCESS || state.m_status == Status::ENDED) {
+ return;
+ }
+ if (state.m_isNewlyCreatedIdentity) {
+ // put the identity into the if scope is because it may cause an error
+ // outside since when endSession is called, identity may not have been created yet.
+ auto identity = state.m_keyChain.getPib().getIdentity(state.m_identityName);
+ state.m_keyChain.deleteIdentity(identity);
+ }
+ else if (state.m_isNewlyCreatedKey) {
+ auto identity = state.m_keyChain.getPib().getIdentity(state.m_identityName);
+ state.m_keyChain.deleteKey(identity, state.m_keyPair);
+ }
+ state.m_status = Status::ENDED;
+}
+
+void
+Requester::processIfError(const Data& data)
+{
+ auto errorInfo = ErrorTLV::decodefromDataContent(data.getContent());
+ if (std::get<0>(errorInfo) == ErrorCode::NO_ERROR) {
+ return;
+ }
+ BOOST_THROW_EXCEPTION(std::runtime_error("Error info replied from the CA with Error code: " +
+ errorCodeToString(std::get<0>(errorInfo)) +
+ " and Error Info: " + std::get<1>(errorInfo)));
+}
+
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/requester.hpp b/src/requester.hpp
new file mode 100644
index 0000000..44dd7ee
--- /dev/null
+++ b/src/requester.hpp
@@ -0,0 +1,123 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_CLIENT_MODULE_HPP
+#define NDNCERT_CLIENT_MODULE_HPP
+
+#include "configuration.hpp"
+#include "request-state.hpp"
+#include "crypto-support/crypto-helper.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+// TODO
+// For each RequesterState, create a validator instance and initialize it with CA's cert
+// The validator instance should be in CaProfile
+
+struct RequesterState {
+ explicit
+ RequesterState(security::v2::KeyChain& keyChain, const CaProfile& caItem, RequestType requestType);
+
+ CaProfile m_caItem;
+ security::v2::KeyChain& m_keyChain;
+ RequestType m_type;
+
+ Name m_identityName;
+ security::Key m_keyPair;
+ std::string m_requestId;
+ Status m_status = Status::NOT_STARTED;
+ std::string m_challengeType;
+ std::string m_challengeStatus;
+ int m_remainingTries = 0;
+ time::system_clock::TimePoint m_freshBefore;
+ Name m_issuedCertName;
+
+ ECDHState m_ecdh;
+ uint8_t m_aesKey[16] = {0};
+
+ bool m_isCertInstalled = false;
+ bool m_isNewlyCreatedIdentity = false;
+ bool m_isNewlyCreatedKey = false;
+};
+
+class Requester : noncopyable
+{
+public:
+ // INFO related helpers
+ static shared_ptr<Interest>
+ genCaProfileInterest(const Name& caName);
+
+ /**
+ * Will first verify the signature of the packet using the key provided inside the profile.
+ * The application should be cautious whether to add CaProfile into the RequesterCaCache.
+ */
+ static boost::optional<CaProfile>
+ onCaProfileResponse(const Data& reply);
+
+ // PROBE related helpers
+ static shared_ptr<Interest>
+ genProbeInterest(const CaProfile& ca, std::vector<std::tuple<std::string, std::string>>&& probeInfo);
+
+ static void
+ onProbeResponse(const Data& reply, const CaProfile& ca,
+ std::vector<Name>& identityNames, std::vector<Name>& otherCas);
+
+ // NEW/REVOKE/RENEW related helpers
+ static shared_ptr<Interest>
+ genNewInterest(RequesterState& state, const Name& identityName,
+ const time::system_clock::TimePoint& notBefore,
+ const time::system_clock::TimePoint& notAfter);
+
+ static shared_ptr<Interest>
+ genRevokeInterest(RequesterState& state, const security::v2::Certificate& certificate);
+
+ static std::list<std::string>
+ onNewRenewRevokeResponse(RequesterState& state, const Data& reply);
+
+ // CHALLENGE helpers
+ static std::vector<std::tuple<std::string, std::string>>
+ selectOrContinueChallenge(RequesterState& state, const std::string& challengeSelected);
+
+ static shared_ptr<Interest>
+ genChallengeInterest(const RequesterState& state,
+ std::vector<std::tuple<std::string, std::string>>&& parameters);
+
+ static void
+ onChallengeResponse(RequesterState& state, const Data& reply);
+
+ static shared_ptr<Interest>
+ genCertFetchInterest(const RequesterState& state);
+
+ shared_ptr<security::v2::Certificate>
+ onCertFetchResponse(const Data& reply);
+
+ void
+ endSession(RequesterState& state);
+
+private:
+ static void
+ processIfError(const Data& data);
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CLIENT_MODULE_HPP
diff --git a/tests/unit-tests/bench.t.cpp b/tests/unit-tests/bench.t.cpp
index da7e699..28850a8 100644
--- a/tests/unit-tests/bench.t.cpp
+++ b/tests/unit-tests/bench.t.cpp
@@ -19,7 +19,7 @@
*/
#include "ca-module.hpp"
-#include "client-module.hpp"
+#include "requester.hpp"
#include "challenge-modules/challenge-pin.hpp"
#include "protocol-detail/info.hpp"
#include "test-common.hpp"
@@ -90,18 +90,17 @@
advanceClocks(time::milliseconds(20), 60);
// generate NEW Interest
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
- auto newInterest = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(1), Name("/ndn/alice"));
+ RequesterState state(m_keyChain, item, RequestType::NEW);
+ auto newInterest = Requester::genNewInterest(state, Name("/ndn/alice"),
+ time::system_clock::now(),
+ time::system_clock::now() + time::days(1));
std::cout << "New Interest Size: " << newInterest->wireEncode().size() << std::endl;
// generate CHALLENGE Interest
- ChallengePin pinChallenge;
shared_ptr<Interest> challengeInterest = nullptr;
shared_ptr<Interest> challengeInterest2 = nullptr;
shared_ptr<Interest> challengeInterest3 = nullptr;
@@ -110,49 +109,41 @@
face.onSendData.connect([&](const Data& response) {
if (Name("/ndn/CA/NEW").isPrefixOf(response.getName())) {
std::cout << "NEW Data Size: " << response.wireEncode().size() << std::endl;
- client.onNewRenewRevokeResponse(response);
- auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
- challengeInterest = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
- client.m_challengeStatus,
- std::move(paramList)));
+ auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
+ auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+ challengeInterest = Requester::genChallengeInterest(state, std::move(paramList));
}
else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 0) {
count++;
BOOST_CHECK(security::verifySignature(response, cert));
- client.onChallengeResponse(response);
- BOOST_CHECK(client.m_status == Status::CHALLENGE);
- BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::NEED_CODE);
-
- auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
- challengeInterest2 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
- client.m_challengeStatus,
- std::move(paramList)));
+ Requester::onChallengeResponse(state, response);
+ BOOST_CHECK(state.m_status == Status::CHALLENGE);
+ BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::NEED_CODE);
+ auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+ challengeInterest2 = Requester::genChallengeInterest(state, std::move(paramList));
}
else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 1) {
count++;
BOOST_CHECK(security::verifySignature(response, cert));
- client.onChallengeResponse(response);
- BOOST_CHECK(client.m_status == Status::CHALLENGE);
- BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::WRONG_CODE);
+ Requester::onChallengeResponse(state, response);
+ BOOST_CHECK(state.m_status == Status::CHALLENGE);
+ BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::WRONG_CODE);
- auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
+ auto paramList = Requester::selectOrContinueChallenge(state, "pin");
auto request = ca.getCertificateRequest(*challengeInterest2);
auto secret = request.m_challengeState->m_secrets.get(ChallengePin::PARAMETER_KEY_CODE, "");
std::get<1>(paramList[0]) = secret;
- challengeInterest3 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
- client.m_challengeStatus,
- std::move(paramList)));
+ challengeInterest3 = Requester::genChallengeInterest(state, std::move(paramList));
std::cout << "CHALLENGE Interest Size: " << challengeInterest3->wireEncode().size() << std::endl;
}
else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 2) {
std::cout << "CHALLENGE Data Size: " << response.wireEncode().size() << std::endl;
count++;
BOOST_CHECK(security::verifySignature(response, cert));
-
- client.onChallengeResponse(response);
- BOOST_CHECK(client.m_status == Status::SUCCESS);
+ Requester::onChallengeResponse(state, response);
+ BOOST_CHECK(state.m_status == Status::SUCCESS);
}
});
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index d6a2980..ff139c6 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -22,8 +22,8 @@
#include "challenge-module.hpp"
#include "challenge-modules/challenge-email.hpp"
#include "challenge-modules/challenge-pin.hpp"
-#include "client-module.hpp"
#include "protocol-detail/info.hpp"
+#include "requester.hpp"
#include "test-common.hpp"
namespace ndn {
@@ -171,15 +171,13 @@
CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1", "ca-storage-memory");
advanceClocks(time::milliseconds(20), 60);
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
-
- auto interest = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(1),
- Name("/ndn/zhiyi"));
+ RequesterState state(m_keyChain, item, RequestType::NEW);
+ auto interest = Requester::genNewInterest(state, Name("/ndn/zhiyi"),
+ time::system_clock::now(),
+ time::system_clock::now() + time::days(1));
int count = 0;
face.onSendData.connect([&](const Data& response) {
@@ -201,9 +199,9 @@
BOOST_CHECK(challengeBlockCount != 0);
- client.onNewRenewRevokeResponse(response);
+ auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
auto ca_encryption_key = ca.getCaStorage()->getRequest(readString(contentBlock.get(tlv_request_id))).m_encryptionKey;
- BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + sizeof(client.m_aesKey),
+ BOOST_CHECK_EQUAL_COLLECTIONS(state.m_aesKey, state.m_aesKey + sizeof(state.m_aesKey),
ca_encryption_key.value(), ca_encryption_key.value() + ca_encryption_key.value_size());
});
face.receive(*interest);
@@ -222,19 +220,14 @@
CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1");
advanceClocks(time::milliseconds(20), 60);
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
+ RequesterState state(m_keyChain, item, RequestType::NEW);
auto current_tp = time::system_clock::now();
- auto interest1 = client.generateNewInterest(current_tp, current_tp - time::hours(1),
- Name("/ndn/zhiyi"));
- auto interest2 = client.generateNewInterest(current_tp, current_tp + time::days(361),
- Name("/ndn/zhiyi"));
- auto interest3 = client.generateNewInterest(current_tp - time::hours(1),
- current_tp + time::hours(2),
- Name("/ndn/zhiyi"));
+ auto interest1 = Requester::genNewInterest(state, Name("/ndn/zhiyi"), current_tp, current_tp - time::hours(1));
+ auto interest2 = Requester::genNewInterest(state, Name("/ndn/zhiyi"), current_tp, current_tp + time::days(361));
+ auto interest3 = Requester::genNewInterest(state, Name("/ndn/zhiyi"), current_tp - time::hours(1), current_tp + time::hours(2));
face.onSendData.connect([&](const Data& response) {
auto contentTlv = response.getContent();
contentTlv.parse();
@@ -258,21 +251,17 @@
CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1", "ca-storage-memory");
advanceClocks(time::milliseconds(20), 60);
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
+ RequesterState state(m_keyChain, item, RequestType::NEW);
- auto interest1 = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(1),
- Name("/ndn/a"));
- auto interest2 = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(1),
- Name("/ndn/a/b"));
- auto interest3 = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(1),
- Name("/ndn/a/b/c/d"));
+ auto interest1 = Requester::genNewInterest(state, Name("/ndn/a"), time::system_clock::now(),
+ time::system_clock::now() + time::days(1));
+ auto interest2 = Requester::genNewInterest(state, Name("/ndn/a/b"), time::system_clock::now(),
+ time::system_clock::now() + time::days(1));
+ auto interest3 = Requester::genNewInterest(state, Name("/ndn/a/b/c/d"), time::system_clock::now(),
+ time::system_clock::now() + time::days(1));
face.onSendData.connect([&](const Data& response) {
auto contentTlv = response.getContent();
@@ -303,14 +292,14 @@
CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1");
advanceClocks(time::milliseconds(20), 60);
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
+ RequesterState state(m_keyChain, item, RequestType::NEW);
+
auto current_tp = time::system_clock::now();
- auto interest1 = client.generateNewInterest(current_tp, current_tp + time::days(1), Name("/ndn"));
- auto interest2 = client.generateNewInterest(current_tp, current_tp + time::days(1), Name("/ndn/a/b/c/d"));
+ auto interest1 = Requester::genNewInterest(state, Name("/ndn"), current_tp, current_tp + time::days(1));
+ auto interest2 = Requester::genNewInterest(state, Name("/ndn/a/b/c/d"), current_tp, current_tp + time::days(1));
face.onSendData.connect([&](const Data& response) {
auto contentTlv = response.getContent();
contentTlv.parse();
@@ -334,16 +323,15 @@
advanceClocks(time::milliseconds(20), 60);
// generate NEW Interest
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
- auto newInterest = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(1), Name("/ndn/zhiyi"));
+ RequesterState state(m_keyChain, item, RequestType::NEW);
+
+ auto newInterest = Requester::genNewInterest(state, Name("/ndn/zhiyi"), time::system_clock::now(),
+ time::system_clock::now() + time::days(1));
// generate CHALLENGE Interest
- ChallengePin pinChallenge;
shared_ptr<Interest> challengeInterest = nullptr;
shared_ptr<Interest> challengeInterest2 = nullptr;
shared_ptr<Interest> challengeInterest3 = nullptr;
@@ -351,47 +339,40 @@
int count = 0;
face.onSendData.connect([&](const Data& response) {
if (Name("/ndn/CA/NEW").isPrefixOf(response.getName())) {
- client.onNewRenewRevokeResponse(response);
- auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
- challengeInterest = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
- client.m_challengeStatus,
- std::move(paramList)));
+ auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
+ auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+ challengeInterest = Requester::genChallengeInterest(state, std::move(paramList));
}
else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 0) {
count++;
BOOST_CHECK(security::verifySignature(response, cert));
- client.onChallengeResponse(response);
- BOOST_CHECK(client.m_status == Status::CHALLENGE);
- BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::NEED_CODE);
-
- auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
- challengeInterest2 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
- client.m_challengeStatus,
- std::move(paramList)));
+ Requester::onChallengeResponse(state, response);
+ BOOST_CHECK(state.m_status == Status::CHALLENGE);
+ BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::NEED_CODE);
+ auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+ challengeInterest2 = Requester::genChallengeInterest(state, std::move(paramList));
}
else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 1) {
count++;
BOOST_CHECK(security::verifySignature(response, cert));
- client.onChallengeResponse(response);
- BOOST_CHECK(client.m_status == Status::CHALLENGE);
- BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::WRONG_CODE);
+ Requester::onChallengeResponse(state, response);
+ BOOST_CHECK(state.m_status == Status::CHALLENGE);
+ BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::WRONG_CODE);
- auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
+ auto paramList = Requester::selectOrContinueChallenge(state, "pin");
auto request = ca.getCertificateRequest(*challengeInterest2);
auto secret = request.m_challengeState->m_secrets.get(ChallengePin::PARAMETER_KEY_CODE, "");
std::get<1>(paramList[0]) = secret;
- challengeInterest3 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
- client.m_challengeStatus,
- std::move(paramList)));
+ challengeInterest3 = Requester::genChallengeInterest(state, std::move(paramList));
+ std::cout << "CHALLENGE Interest Size: " << challengeInterest3->wireEncode().size() << std::endl;
}
else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 2) {
count++;
BOOST_CHECK(security::verifySignature(response, cert));
-
- client.onChallengeResponse(response);
- BOOST_CHECK(client.m_status == Status::SUCCESS);
+ Requester::onChallengeResponse(state, response);
+ BOOST_CHECK(state.m_status == Status::SUCCESS);
}
});
@@ -431,13 +412,12 @@
RequestState certRequest(Name("/ndn"), "122", RequestType::NEW, Status::SUCCESS, clientCert, makeEmptyBlock(tlv::ContentType_Key));
auto issuedCert = ca.issueCertificate(certRequest);
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
+ RequesterState state(m_keyChain, item, RequestType::REVOKE);
- auto interest = client.generateRevokeInterest(issuedCert);
+ auto interest = Requester::genRevokeInterest(state, issuedCert);
int count = 0;
face.onSendData.connect([&](const Data& response) {
@@ -459,9 +439,9 @@
BOOST_CHECK(challengeBlockCount != 0);
- client.onNewRenewRevokeResponse(response);
+ auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
auto ca_encryption_key = ca.getCaStorage()->getRequest(readString(contentBlock.get(tlv_request_id))).m_encryptionKey;
- BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + sizeof(client.m_aesKey),
+ BOOST_CHECK_EQUAL_COLLECTIONS(state.m_aesKey, state.m_aesKey + sizeof(state.m_aesKey),
ca_encryption_key.value(), ca_encryption_key.value() + ca_encryption_key.value_size());
});
face.receive(*interest);
@@ -493,13 +473,12 @@
time::system_clock::now() + time::hours(10)));
m_keyChain.sign(clientCert, signingByKey(clientKey.getName()).setSignatureInfo(signatureInfo));
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/ndn");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
+ RequesterState state(m_keyChain, item, RequestType::NEW);
- auto interest = client.generateRevokeInterest(clientCert);
+ auto interest = Requester::genRevokeInterest(state, clientCert);
bool receiveData = false;
face.onSendData.connect([&](const Data& response) {
diff --git a/tests/unit-tests/client-module.t.cpp b/tests/unit-tests/client-module.t.cpp
index eb4cd45..79baee2 100644
--- a/tests/unit-tests/client-module.t.cpp
+++ b/tests/unit-tests/client-module.t.cpp
@@ -19,7 +19,7 @@
*/
#include <protocol-detail/error.hpp>
-#include "client-module.hpp"
+#include "requester.hpp"
#include "challenge-module.hpp"
#include "ca-module.hpp"
#include "test-common.hpp"
@@ -28,43 +28,7 @@
namespace ndncert {
namespace tests {
-BOOST_FIXTURE_TEST_SUITE(TestClientModule, IdentityManagementTimeFixture)
-
-BOOST_AUTO_TEST_CASE(ClientModuleInitialize)
-{
- ClientModule client(m_keyChain);
- client.getClientConf().load("tests/unit-tests/config-files/config-client-1");
- BOOST_CHECK_EQUAL(client.getClientConf().m_caItems.size(), 2);
-}
-
-BOOST_AUTO_TEST_CASE(Probe)
-{
- ClientModule client(m_keyChain);
- client.getClientConf().load("tests/unit-tests/config-files/config-client-1");
-
- auto identity = addIdentity(Name("/site"));
- auto key = identity.getDefaultKey();
- auto cert = key.getDefaultCertificate();
-
- CaConfigItem item;
- item.m_probeParameterKeys.push_back("email");
- item.m_probeParameterKeys.push_back("uid");
- item.m_probeParameterKeys.push_back("name");
- item.m_caPrefix = Name("/site");
- item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
-
- std::vector<std::tuple<std::string, std::string>> probeParams;
- probeParams.push_back(std::make_tuple("email", "zhiyi@cs.ucla.edu"));
- probeParams.push_back(std::make_tuple("uid", "987654321"));
- probeParams.push_back(std::make_tuple("name", "Zhiyi Zhang"));
- auto firstInterest = client.generateProbeInterest(item, std::move(probeParams));
- BOOST_CHECK(firstInterest->getName().at(-1).isParametersSha256Digest());
- // ignore the last name component (ParametersSha256Digest)
- BOOST_CHECK_EQUAL(firstInterest->getName().getPrefix(-1), "/site/CA/PROBE");
- BOOST_CHECK_EQUAL(readString(firstInterest->getApplicationParameters().get(tlv_parameter_value)),
- "zhiyi@cs.ucla.edu");
-}
+BOOST_FIXTURE_TEST_SUITE(TestRequester, IdentityManagementTimeFixture)
BOOST_AUTO_TEST_CASE(ErrorHandling)
{
@@ -72,47 +36,24 @@
auto key = identity.getDefaultKey();
auto cert = key.getDefaultCertificate();
- ClientModule client(m_keyChain);
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/site");
item.m_cert = std::make_shared<security::v2::Certificate>(cert);
- client.getClientConf().m_caItems.push_back(item);
-
- client.generateProbeInterest(item,std::vector<std::tuple<std::string, std::string>>());
+ RequesterState state(m_keyChain, item, RequestType::NEW);
Data errorPacket;
errorPacket.setName(Name("/site/pretend/this/is/error/packet"));
errorPacket.setFreshnessPeriod(time::seconds(100));
- errorPacket.setContent(ErrorTLV::encodeDataContent(ErrorCode::NO_ERROR, "This is a test."));
+ errorPacket.setContent(ErrorTLV::encodeDataContent(ErrorCode::INVALID_PARAMETER, "This is a test."));
m_keyChain.sign(errorPacket, signingByIdentity(identity));
- BOOST_CHECK_THROW(client.onProbeResponse(errorPacket), std::exception);
- BOOST_CHECK_THROW(client.onNewRenewRevokeResponse(errorPacket), std::exception);
- BOOST_CHECK_THROW(client.onChallengeResponse(errorPacket), std::exception);
+ std::vector<Name> ids, cas;
+ BOOST_CHECK_THROW(Requester::onProbeResponse(errorPacket, item, ids, cas), std::runtime_error);
+ BOOST_CHECK_THROW(Requester::onNewRenewRevokeResponse(state, errorPacket), std::runtime_error);
+ BOOST_CHECK_THROW(Requester::onChallengeResponse(state, errorPacket), std::runtime_error);
}
-// BOOST_AUTO_TEST_CASE(GenProbeRequestJson)
-// {
-// ClientModule client(m_keyChain);
-// client.getClientConf().load("tests/unit-tests/config-files/config-client-1");
-
-// auto identity = addIdentity(Name("/site"));
-// auto key = identity.getDefaultKey();
-// auto cert = key.getDefaultCertificate();
-
-// CaConfigItem item;
-// item.m_probe = "email:uid:name";
-// item.m_caPrefix = Name("/site");
-// item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-// client.getClientConf().m_caItems.push_back(item);
-
-// auto interestPacket = client.genProbeRequestJson(item, "yufeng@ucla.edu:123456789:Yufeng Zhang");
-// BOOST_CHECK_EQUAL(interestPacket.get("email", ""), "yufeng@ucla.edu");
-// BOOST_CHECK_EQUAL(interestPacket.get("uid", ""), "123456789");
-// BOOST_CHECK_EQUAL(interestPacket.get("name", ""), "Yufeng Zhang");
-// }
-
-BOOST_AUTO_TEST_SUITE_END() // TestClientModule
+BOOST_AUTO_TEST_SUITE_END() // TestRequester
} // namespace tests
} // namespace ndncert
diff --git a/tests/unit-tests/configuration.t.cpp b/tests/unit-tests/configuration.t.cpp
index ec47f9d..d7cb506 100644
--- a/tests/unit-tests/configuration.t.cpp
+++ b/tests/unit-tests/configuration.t.cpp
@@ -67,9 +67,9 @@
BOOST_CHECK_THROW(config.load("tests/unit-tests/config-files/config-ca-4"), std::runtime_error);
}
-BOOST_AUTO_TEST_CASE(ClientConfigFile)
+BOOST_AUTO_TEST_CASE(RequesterCaCacheFile)
{
- ClientConfig config;
+ RequesterCaCache config;
config.load("tests/unit-tests/config-files/config-client-1");
BOOST_CHECK_EQUAL(config.m_caItems.size(), 2);
@@ -93,9 +93,9 @@
"/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5");
}
-BOOST_AUTO_TEST_CASE(ClientConfigFileWithErrors)
+BOOST_AUTO_TEST_CASE(RequesterCaCacheFileWithErrors)
{
- ClientConfig config;
+ RequesterCaCache config;
// nonexistent file
BOOST_CHECK_THROW(config.load("tests/unit-tests/config-files/Nonexist"), std::runtime_error);
// missing certificate
@@ -104,12 +104,12 @@
BOOST_CHECK_THROW(config.load("tests/unit-tests/config-files/config-client-3"), std::runtime_error);
}
-BOOST_AUTO_TEST_CASE(ClientConfigFileAddAndRemoveCaItem)
+BOOST_AUTO_TEST_CASE(RequesterCaCacheFileAddAndremoveCaProfile)
{
- ClientConfig config;
+ RequesterCaCache config;
config.load("tests/unit-tests/config-files/config-client-1");
- CaConfigItem item;
+ CaProfile item;
item.m_caPrefix = Name("/test");
item.m_caInfo = "test";
@@ -118,7 +118,7 @@
auto lastItem = config.m_caItems.back();
BOOST_CHECK_EQUAL(lastItem.m_caPrefix, "/test");
- config.removeCaItem(Name("/test"));
+ config.removeCaProfile(Name("/test"));
BOOST_CHECK_EQUAL(config.m_caItems.size(), 2);
lastItem = config.m_caItems.back();
BOOST_CHECK_EQUAL(lastItem.m_caPrefix, "/ndn/edu/ucla/zhiyi");
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index e81d792..5fbea9b 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -19,7 +19,7 @@
*/
#include "challenge-module.hpp"
-#include "client-module.hpp"
+#include "requester.hpp"
#include "protocol-detail/info.hpp"
#include <ndn-cxx/security/verification-helpers.hpp>
#include <boost/asio.hpp>
@@ -40,7 +40,7 @@
security::v2::KeyChain keyChain;
std::string challengeType;
int validityPeriod = -1;
-ClientModule client(keyChain);
+Requester client(keyChain);
static void
captureParams(std::vector<std::tuple<std::string, std::string>>& requirement)
@@ -257,7 +257,7 @@
<< "Introduction: " << item.m_caInfo << "\n"
<< "***************************************\n";
}
- std::vector<CaConfigItem> caVector{std::begin(caList), std::end(caList)};
+ std::vector<CaProfile> caVector{std::begin(caList), std::end(caList)};
std::cerr << "Step "
<< 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";