security: Abstract certificate fetching from v2::Validator
Change-Id: Ia98d11ac67b0095f632818ac37a19a1e5a7656a8
Refs: #3921
diff --git a/src/security/v2/certificate-fetcher-from-network.cpp b/src/security/v2/certificate-fetcher-from-network.cpp
new file mode 100644
index 0000000..5d7bcaa
--- /dev/null
+++ b/src/security/v2/certificate-fetcher-from-network.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "certificate-fetcher-from-network.hpp"
+#include "face.hpp"
+#include "util/logger.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+NDN_LOG_INIT(ndn.security.v2.CertificateFetcher);
+
+#define NDN_LOG_DEBUG_DEPTH(x) NDN_LOG_DEBUG(std::string(state->getDepth() + 1, '>') << " " << x)
+#define NDN_LOG_TRACE_DEPTH(x) NDN_LOG_TRACE(std::string(state->getDepth() + 1, '>') << " " << x)
+
+CertificateFetcherFromNetwork::CertificateFetcherFromNetwork(Face& face)
+ : m_face(face)
+{
+}
+
+void
+CertificateFetcherFromNetwork::doFetch(const shared_ptr<CertificateRequest>& certRequest,
+ const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ m_face.expressInterest(certRequest->m_interest,
+ [=] (const Interest& interest, const Data& data) {
+ dataCallback(data, certRequest, state, continueValidation);
+ },
+ [=] (const Interest& interest, const lp::Nack& nack) {
+ nackCallback(nack, certRequest, state, continueValidation);
+ },
+ [=] (const Interest& interest) {
+ timeoutCallback(certRequest, state, continueValidation);
+ });
+}
+
+void
+CertificateFetcherFromNetwork::dataCallback(const Data& data,
+ const shared_ptr<CertificateRequest>& certRequest,
+ const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ NDN_LOG_DEBUG_DEPTH("Fetched certificate from network " << data.getName());
+
+ Certificate cert;
+ try {
+ cert = Certificate(data);
+ }
+ catch (const tlv::Error& e) {
+ return state->fail({ValidationError::Code::MALFORMED_CERT, "Fetched a malformed certificate "
+ "`" + data.getName().toUri() + "` (" + e.what() + ")"});
+ }
+ continueValidation(cert, state);
+}
+
+void
+CertificateFetcherFromNetwork::nackCallback(const lp::Nack& nack,
+ const shared_ptr<CertificateRequest>& certRequest,
+ const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ NDN_LOG_DEBUG_DEPTH("NACK (" << nack.getReason() << ") while fetching certificate "
+ << certRequest->m_interest.getName());
+
+ --certRequest->m_nRetriesLeft;
+ if (certRequest->m_nRetriesLeft >= 0) {
+ // TODO implement delay for the the next fetch
+ fetch(certRequest, state, continueValidation);
+ }
+ else {
+ state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate after all "
+ "retries `" + certRequest->m_interest.getName().toUri() + "`"});
+ }
+}
+
+void
+CertificateFetcherFromNetwork::timeoutCallback(const shared_ptr<CertificateRequest>& certRequest,
+ const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ NDN_LOG_DEBUG_DEPTH("Timeout while fetching certificate " << certRequest->m_interest.getName()
+ << ", retrying");
+
+ --certRequest->m_nRetriesLeft;
+ if (certRequest->m_nRetriesLeft >= 0) {
+ fetch(certRequest, state, continueValidation);
+ }
+ else {
+ state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate after all "
+ "retries `" + certRequest->m_interest.getName().toUri() + "`"});
+ }
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/certificate-fetcher-from-network.hpp b/src/security/v2/certificate-fetcher-from-network.hpp
new file mode 100644
index 0000000..e5076c3
--- /dev/null
+++ b/src/security/v2/certificate-fetcher-from-network.hpp
@@ -0,0 +1,88 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_SECURITY_V2_CERTIFICATE_FETCHER_FROM_NETWORK_HPP
+#define NDN_SECURITY_V2_CERTIFICATE_FETCHER_FROM_NETWORK_HPP
+
+#include "certificate-fetcher.hpp"
+
+namespace ndn {
+
+namespace lp {
+class Nack;
+} // namespace lp
+
+namespace security {
+namespace v2 {
+
+/**
+ * @brief Fetch missing keys from the network
+ */
+class CertificateFetcherFromNetwork : public CertificateFetcher
+{
+public:
+ explicit
+ CertificateFetcherFromNetwork(Face& face);
+
+protected:
+ void
+ doFetch(const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) override;
+
+private:
+ /**
+ * @brief Callback invoked when certificate is retrieved.
+ */
+ void
+ dataCallback(const Data& data,
+ const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation);
+
+ /**
+ * @brief Callback invoked when interest for fetching certificate gets NACKed.
+ *
+ * It will retry if certRequest->m_nRetriesLeft > 0
+ *
+ * @todo Delay retry for some amount of time
+ */
+ void
+ nackCallback(const lp::Nack& nack,
+ const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation);
+
+ /**
+ * @brief Callback invoked when interest for fetching certificate times out.
+ *
+ * It will retry if certRequest->m_nRetriesLeft > 0
+ */
+ void
+ timeoutCallback(const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation);
+
+protected:
+ Face& m_face;
+};
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_CERTIFICATE_FETCHER_FROM_NETWORK_HPP
diff --git a/src/security/v2/certificate-fetcher-offline.cpp b/src/security/v2/certificate-fetcher-offline.cpp
new file mode 100644
index 0000000..8bf28e1
--- /dev/null
+++ b/src/security/v2/certificate-fetcher-offline.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "certificate-fetcher-offline.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+void
+CertificateFetcherOffline::doFetch(const shared_ptr<CertificateRequest>& certRequest,
+ const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT,
+ "Cannot fetch certificate " + certRequest->m_interest.getName().toUri() + " in offline mode"});
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/certificate-fetcher-offline.hpp b/src/security/v2/certificate-fetcher-offline.hpp
new file mode 100644
index 0000000..9cfa176
--- /dev/null
+++ b/src/security/v2/certificate-fetcher-offline.hpp
@@ -0,0 +1,46 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_SECURITY_V2_CERTIFICATE_FETCHER_OFFLINE_HPP
+#define NDN_SECURITY_V2_CERTIFICATE_FETCHER_OFFLINE_HPP
+
+#include "certificate-fetcher.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+/**
+ * @brief Certificate fetcher realization that does not fetch keys (always offline)
+ */
+class CertificateFetcherOffline : public CertificateFetcher
+{
+protected:
+ void
+ doFetch(const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) override;
+};
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_CERTIFICATE_FETCHER_OFFLINE_HPP
diff --git a/src/security/v2/certificate-fetcher.cpp b/src/security/v2/certificate-fetcher.cpp
new file mode 100644
index 0000000..ffb4416
--- /dev/null
+++ b/src/security/v2/certificate-fetcher.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "certificate-fetcher.hpp"
+#include "util/logger.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+NDN_LOG_INIT(ndn.security.v2.CertificateFetcher);
+
+#define NDN_LOG_DEBUG_DEPTH(x) NDN_LOG_DEBUG(std::string(state->getDepth() + 1, '>') << " " << x)
+
+CertificateFetcher::CertificateFetcher()
+ : m_certStorage(nullptr)
+{
+}
+
+CertificateFetcher::~CertificateFetcher() = default;
+
+void
+CertificateFetcher::setCertificateStorage(CertificateStorage& certStorage)
+{
+ m_certStorage = &certStorage;
+}
+
+void
+CertificateFetcher::fetch(const shared_ptr<CertificateRequest>& certRequest,
+ const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ BOOST_ASSERT(m_certStorage != nullptr);
+ auto cert = m_certStorage->getUnverifiedCertCache().find(certRequest->m_interest);
+ if (cert != nullptr) {
+ NDN_LOG_DEBUG_DEPTH("Found certificate in **un**verified key cache " << cert->getName());
+ continueValidation(*cert, state);
+ return;
+ }
+ doFetch(certRequest, state,
+ [continueValidation, this] (const Certificate& cert, const shared_ptr<ValidationState>& state) {
+ m_certStorage->cacheUnverifiedCert(Certificate(cert));
+ continueValidation(cert, state);
+ });
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/certificate-fetcher.hpp b/src/security/v2/certificate-fetcher.hpp
new file mode 100644
index 0000000..7abc5f5
--- /dev/null
+++ b/src/security/v2/certificate-fetcher.hpp
@@ -0,0 +1,91 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_SECURITY_V2_CERTIFICATE_FETCHER_HPP
+#define NDN_SECURITY_V2_CERTIFICATE_FETCHER_HPP
+
+#include "certificate-request.hpp"
+#include "certificate-storage.hpp"
+#include "validation-state.hpp"
+
+namespace ndn {
+
+class Face;
+
+namespace security {
+namespace v2 {
+
+/**
+ * @brief Interface used by the validator to fetch missing certificates
+ */
+class CertificateFetcher : noncopyable
+{
+public:
+ using ValidationContinuation = std::function<void(const Certificate& cert,
+ const shared_ptr<ValidationState>& state)>;
+
+ CertificateFetcher();
+
+ virtual
+ ~CertificateFetcher();
+
+ /**
+ * @brief Assign certificate storage to check known certificate and to cache unverified ones
+ * @note The supplied @p certStorage should be valid for the lifetime of CertificateFetcher
+ */
+ void
+ setCertificateStorage(CertificateStorage& certStorage);
+
+ /**
+ * @brief Asynchronously fetch certificate
+ * @pre m_certStorage != nullptr
+ *
+ * If the requested certificate exists in the storage, then this method will immediately call
+ * continueValidation with the certification. If certificate is not available, the
+ * implementation-specific doFetch will be called to asynchronously fetch certificate. The
+ * successfully retrieved certificate will be automatically added to the unverified cache of
+ * the certificate storage.
+ *
+ * When the requested certificate is retrieved, continueValidation is called. Otherwise, the
+ * fetcher implementation call state->failed() with the appropriate error code and diagnostic
+ * message.
+ */
+ void
+ fetch(const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation);
+
+private:
+ /**
+ * @brief Asynchronous certificate fetching implementation
+ */
+ virtual void
+ doFetch(const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) = 0;
+
+protected:
+ CertificateStorage* m_certStorage;
+};
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_CERTIFICATE_FETCHER_HPP
diff --git a/src/security/v2/certificate-storage.cpp b/src/security/v2/certificate-storage.cpp
new file mode 100644
index 0000000..89869fa
--- /dev/null
+++ b/src/security/v2/certificate-storage.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "certificate-storage.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+CertificateStorage::CertificateStorage()
+ : m_verifiedCertCache(time::hours(1))
+ , m_unverifiedCertCache(time::minutes(5))
+{
+}
+
+const Certificate*
+CertificateStorage::findTrustedCert(const Interest& interestForCert) const
+{
+ auto cert = m_trustAnchors.find(interestForCert);
+ if (cert != nullptr) {
+ return cert;
+ }
+
+ cert = m_verifiedCertCache.find(interestForCert);
+ return cert;
+}
+
+bool
+CertificateStorage::isCertKnown(const Name& certName) const
+{
+ return (m_trustAnchors.find(certName) != nullptr ||
+ m_verifiedCertCache.find(certName) != nullptr ||
+ m_unverifiedCertCache.find(certName) != nullptr);
+}
+
+void
+CertificateStorage::loadAnchor(const std::string& groupId, Certificate&& cert)
+{
+ m_trustAnchors.insert(groupId, std::move(cert));
+}
+
+void
+CertificateStorage::loadAnchor(const std::string& groupId, const std::string& certfilePath,
+ time::nanoseconds refreshPeriod, bool isDir)
+{
+ m_trustAnchors.insert(groupId, certfilePath, refreshPeriod, isDir);
+}
+
+void
+CertificateStorage::cacheVerifiedCert(Certificate&& cert)
+{
+ m_verifiedCertCache.insert(std::move(cert));
+}
+
+void
+CertificateStorage::cacheUnverifiedCert(Certificate&& cert)
+{
+ m_unverifiedCertCache.insert(std::move(cert));
+}
+
+const TrustAnchorContainer&
+CertificateStorage::getTrustAnchors() const
+{
+ return m_trustAnchors;
+}
+
+const CertificateCache&
+CertificateStorage::getVerifiedCertCache() const
+{
+ return m_verifiedCertCache;
+}
+
+const CertificateCache&
+CertificateStorage::getUnverifiedCertCache() const
+{
+ return m_unverifiedCertCache;
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/certificate-storage.hpp b/src/security/v2/certificate-storage.hpp
new file mode 100644
index 0000000..ff54ae2
--- /dev/null
+++ b/src/security/v2/certificate-storage.hpp
@@ -0,0 +1,131 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_SECURITY_V2_CERTIFICATE_STORAGE_HPP
+#define NDN_SECURITY_V2_CERTIFICATE_STORAGE_HPP
+
+#include "certificate.hpp"
+#include "certificate-cache.hpp"
+#include "trust-anchor-container.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+/**
+ * @brief Storage for trusted anchors, verified certificate cache, and unverified certificate cache.
+ */
+class CertificateStorage : noncopyable
+{
+public:
+ CertificateStorage();
+
+ /**
+ * @brief Find a trusted certificate in trust anchor container or in verified cache
+ * @param interestForCert Interest for certificate
+ * @return found certificate, nullptr if not found.
+ *
+ * @note The returned pointer may get invalidated after next findTrustedCert or findCert calls.
+ */
+ const Certificate*
+ findTrustedCert(const Interest& interestForCert) const;
+
+ /**
+ * @brief Check if certificate exists in verified, unverified cache, or in the set of trust
+ * anchors
+ */
+ bool
+ isCertKnown(const Name& certPrefix) const;
+
+ /**
+ * @brief Cache unverified certificate for a period of time (5 minutes)
+ * @param cert The certificate packet
+ *
+ * @todo Add ability to customize time period
+ */
+ void
+ cacheUnverifiedCert(Certificate&& cert);
+
+ /**
+ * @return Trust anchor container
+ */
+ const TrustAnchorContainer&
+ getTrustAnchors() const;
+
+ /**
+ * @return Verified certificate cache
+ */
+ const CertificateCache&
+ getVerifiedCertCache() const;
+
+ /**
+ * @return Unverified certificate cache
+ */
+ const CertificateCache&
+ getUnverifiedCertCache() const;
+
+protected:
+ /**
+ * @brief load static trust anchor.
+ *
+ * Static trust anchors are permanently associated with the validator and never expire.
+ *
+ * @param groupId Certificate group id.
+ * @param cert Certificate to load as a trust anchor.
+ */
+ void
+ loadAnchor(const std::string& groupId, Certificate&& cert);
+
+ /**
+ * @brief load dynamic trust anchors.
+ *
+ * Dynamic trust anchors are associated with the validator for as long as the underlying
+ * trust anchor file (set of files) exist(s).
+ *
+ * @param groupId Certificate group id, must not be empty.
+ * @param certfilePath Specifies the path to load the trust anchors.
+ * @param refreshPeriod Refresh period for the trust anchors, must be positive.
+ * @param isDir Tells whether the path is a directory or a single file.
+ */
+ void
+ loadAnchor(const std::string& groupId, const std::string& certfilePath,
+ time::nanoseconds refreshPeriod, bool isDir = false);
+
+ /**
+ * @brief Cache verified certificate a period of time (1 hour)
+ * @param cert The certificate packet
+ *
+ * @todo Add ability to customize time period
+ */
+ void
+ cacheVerifiedCert(Certificate&& cert);
+
+protected:
+ TrustAnchorContainer m_trustAnchors;
+ CertificateCache m_verifiedCertCache;
+ CertificateCache m_unverifiedCertCache;
+};
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_CERTIFICATE_STORAGE_HPP
diff --git a/src/security/v2/validator.cpp b/src/security/v2/validator.cpp
index b18fc75..4928c67 100644
--- a/src/security/v2/validator.cpp
+++ b/src/security/v2/validator.cpp
@@ -34,13 +34,14 @@
#define NDN_LOG_DEBUG_DEPTH(x) NDN_LOG_DEBUG(std::string(state->getDepth() + 1, '>') << " " << x)
#define NDN_LOG_TRACE_DEPTH(x) NDN_LOG_TRACE(std::string(state->getDepth() + 1, '>') << " " << x)
-Validator::Validator(unique_ptr<ValidationPolicy> policy, Face* face)
+Validator::Validator(unique_ptr<ValidationPolicy> policy, unique_ptr<CertificateFetcher> certFetcher)
: m_policy(std::move(policy))
- , m_face(face)
- , m_verifiedCertificateCache(time::hours(1))
- , m_unverifiedCertificateCache(time::minutes(5))
+ , m_certFetcher(std::move(certFetcher))
, m_maxDepth(25)
{
+ BOOST_ASSERT(m_policy != nullptr);
+ BOOST_ASSERT(m_certFetcher != nullptr);
+ m_certFetcher->setCertificateStorage(*this);
}
Validator::~Validator() = default;
@@ -101,6 +102,12 @@
Validator::validate(const Certificate& cert, const shared_ptr<ValidationState>& state)
{
NDN_LOG_DEBUG_DEPTH("Start validating certificate " << cert.getName());
+
+ if (!cert.isValid()) {
+ return state->fail({ValidationError::Code::EXPIRED_CERT, "Retrieved certificate is not yet valid or expired "
+ "`" + cert.getName().toUri() + "`"});
+ }
+
m_policy->checkPolicy(cert, state,
[this, cert] (const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state) {
if (certRequest == nullptr) {
@@ -115,23 +122,6 @@
});
}
-const Certificate*
-Validator::findTrustedCert(const Interest& interestForCertificate, const shared_ptr<ValidationState>& state)
-{
- auto anchor = m_trustAnchors.find(interestForCertificate);
- if (anchor != nullptr) {
- NDN_LOG_TRACE_DEPTH("Found certificate in anchor cache " << anchor->getName());
- return anchor;
- }
-
- auto key = m_verifiedCertificateCache.find(interestForCertificate);
- if (key != nullptr) {
- NDN_LOG_TRACE_DEPTH("Found certificate in verified key cache " << key->getName());
- return key;
- }
- return nullptr;
-}
-
void
Validator::requestCertificate(const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state)
@@ -145,9 +135,10 @@
NDN_LOG_DEBUG_DEPTH("Retrieving " << certRequest->m_interest.getName());
- // Check the trusted cache
- auto cert = findTrustedCert(certRequest->m_interest, state);
+ auto cert = findTrustedCert(certRequest->m_interest);
if (cert != nullptr) {
+ NDN_LOG_TRACE_DEPTH("Found trusted certificate " << cert->getName());
+
cert = state->verifyCertificateChain(*cert);
if (cert != nullptr) {
state->verifyOriginalPacket(*cert);
@@ -160,159 +151,34 @@
return;
}
- if (state->hasSeenCertificateName(certRequest->m_interest.getName())) {
- state->fail({ValidationError::Code::LOOP_DETECTED,
- "Loop detected at " + certRequest->m_interest.getName().toUri()});
- return;
- }
-
- // Check untrusted cache
- cert = m_unverifiedCertificateCache.find(certRequest->m_interest);
- if (cert != nullptr) {
- NDN_LOG_DEBUG_DEPTH("Found certificate in **un**verified key cache " << cert->getName());
- return dataCallback(*cert, certRequest, state, false); // to avoid caching the cached key
- }
-
- // Attempt to retrieve certificate from the network
- fetchCertificateFromNetwork(certRequest, state);
-}
-
-void
-Validator::fetchCertificateFromNetwork(const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state)
-{
- if (m_face == nullptr) {
- state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate in offline mode "
- "`" + certRequest->m_interest.getName().toUri() + "`"});
- return;
- }
-
- m_face->expressInterest(certRequest->m_interest,
- [=] (const Interest& interest, const Data& data) {
- dataCallback(data, certRequest, state);
- },
- [=] (const Interest& interest, const lp::Nack& nack) {
- nackCallback(nack, certRequest, state);
- },
- [=] (const Interest& interest) {
- timeoutCallback(certRequest, state);
- });
-}
-
-void
-Validator::dataCallback(const Data& data,
- const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state,
- bool isFromNetwork)
-{
- NDN_LOG_DEBUG_DEPTH("Retrieved certificate " << (isFromNetwork ? "from network " : "from cache ") << data.getName());
-
- Certificate cert;
- try {
- cert = Certificate(data);
- }
- catch (const tlv::Error& e) {
- return state->fail({ValidationError::Code::MALFORMED_CERT, "Retrieved a malformed certificate "
- "`" + data.getName().toUri() + "` (" + e.what() + ")"});
- }
-
- if (!cert.isValid()) {
- return state->fail({ValidationError::Code::EXPIRED_CERT, "Retrieved certificate is not yet "
- "valid or has expired `" + cert.getName().toUri() + "`"});
- }
- if (isFromNetwork) {
- cacheUnverifiedCertificate(Certificate(cert));
- }
- return validate(cert, state); // recursion step
-}
-
-void
-Validator::nackCallback(const lp::Nack& nack, const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state)
-{
- NDN_LOG_DEBUG_DEPTH("NACK (" << nack.getReason() << ") while retrieving certificate "
- << certRequest->m_interest.getName());
-
- --certRequest->m_nRetriesLeft;
- if (certRequest->m_nRetriesLeft > 0) {
- // TODO implement delay for the the next fetch
- fetchCertificateFromNetwork(certRequest, state);
- }
- else {
- state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate after all "
- "retries `" + certRequest->m_interest.getName().toUri() + "`"});
- }
-}
-
-void
-Validator::timeoutCallback(const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state)
-{
- NDN_LOG_DEBUG_DEPTH("Timeout while retrieving certificate "
- << certRequest->m_interest.getName() << ", retrying");
-
- --certRequest->m_nRetriesLeft;
- if (certRequest->m_nRetriesLeft > 0) {
- fetchCertificateFromNetwork(certRequest, state);
- }
- else {
- state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate after all "
- "retries `" + certRequest->m_interest.getName().toUri() + "`"});
- }
+ m_certFetcher->fetch(certRequest, state, [this] (const Certificate& cert, const shared_ptr<ValidationState>& state) {
+ validate(cert, state);
+ });
}
////////////////////////////////////////////////////////////////////////
// Trust anchor management
////////////////////////////////////////////////////////////////////////
+// to change visibility from protected to public
+
void
Validator::loadAnchor(const std::string& groupId, Certificate&& cert)
{
- m_trustAnchors.insert(groupId, std::move(cert));
+ CertificateStorage::loadAnchor(groupId, std::move(cert));
}
void
Validator::loadAnchor(const std::string& groupId, const std::string& certfilePath,
time::nanoseconds refreshPeriod, bool isDir)
{
- m_trustAnchors.insert(groupId, certfilePath, refreshPeriod, isDir);
+ CertificateStorage::loadAnchor(groupId, certfilePath, refreshPeriod, isDir);
}
void
Validator::cacheVerifiedCertificate(Certificate&& cert)
{
- m_verifiedCertificateCache.insert(std::move(cert));
-}
-
-void
-Validator::cacheUnverifiedCertificate(Certificate&& cert)
-{
- m_unverifiedCertificateCache.insert(std::move(cert));
-}
-
-const TrustAnchorContainer&
-Validator::getTrustAnchors() const
-{
- return m_trustAnchors;
-}
-
-const CertificateCache&
-Validator::getVerifiedCertificateCache() const
-{
- return m_verifiedCertificateCache;
-}
-
-const CertificateCache&
-Validator::getUnverifiedCertificateCache() const
-{
- return m_unverifiedCertificateCache;
-}
-
-bool
-Validator::isCertificateCached(const Name& certName) const
-{
- return (getVerifiedCertificateCache().find(certName) != nullptr ||
- getVerifiedCertificateCache().find(certName) != nullptr);
+ CertificateStorage::cacheVerifiedCert(std::move(cert));
}
} // namespace v2
diff --git a/src/security/v2/validator.hpp b/src/security/v2/validator.hpp
index c17ce82..f24499c 100644
--- a/src/security/v2/validator.hpp
+++ b/src/security/v2/validator.hpp
@@ -22,10 +22,9 @@
#ifndef NDN_SECURITY_V2_VALIDATOR_HPP
#define NDN_SECURITY_V2_VALIDATOR_HPP
-#include "certificate.hpp"
-#include "certificate-cache.hpp"
+#include "certificate-fetcher.hpp"
#include "certificate-request.hpp"
-#include "trust-anchor-container.hpp"
+#include "certificate-storage.hpp"
#include "validation-callback.hpp"
#include "validation-policy.hpp"
#include "validation-state.hpp"
@@ -34,10 +33,6 @@
class Face;
-namespace lp {
-class Nack;
-} // namespace lp
-
namespace security {
namespace v2 {
@@ -52,8 +47,8 @@
* - record names of the requested certificates to detect loops in the certificate chain
* - keep track of the validation chain size (aka validation "depth")
*
- * During validation, policy can augment validation state with policy- and fetcher-specific
- * information using ndn::Tag's.
+ * During validation, policy and/or key fetcher can augment validation state with policy- and
+ * fetcher-specific information using ndn::Tag's.
*
* A validator has a trust anchor cache to save static and dynamic trust anchors, a verified
* certificate cache for saving certificates that are already verified and an unverified
@@ -63,18 +58,16 @@
* @todo Ability to customize maximum lifetime for trusted and untrusted certificate caches.
* Current implementation hard-codes them to be 1 hour and 5 minutes.
*/
-class Validator : noncopyable
+class Validator : public CertificateStorage
{
public:
/**
* @brief Validator constructor.
*
- * @param policy Validation policy to be associated with the validator
- * @param face Face for fetching certificates from network. If provided, the Validator
- * operates in online mode; otherwise, the Validator operates in offline mode.
+ * @param policy Validation policy to be associated with the validator
+ * @param certFetcher Certificate fetcher implementation.
*/
- explicit
- Validator(unique_ptr<ValidationPolicy> policy, Face* face = nullptr);
+ Validator(unique_ptr<ValidationPolicy> policy, unique_ptr<CertificateFetcher> certFetcher);
~Validator();
@@ -145,38 +138,6 @@
void
cacheVerifiedCertificate(Certificate&& cert);
- /**
- * @brief Cache unverified @p cert for a period of time (5 minutes)
- *
- * @todo Add ability to customize time period
- */
- void
- cacheUnverifiedCertificate(Certificate&& cert);
-
- /**
- * @return Trust anchor container
- */
- const TrustAnchorContainer&
- getTrustAnchors() const;
-
- /**
- * @return Verified certificate cache
- */
- const CertificateCache&
- getVerifiedCertificateCache() const;
-
- /**
- * @return Unverified certificate cache
- */
- const CertificateCache&
- getUnverifiedCertificateCache() const;
-
- /**
- * @brief Check if certificate with @p certName exists in verified or unverified cache
- */
- bool
- isCertificateCached(const Name& certName) const;
-
private: // Common validator operations
/**
* @brief Recursive validation of the certificate in the certification chain
@@ -197,75 +158,9 @@
requestCertificate(const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state);
- /**
- * @brief Find trusted certificate among trust anchors and verified certificates.
- *
- * @param interestForCertificate Interest for certificate
- * @param state The current validation state.
- *
- * @return found certificate, nullptr if not found.
- *
- * @note The returned pointer may get invalidated after next findTrustedCert call.
- */
- const Certificate*
- findTrustedCert(const Interest& interestForCertificate,
- const shared_ptr<ValidationState>& state);
-
- /**
- * @brief fetch certificate from network based on certificate request.
- *
- * @param certRequest Certificate request.
- * @param state The current validation state.
- */
- void
- fetchCertificateFromNetwork(const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state);
-
- /**
- * @brief Callback invoked when certificated is retrieved.
- *
- * @param data Retrieved certificate.
- * @param certRequest Certificate request.
- * @param state The current validation state.
- * @param isFromNetwork Flag to indicate that the data packet is retrieved (to avoid re-caching).
- */
- void
- dataCallback(const Data& data,
- const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state,
- bool isFromNetwork = true);
-
- /**
- * @brief Callback invoked when interest for fetching certificate gets NACKed.
- *
- * It will retry for pre-configured amount of retries.
- *
- * @param nack Received NACK
- * @param certRequest Certificate request.
- * @param state The current validation state.
- */
- void
- nackCallback(const lp::Nack& nack, const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state);
-
- /**
- * @brief Callback invoked when interest for fetching certificate times out.
- *
- * It will retry for pre-configured amount of retries.
- *
- * @param certRequest Certificate request.
- * @param state The current validation state.
- */
- void
- timeoutCallback(const shared_ptr<CertificateRequest>& certRequest,
- const shared_ptr<ValidationState>& state);
-
private:
unique_ptr<ValidationPolicy> m_policy;
- Face* m_face;
- TrustAnchorContainer m_trustAnchors;
- CertificateCache m_verifiedCertificateCache;
- CertificateCache m_unverifiedCertificateCache;
+ unique_ptr<CertificateFetcher> m_certFetcher;
size_t m_maxDepth;
};