Update the NDNCERT library to version NDNCERT v2
spec:[https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-new]
Change-Id: Ia480a8e70c4b38ca170dfe2fcf50d1265ab65f46
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index a6954a8..7a575a9 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2017-2018, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -21,6 +21,7 @@
#include "ca-module.hpp"
#include "challenge-module.hpp"
#include "logging.hpp"
+#include "crypto-support/enc-tlv.hpp"
#include <ndn-cxx/util/io.hpp>
#include <ndn-cxx/security/verification-helpers.hpp>
#include <ndn-cxx/security/signing-helpers.hpp>
@@ -29,6 +30,8 @@
namespace ndn {
namespace ndncert {
+static const int IS_SUBNAME_MIN_OFFSET = 5;
+
_LOG_INIT(ndncert.ca);
CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
@@ -45,525 +48,336 @@
CaModule::~CaModule()
{
- for (auto prefixId : m_interestFilterIds) {
- m_face.unsetInterestFilter(prefixId);
+ for (auto handle : m_interestFilterHandles) {
+ handle.cancel();
}
- for (auto prefixId : m_registeredPrefixIds) {
- m_face.unregisterPrefix(prefixId, nullptr, nullptr);
+ for (auto handle : m_registeredPrefixHandles) {
+ handle.unregister();
}
}
void
CaModule::registerPrefix()
{
- // register localhost list prefix
- Name localProbePrefix("/localhost/CA/_LIST");
- auto prefixId = m_face.setInterestFilter(InterestFilter(localProbePrefix),
- bind(&CaModule::handleLocalhostList, this, _2),
+ // register localhop discovery prefix
+ Name localhopProbePrefix("/localhop/CA/PROBE/INFO");
+ auto prefixId = m_face.setInterestFilter(InterestFilter(localhopProbePrefix),
+ bind(&CaModule::onProbe, this, _2),
bind(&CaModule::onRegisterFailed, this, _2));
- m_registeredPrefixIds.push_back(prefixId);
- _LOG_TRACE("Prefix " << localProbePrefix << " got registered");
+ m_registeredPrefixHandles.push_back(prefixId);
+ _LOG_TRACE("Prefix " << localhopProbePrefix << " got registered");
- // register prefixes for each CA
- for (const auto& item : m_config.m_caItems) {
- Name prefix = item.m_caName;
- prefix.append("CA");
+ // register prefixes
+ Name prefix = m_config.m_caName;
+ prefix.append("CA");
- prefixId = m_face.registerPrefix(prefix,
- [&] (const Name& name) {
- // NEW
- auto filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
- bind(&CaModule::handleNew, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- // SELECT
- filterId = m_face.setInterestFilter(Name(name).append("_SELECT"),
- bind(&CaModule::handleSelect, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- // VALIDATE
- filterId = m_face.setInterestFilter(Name(name).append("_VALIDATE"),
- bind(&CaModule::handleValidate, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- // STATUS
- filterId = m_face.setInterestFilter(Name(name).append("_STATUS"),
- bind(&CaModule::handleStatus, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- // DOWNLOAD
- filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
- bind(&CaModule::handleDownload, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- // PROBE
- if (item.m_probe != "") {
- filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
- bind(&CaModule::handleProbe, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- }
- // LIST
- if (item.m_relatedCaList.size() > 0) {
- filterId = m_face.setInterestFilter(Name(name).append("_LIST"),
- bind(&CaModule::handleList, this, _2, item));
- m_interestFilterIds.push_back(filterId);
- }
- _LOG_TRACE("Prefix " << name << " got registered");
- },
- bind(&CaModule::onRegisterFailed, this, _2));
- m_registeredPrefixIds.push_back(prefixId);
- }
+ prefixId = m_face.registerPrefix(prefix,
+ [&] (const Name& name) {
+ // register PROBE prefix
+ auto filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
+ bind(&CaModule::onProbe, this, _2));
+ m_interestFilterHandles.push_back(filterId);
+
+ // register NEW prefix
+ filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
+ bind(&CaModule::onNew, this, _2));
+ m_interestFilterHandles.push_back(filterId);
+
+ // register SELECT prefix
+ filterId = m_face.setInterestFilter(Name(name).append("_CHALLENGE"),
+ bind(&CaModule::onChallenge, this, _2));
+ m_interestFilterHandles.push_back(filterId);
+
+ // register DOWNLOAD prefix
+ filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
+ bind(&CaModule::onDownload, this, _2));
+ m_interestFilterHandles.push_back(filterId);
+ _LOG_TRACE("Prefix " << name << " got registered");
+ },
+ bind(&CaModule::onRegisterFailed, this, _2));
+ m_registeredPrefixHandles.push_back(prefixId);
}
bool
-CaModule::setProbeHandler(const Name caName, const ProbeHandler& handler)
+CaModule::setProbeHandler(const ProbeHandler& handler)
{
- for (auto& entry : m_config.m_caItems) {
- if (entry.m_caName == caName) {
- entry.m_probeHandler = handler;
- return true;
- }
- }
+ m_config.m_probeHandler = handler;
return false;
}
bool
-CaModule::setRecommendCaHandler(const Name caName, const RecommendCaHandler& handler)
+CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
{
- for (auto& entry : m_config.m_caItems) {
- if (entry.m_caName == caName) {
- entry.m_recommendCaHandler = handler;
- return true;
- }
- }
- return false;
-}
-
-bool
-CaModule::setStatusUpdateCallback(const Name caName, const StatusUpdateCallback& onUpateCallback)
-{
- for (auto& entry : m_config.m_caItems) {
- if (entry.m_caName == caName) {
- entry.m_statusUpdateCallback = onUpateCallback;
- return true;
- }
- }
+ m_config.m_statusUpdateCallback = onUpdateCallback;
return false;
}
void
-CaModule::handleLocalhostList(const Interest& request)
+CaModule::onProbe(const Interest& request)
{
- _LOG_TRACE("Got Localhost LIST request");
+ // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent|INFO]
+ _LOG_TRACE("Receive PROBE request");
+ JsonSection contentJson;
- JsonSection root;
- JsonSection caListSection;
-
- for (const auto& entry : m_config.m_caItems) {
- JsonSection caItem;
-
- const auto& pib = m_keyChain.getPib();
- auto identity = pib.getIdentity(entry.m_caName);
- auto cert = identity.getDefaultKey().getDefaultCertificate();
-
- // ca-prefix
- Name caName = entry.m_caName;
- caName.append("CA");
- caItem.put("ca-prefix", caName.toUri());
-
- // ca-info
- std::string caInfo;
- if (entry.m_caInfo == "") {
- caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
+ // process PROBE INFO requests
+ if (readString(request.getName().at(-1)) == "INFO") {
+ contentJson = genProbeResponseJson();
+ }
+ else {
+ // if not a PROBE INFO, find an available name
+ std::string availableId = "";
+ const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
+ std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
+ if (m_config.m_probeHandler) {
+ try {
+ availableId = m_config.m_probeHandler(probeInfoStr);
+ }
+ catch (const std::exception& e) {
+ _LOG_TRACE("Cannot find PROBE input from PROBE parameters " << e.what());
+ return;
+ }
}
else {
- caInfo = entry.m_caInfo;
+ // if there is no app-specified name lookup, use a random name id
+ availableId = std::to_string(random::generateSecureWord64());
}
- caItem.put("ca-info", caInfo);
-
- // probe is always false for local client
-
- // ca-target list
- caItem.put("target-list", entry.m_targetedList);
-
- // certificate
- std::stringstream ss;
- io::save(cert, ss);
- caItem.put("certificate", ss.str());
-
- caListSection.push_back(std::make_pair("", caItem));
+ Name newIdentityName = m_config.m_caName;
+ _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
+ newIdentityName.append(availableId);
+ contentJson = genProbeResponseJson(newIdentityName.toUri());
}
- root.add_child("ca-list", caListSection);
-
- Data result;
- Name dataName = request.getName();
- dataName.appendTimestamp();
- result.setName(dataName);
- result.setContent(dataContentFromJson(root));
- m_keyChain.sign(result, signingByIdentity(m_keyChain.getPib().getDefaultIdentity().getName()));
- m_face.put(result);
-}
-
-void
-CaModule::handleList(const Interest& request, const CaItem& caItem)
-{
- _LOG_TRACE("Got LIST request");
-
- bool getRecommendation = false;
- Name recommendedCaName;
- std::string identityName;
-
- // LIST naming convention: /CA-prefix/CA/_LIST/[optional info]
- if (readString(request.getName().at(-1)) != "_LIST" && caItem.m_recommendCaHandler) {
- const auto& additionInfo = readString(request.getName().at(-1));
- try {
- std::tie(recommendedCaName, identityName) = caItem.m_recommendCaHandler(additionInfo, caItem.m_relatedCaList);
- getRecommendation = true;
- }
- catch (const std::exception& e) {
- _LOG_TRACE("Cannot recommend CA for LIST request. Degrade to non-target list." << e.what());
- }
- }
-
- JsonSection root;
- JsonSection caListSection;
- if (getRecommendation) {
- // JSON format
- // {
- // "recommended-ca": "/ndn/edu/ucla"
- // "recommended-identity": "something"
- // "trust-schema": "schema Data packet name"
- // }
- root.put("recommended-ca", recommendedCaName.toUri());
- root.put("recommended-identity", identityName);
- }
- else {
- // JSON format
- // {
- // "ca-list": [
- // {"ca-prefix": "/ndn/edu/ucla"},
- // {"ca-prefix": "/ndn/edu/memphis"},
- // ...
- // ]
- // "trust-schema": "schema Data packet name"
- // }
- for (const auto& entry : caItem.m_relatedCaList) {
- JsonSection caItem;
- caItem.put("ca-prefix", entry.toUri());
- caListSection.push_back(std::make_pair("", caItem));
- }
- root.add_child("ca-list", caListSection);
- }
-
- // TODO: add trust schema
- std::string schemaDataName = "TODO: add trust schema";
- root.put("trust-schema", schemaDataName);
-
- Data result;
- Name dataName = request.getName();
- dataName.appendTimestamp();
- result.setName(dataName);
- result.setContent(dataContentFromJson(root));
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
- m_face.put(result);
-}
-
-void
-CaModule::handleProbe(const Interest& request, const CaItem& caItem)
-{
- // PROBE Naming Convention: /CA-prefix/CA/_PROBE/<Probe Information>
- _LOG_TRACE("Handle PROBE request");
-
- std::string identifier;
- if (caItem.m_probeHandler) {
- try {
- identifier = caItem.m_probeHandler(readString(request.getName().at(caItem.m_caName.size() + 2)));
- }
- catch (const std::exception& e) {
- _LOG_TRACE("Cannot generate identifier for PROBE request " << e.what());
- return;
- }
- }
- else {
- identifier = readString(request.getName().at(caItem.m_caName.size() + 2));
- }
- Name identityName = caItem.m_caName;
- identityName.append(identifier);
Data result;
result.setName(request.getName());
- result.setContent(dataContentFromJson(genResponseProbeJson(identityName, "")));
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
+ result.setContent(dataContentFromJson(contentJson));
+ m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
m_face.put(result);
-
- _LOG_TRACE("Handle PROBE: generate identity " << identityName);
+ _LOG_TRACE("Handle PROBE: send out the PROBE response");
}
void
-CaModule::handleNew(const Interest& request, const CaItem& caItem)
+CaModule::onNew(const Interest& request)
{
- // NEW Naming Convention: /CA-prefix/CA/_NEW/<certificate-request>/[signature]
- _LOG_TRACE("Handle NEW request");
+ // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
- security::v2::Certificate clientCert;
+ // get ECDH pub key and cert request
+ const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
+ std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
+
+ // get server's ECDH pub key
+ auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
+ m_ecdh.deriveSecret(peerKeyBase64);
+ // 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);
+
+ // parse certificate request
+ std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
+ shared_ptr<security::v2::Certificate> clientCert = nullptr;
try {
- clientCert.wireDecode(request.getName().at(caItem.m_caName.size() + 2).blockFromValue());
+ std::stringstream ss(certRequestStr);
+ clientCert = io::load<security::v2::Certificate>(ss);
}
catch (const std::exception& e) {
_LOG_ERROR("Unrecognized certificate request " << e.what());
return;
}
- if (!security::verifySignature(clientCert, clientCert)) {
+ // verify the self-signed certificate and the request
+ if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
+ || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
+ || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
+ _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
+ return;
+ }
+ if (!security::verifySignature(*clientCert, *clientCert)) {
_LOG_TRACE("Cert request with bad signature.");
return;
}
- if (!security::verifySignature(request, clientCert)) {
+ if (!security::verifySignature(request, *clientCert)) {
_LOG_TRACE("Interest with bad signature.");
return;
}
+ // create new request instance
std::string requestId = std::to_string(random::generateWord64());
- CertificateRequest certRequest(caItem.m_caName, requestId, clientCert);
- certRequest.setStatus(ChallengeModule::WAIT_SELECTION);
+ CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
try {
m_storage->addRequest(certRequest);
}
catch (const std::exception& e) {
- _LOG_TRACE("Cannot add new request instance " << e.what());
+ _LOG_TRACE("Cannot add new request instance into the storage" << e.what());
return;
}
Data result;
result.setName(request.getName());
- result.setContent(dataContentFromJson(genResponseNewJson(requestId, certRequest.getStatus(),
- caItem.m_supportedChallenges)));
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
+ result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
+ std::to_string(saltInt),
+ certRequest,
+ m_config.m_supportedChallenges)));
+ m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
m_face.put(result);
- if (caItem.m_statusUpdateCallback) {
- caItem.m_statusUpdateCallback(certRequest);
+ if (m_config.m_statusUpdateCallback) {
+ m_config.m_statusUpdateCallback(certRequest);
}
}
void
-CaModule::handleSelect(const Interest& request, const CaItem& caItem)
+CaModule::onChallenge(const Interest& request)
{
- // SELECT Naming Convention: /CA-prefix/CA/_SELECT/{Request-ID JSON}/<ChallengeID>/
- // {Param JSON}/[Signature components]
- _LOG_TRACE("Handle SELECT request");
-
- CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
- if (certRequest.getRequestId().empty()) {
+ // get certificate request state
+ CertificateRequest certRequest = getCertificateRequest(request);
+ if (certRequest.m_requestId == "") {
+ // cannot get the request state
return;
}
-
- if (!security::verifySignature(request, certRequest.getCert())) {
+ // verify signature
+ if (!security::verifySignature(request, certRequest.m_cert)) {
_LOG_TRACE("Interest with bad signature.");
return;
}
+ // decrypt the parameters
+ auto paramJsonPayload = parseEncBlock(m_ecdh.context->sharedSecret,
+ m_ecdh.context->sharedSecretLen,
+ request.getApplicationParameters());
+ std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
+ std::istringstream ss(paramJsonStr);
+ JsonSection paramJson;
+ boost::property_tree::json_parser::read_json(ss, paramJson);
- std::string challengeType;
- try {
- challengeType = readString(request.getName().at(caItem.m_caName.size() + 3));
- }
- catch (const std::exception& e) {
- _LOG_ERROR(e.what());
- return;
- }
- _LOG_TRACE("SELECT request choosing challenge " << challengeType);
+ // load the corresponding challenge module
+ std::string challengeType = paramJson.get<std::string>(JSON_CLIENT_SELECTED_CHALLENGE);
auto challenge = ChallengeModule::createChallengeModule(challengeType);
+ JsonSection contentJson;
if (challenge == nullptr) {
_LOG_TRACE("Unrecognized challenge type " << challengeType);
- return;
- }
- JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
- if (certRequest.getStatus() == ChallengeModule::FAILURE) {
- m_storage->deleteRequest(certRequest.getRequestId());
+ certRequest.m_status = STATUS_FAILURE;
+ certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
+ contentJson = genChallengeResponseJson(certRequest);
}
else {
- try {
- m_storage->updateRequest(certRequest);
+ _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
+ // let challenge module handle the request
+ challenge->handleChallengeRequest(paramJson, certRequest);
+ if (certRequest.m_status == STATUS_FAILURE) {
+ // if challenge failed
+ m_storage->deleteRequest(certRequest.m_requestId);
+ contentJson = genChallengeResponseJson(certRequest);
+ _LOG_TRACE("Challenge failed");
}
- catch (const std::exception& e) {
- _LOG_TRACE("Cannot update request instance " << e.what());
- return;
- }
- }
-
- Data result;
- result.setName(request.getName());
- result.setContent(dataContentFromJson(contentJson));
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
- m_face.put(result);
-
- if (caItem.m_statusUpdateCallback) {
- caItem.m_statusUpdateCallback(certRequest);
- }
-}
-
-void
-CaModule::handleValidate(const Interest& request, const CaItem& caItem)
-{
- // VALIDATE Naming Convention: /CA-prefix/CA/_VALIDATE/{Request-ID JSON}/<ChallengeID>/
- // {Param JSON}/[Signature components]
- _LOG_TRACE("Handle VALIDATE request");
-
- CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
- if (certRequest.getRequestId().empty()) {
- return;
- }
-
- if (!security::verifySignature(request, certRequest.getCert())) {
- _LOG_TRACE("Interest with bad signature.");
- return;
- }
-
- std::string challengeType = certRequest.getChallengeType();
- auto challenge = ChallengeModule::createChallengeModule(challengeType);
- if (challenge == nullptr) {
- _LOG_TRACE("Unrecognized challenge type " << challengeType);
- return;
- }
- JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
- if (certRequest.getStatus() == ChallengeModule::FAILURE) {
- m_storage->deleteRequest(certRequest.getRequestId());
- }
- else {
- try {
- m_storage->updateRequest(certRequest);
- }
- catch (const std::exception& e) {
- _LOG_TRACE("Cannot update request instance " << e.what());
- return;
- }
- }
- Data result;
- result.setName(request.getName());
- result.setContent(dataContentFromJson(contentJson));
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
- m_face.put(result);
-
- if (certRequest.getStatus() == ChallengeModule::SUCCESS) {
- auto issuedCert = issueCertificate(certRequest, caItem);
- if (caItem.m_statusUpdateCallback) {
- certRequest.setCert(issuedCert);
- caItem.m_statusUpdateCallback(certRequest);
- }
- try {
- m_storage->addCertificate(certRequest.getRequestId(), issuedCert);
- m_storage->deleteRequest(certRequest.getRequestId());
- _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
- }
- catch (const std::exception& e) {
- _LOG_ERROR("Cannot add issued cert and remove the request " << e.what());
- return;
- }
- }
-}
-
-void
-CaModule::handleStatus(const Interest& request, const CaItem& caItem)
-{
- // STATUS Naming Convention: /CA-prefix/CA/_STATUS/{Request-ID JSON}/[Signature components]
- _LOG_TRACE("Handle STATUS request");
-
- CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
- if (certRequest.getRequestId().empty()) {
- return;
- }
-
- if (!security::verifySignature(request, certRequest.getCert())) {
- _LOG_TRACE("Interest with bad signature.");
- return;
- }
-
- std::string challengeType = certRequest.getChallengeType();
- auto challenge = ChallengeModule::createChallengeModule(challengeType);
- if (challenge == nullptr) {
- _LOG_TRACE("Unrecognized challenge type " << challengeType);
- return;
- }
- JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
-
- Data result;
- result.setName(request.getName());
- result.setContent(dataContentFromJson(contentJson));
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
- m_face.put(result);
-}
-
-void
-CaModule::handleDownload(const Interest& request, const CaItem& caItem)
-{
- // DOWNLOAD Naming Convention: /CA-prefix/CA/_DOWNLOAD/{Request-ID JSON}
- _LOG_TRACE("Handle DOWNLOAD request");
-
- Data result;
- result.setName(request.getName());
- if (readString(request.getName().at(-1)) == "ANCHOR") {
- JsonSection contentJson;
-
- const auto& pib = m_keyChain.getPib();
- auto identity = pib.getIdentity(caItem.m_caName);
- auto cert = identity.getDefaultKey().getDefaultCertificate();
-
- // ca-prefix
- Name caName = caItem.m_caName;
- caName.append("CA");
- contentJson.put("ca-prefix", caName.toUri());
-
- // ca-info
- std::string caInfo;
- if (caItem.m_caInfo == "") {
- caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
+ else if (certRequest.m_status == STATUS_PENDING) {
+ // if challenge succeeded
+ auto issuedCert = issueCertificate(certRequest);
+ certRequest.m_cert = issuedCert;
+ certRequest.m_status = STATUS_SUCCESS;
+ try {
+ m_storage->addCertificate(certRequest.m_requestId, issuedCert);
+ m_storage->deleteRequest(certRequest.m_requestId);
+ _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
+ }
+ catch (const std::exception& e) {
+ _LOG_ERROR("Cannot add issued cert and remove the request " << e.what());
+ return;
+ }
+ if (m_config.m_statusUpdateCallback) {
+ m_config.m_statusUpdateCallback(certRequest);
+ }
+ contentJson = genChallengeResponseJson(certRequest);
+ contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
+ _LOG_TRACE("Challenge succeeded. Certificate has been issued");
}
else {
- caInfo = caItem.m_caInfo;
+ try {
+ m_storage->updateRequest(certRequest);
+ }
+ catch (const std::exception& e) {
+ _LOG_TRACE("Cannot update request instance " << e.what());
+ return;
+ }
+ contentJson = genChallengeResponseJson(certRequest);
+ _LOG_TRACE("No failure no success. Challenge moves on");
}
- contentJson.put("ca-info", caInfo);
-
- // probe
- contentJson.put("probe", caItem.m_probe);
-
- // ca-target list
- contentJson.put("target-list", caItem.m_targetedList);
-
- // certificate
- std::stringstream ss;
- io::save(cert, ss);
- contentJson.put("certificate", ss.str());
-
- result.setContent(dataContentFromJson(contentJson));
}
- else {
- JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caItem.m_caName.size() + 2);
- std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
- security::v2::Certificate signedCert;
- try {
- signedCert = m_storage->getCertificate(requestId);
- }
- catch (const std::exception& e) {
- _LOG_ERROR(e.what());
- return;
- }
- result.setContent(signedCert.wireEncode());
+
+ Data result;
+ result.setName(request.getName());
+
+ // encrypt the content
+ std::stringstream ss2;
+ boost::property_tree::write_json(ss2, contentJson);
+ auto payload = ss2.str();
+ auto contentBlock = genEncBlock(tlv::Content, m_ecdh.context->sharedSecret,
+ m_ecdh.context->sharedSecretLen,
+ (const uint8_t*)payload.c_str(), payload.size());
+ result.setContent(contentBlock);
+ m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
+ m_face.put(result);
+
+ if (m_config.m_statusUpdateCallback) {
+ m_config.m_statusUpdateCallback(certRequest);
}
- m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
+}
+
+void
+CaModule::onDownload(const Interest& request)
+{
+ auto requestId = readString(request.getName().at(-1));
+ security::v2::Certificate signedCert;
+ try {
+ signedCert = m_storage->getCertificate(requestId);
+ }
+ catch (const std::exception& e) {
+ _LOG_ERROR("Cannot read signed cert " << requestId << " from ca database " << e.what());
+ return;
+ }
+ Data result;
+ result.setName(request.getName());
+ result.setContent(signedCert.wireEncode());
+ m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
m_face.put(result);
}
security::v2::Certificate
-CaModule::issueCertificate(const CertificateRequest& certRequest, const CaItem& caItem)
+CaModule::issueCertificate(const CertificateRequest& certRequest)
{
- Name certName = certRequest.getCert().getKeyName();
- certName.append("NDNCERT").appendVersion();
+ auto expectedPeriod =
+ certRequest.m_cert.getValidityPeriod().getPeriod();
+
+ time::system_clock::TimePoint startingTime, endingTime;
+ if (expectedPeriod.first > time::system_clock::now()
+ && expectedPeriod.first < time::system_clock::now()
+ + m_config.m_validityPeriod)
+ {
+ startingTime = expectedPeriod.first;
+ }
+ else {
+ startingTime = time::system_clock::now();
+ }
+ if (expectedPeriod.second < time::system_clock::now() + m_config.m_validityPeriod) {
+ endingTime = expectedPeriod.second;
+ }
+ else {
+ endingTime = time::system_clock::now() + m_config.m_validityPeriod;
+ }
+ security::ValidityPeriod period(startingTime, endingTime);
security::v2::Certificate newCert;
+
+ Name certName = certRequest.m_cert.getKeyName();
+ certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
newCert.setName(certName);
- newCert.setContent(certRequest.getCert().getContent());
- _LOG_TRACE("cert request content " << certRequest.getCert());
+ newCert.setContent(certRequest.m_cert.getContent());
+ _LOG_TRACE("cert request content " << certRequest.m_cert);
SignatureInfo signatureInfo;
- security::ValidityPeriod period(time::system_clock::now(),
- time::system_clock::now() + caItem.m_validityPeriod);
signatureInfo.setValidityPeriod(period);
security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
- caItem.m_caName, signatureInfo);
- newCert.setFreshnessPeriod(caItem.m_freshnessPeriod);
+ m_config.m_caName, signatureInfo);
+ newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
m_keyChain.sign(newCert, signingInfo);
_LOG_TRACE("new cert got signed" << newCert);
@@ -571,10 +385,10 @@
}
CertificateRequest
-CaModule::getCertificateRequest(const Interest& request, const Name& caName)
+CaModule::getCertificateRequest(const Interest& request)
{
- JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caName.size() + 2);
- std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
+ std::string requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
+ _LOG_TRACE("Requet Id to query the database " << requestId);
CertificateRequest certRequest;
try {
certRequest = m_storage->getRequest(requestId);
@@ -585,6 +399,108 @@
return certRequest;
}
+/**
+ * @brief Generate JSON file to response PROBE insterest
+ *
+ * PROBE response JSON format:
+ * {
+ * "name": "@p identifier",
+ * "ca-config": "@p caInformation"
+ * }
+ */
+const JsonSection
+CaModule::genProbeResponseJson(const Name& identifier)
+{
+ JsonSection root;
+ root.put(JSON_CA_NAME, identifier.toUri());
+ return root;
+}
+
+/**
+ * @brief Generate JSON file to response NEW interest
+ *
+ * Target JSON format:
+ * {
+ * "ecdh-pub": "@p echdPub",
+ * "salt": "@p salt"
+ * "request-id": "@p requestId",
+ * "status": "@p status",
+ * "challenges": [
+ * {
+ * "challenge-id": ""
+ * },
+ * {
+ * "challenge-id": ""
+ * },
+ * ...
+ * ]
+ * }
+ */
+const JsonSection
+CaModule::genProbeResponseJson()
+{
+ JsonSection root;
+ // ca-prefix
+ Name caName = m_config.m_caName;
+ root.put("ca-prefix", caName.toUri());
+
+ // ca-info
+ const auto& pib = m_keyChain.getPib();
+ auto identity = pib.getIdentity(m_config.m_caName);
+ auto cert = identity.getDefaultKey().getDefaultCertificate();
+ std::string caInfo = "";
+ if (m_config.m_caInfo == "") {
+ caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
+ }
+ else {
+ caInfo = m_config.m_caInfo;
+ }
+ root.put("ca-info", caInfo);
+
+ // probe
+ root.put("probe", m_config.m_probe);
+
+ // certificate
+ std::stringstream ss;
+ io::save(cert, ss);
+ root.put("certificate", ss.str());
+
+ return root;
+}
+
+const JsonSection
+CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
+ const CertificateRequest& request,
+ const std::list<std::string>& challenges)
+{
+ JsonSection root;
+ JsonSection challengesSection;
+ root.put(JSON_CA_ECDH, ecdhKey);
+ root.put(JSON_CA_SALT, salt);
+ root.put(JSON_CA_EQUEST_ID, request.m_requestId);
+ root.put(JSON_CA_STATUS, std::to_string(request.m_status));
+
+ for (const auto& entry : challenges) {
+ JsonSection challenge;
+ challenge.put(JSON_CA_CHALLENGE_ID, entry);
+ challengesSection.push_back(std::make_pair("", challenge));
+ }
+ root.add_child(JSON_CA_CHALLENGES, challengesSection);
+ return root;
+}
+
+const JsonSection
+CaModule::genChallengeResponseJson(const CertificateRequest& request)
+{
+ JsonSection root;
+ JsonSection challengesSection;
+ root.put(JSON_CA_STATUS, request.m_status);
+ root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
+ root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
+ root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
+ return root;
+}
+
void
CaModule::onRegisterFailed(const std::string& reason)
{
@@ -600,14 +516,14 @@
}
JsonSection
-CaModule::jsonFromNameComponent(const Name& name, int pos)
+CaModule::jsonFromBlock(const Block& block)
{
std::string jsonString;
try {
- jsonString = encoding::readString(name.at(pos));
+ jsonString = encoding::readString(block);
}
catch (const std::exception& e) {
- _LOG_ERROR(e.what());
+ _LOG_ERROR("Cannot read JSON string from TLV Value" << e.what());
return JsonSection();
}
std::istringstream ss(jsonString);