Access Manager
Change-Id: I5febe225dc8ba2f7967dabb5e1bf0bab6428d6c2
diff --git a/src/access-manager.cpp b/src/access-manager.cpp
new file mode 100644
index 0000000..056ef33
--- /dev/null
+++ b/src/access-manager.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018, Regents of the University of California
+ *
+ * NAC 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.
+ *
+ * NAC 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 NAC library authors and contributors.
+ */
+
+#include "access-manager.hpp"
+#include "encrypted-content.hpp"
+
+#include <ndn-cxx/util/logger.hpp>
+
+NDN_LOG_INIT(nac.AccessManager);
+
+namespace ndn {
+namespace nac {
+
+AccessManager::AccessManager(const Identity& identity, const Name& dataset,
+ KeyChain& keyChain, Face& face)
+ : m_identity(identity)
+ , m_keyChain(keyChain)
+ , m_face(face)
+{
+ // NAC Identity: <identity>/NAC/<dataset>
+ // generate NAC key
+ auto nacId = m_keyChain.createIdentity(Name(identity.getName()).append(NAC).append(dataset), RsaKeyParams());
+ m_nacKey = nacId.getDefaultKey();
+ if (m_nacKey.getKeyType() != KeyType::RSA) {
+ NDN_LOG_INFO("Cannot re-use existing KEK/KDK pair, as it is not an RSA key, regenerating");
+ m_nacKey = m_keyChain.createKey(nacId, RsaKeyParams());
+ }
+ auto nacKeyId = m_nacKey.getName().at(-1);
+
+ auto kekPrefix = Name(m_nacKey.getIdentity()).append(KEK);
+
+ auto kek = make_shared<Data>(m_nacKey.getDefaultCertificate());
+ kek->setName(Name(kekPrefix).append(nacKeyId));
+ m_keyChain.sign(*kek, signingByIdentity(m_identity));
+ // kek looks like a cert, but doesn't have ValidityPeriod
+ m_ims.insert(*kek);
+
+ auto serveFromIms = [this] (const Name& prefix, const Interest& interest) {
+ auto data = m_ims.find(interest);
+ if (data != nullptr) {
+ NDN_LOG_DEBUG("Serving " << data->getName() << " from InMemoryStorage");
+ m_face.put(*data);
+ }
+ else {
+ NDN_LOG_DEBUG("Didn't find data for " << interest.getName());
+ // send NACK?
+ }
+ };
+
+ auto handleError = [] (const Name& prefix, const std::string& msg) {
+ NDN_LOG_ERROR("Failed to register prefix " << prefix << ": " << msg);
+ };
+
+ m_kekRegId = m_face.setInterestFilter(kekPrefix, serveFromIms, handleError);
+
+ auto kdkPrefix = Name(m_nacKey.getIdentity()).append(KDK).append(nacKeyId);
+ m_kdkRegId = m_face.setInterestFilter(kdkPrefix, serveFromIms, handleError);
+}
+
+AccessManager::~AccessManager()
+{
+ m_face.unsetInterestFilter(m_kekRegId);
+ m_face.unsetInterestFilter(m_kdkRegId);
+}
+
+void
+AccessManager::addMember(const Certificate& memberCert)
+{
+ Name kdkName(m_nacKey.getIdentity());
+ kdkName
+ .append(KDK)
+ .append(m_nacKey.getName().at(-1)) // key-id
+ .append(ENCRYPTED_BY)
+ .append(memberCert.getKeyName());
+
+ const size_t secretLength = 32;
+ uint8_t secret[secretLength + 1];
+ random::generateSecureBytes(secret, secretLength);
+ // because of stupid bug in ndn-cxx, remove all \0 in generated secret, replace with 1
+ for (size_t i = 0; i < secretLength; ++i) {
+ if (secret[i] == 0) {
+ secret[i] = 1;
+ }
+ }
+ secret[secretLength] = 0;
+
+ auto kdkData = m_keyChain.exportSafeBag(m_nacKey.getDefaultCertificate(),
+ reinterpret_cast<const char*>(secret), secretLength);
+
+ PublicKey memberKey;
+ memberKey.loadPkcs8(memberCert.getPublicKey().data(), memberCert.getPublicKey().size());
+
+ EncryptedContent content;
+ content.setPayload(kdkData->wireEncode());
+ content.setPayloadKey(memberKey.encrypt(secret, secretLength));
+
+ auto kdk = make_shared<Data>(kdkName);
+ kdk->setContent(content.wireEncode());
+ kdk->setFreshnessPeriod(1_h); // this can serve as soft access control for revoking access
+ m_keyChain.sign(*kdk, signingByIdentity(m_identity));
+
+ m_ims.insert(*kdk);
+}
+
+void
+AccessManager::removeMember(const Name& identity)
+{
+ m_ims.erase(Name(m_nacKey.getName()).append(KDK).append(ENCRYPTED_BY).append(identity));
+}
+
+} // namespace nac
+} // namespace ndn
diff --git a/src/access-manager.hpp b/src/access-manager.hpp
new file mode 100644
index 0000000..4ee592f
--- /dev/null
+++ b/src/access-manager.hpp
@@ -0,0 +1,160 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018, Regents of the University of California
+ *
+ * NAC 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.
+ *
+ * NAC 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 NAC library authors and contributors.
+ */
+
+#ifndef NDN_NAC_ACCESS_MANAGER_HPP
+#define NDN_NAC_ACCESS_MANAGER_HPP
+
+#include "common.hpp"
+
+#include <ndn-cxx/face.hpp>
+
+namespace ndn {
+namespace nac {
+
+/**
+ * @brief Access Manager
+ *
+ * Access Manager controls decryption policy by publishing granular per-namespace access
+ * policies in the form of key encryption (KEK, plaintext public) and key decryption (KDK,
+ * encrypted private keys) key pairs.
+ *
+ * TODO Allow rolling KEK
+ */
+class AccessManager
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ using std::runtime_error::runtime_error;
+ };
+
+public:
+ /**
+ * @param identity Identity of the namespace (i.e., public and private keys)
+ *
+ * @param identity Data owner's namespace identity (will be used to sign KEK and KDK)
+ * @param dataset Name of dataset that this manager is controlling
+ * @param keyChain KeyChain
+ * @param face Face that will be used to publish KEK and KDKs
+ *
+ * Additional info:
+ *
+ * [identity]/NAC/[dataset]/KEK || /[key-id] (== KEK, public key)
+ *
+ * [identity]/NAC/[dataset]/KDK/[key-id] || /ENCRYPTED-BY/[user]/KEY/[key-id] (== KDK, encrypted private key)
+ *
+ * \_____________ ______________/
+ * \/
+ * registered with NFD
+ *
+ * AccessManager serves NAC public key for data producers to fetch and encrypted versions of
+ * private keys (as safe bags) for authorized consumers to fetch.
+ */
+ AccessManager(const Identity& identity, const Name& dataset,
+ KeyChain& keyChain, Face& face);
+
+ ~AccessManager();
+
+ /**
+ * @brief Authorize a member identified by its certificate @p memberCert to decrypt data
+ * under the policy
+ */
+ void
+ addMember(const Certificate& memberCert);
+
+ // void
+ // addMemberWithKey(const Name& keyName);
+
+ // void
+ // addMemberWithIdentity(const Name& identityName);
+
+ /**
+ * @brief Remove member with name @p identity from the group
+ */
+ void
+ removeMember(const Name& identity);
+
+public: // accessor interface for published data packets
+
+ /** @return{ number of packets stored in in-memory storage }
+ */
+ size_t
+ size() const
+ {
+ return m_ims.size();
+ }
+
+ /** @brief Returns begin iterator of the in-memory storage ordering by
+ * name with digest
+ *
+ * @return{ const_iterator pointing to the beginning of the m_cache }
+ */
+ InMemoryStorage::const_iterator
+ begin() const
+ {
+ return m_ims.begin();
+ }
+
+ /** @brief Returns end iterator of the in-memory storage ordering by
+ * name with digest
+ *
+ * @return{ const_iterator pointing to the end of the m_cache }
+ */
+ InMemoryStorage::const_iterator
+ end() const
+ {
+ return m_ims.end();
+ }
+
+private:
+ Identity m_identity;
+ Key m_nacKey;
+ KeyChain& m_keyChain;
+ Face& m_face;
+
+ // this interface should be general enough to allow plugging in other things
+
+ // /**
+ // * Should be interface (persisent or in memory) for storing and serving encrypted KDKs
+ // */
+ // class KdkStorage
+ // {
+ // public:
+ // void
+ // addKdk(Data kdk);
+
+ // void
+ // removeKdk(const Name& kdkName);
+
+ // // stuff to serve
+
+ // private:
+ // InMemoryStorage m_ims;
+ // };
+
+ InMemoryStoragePersistent m_ims;
+ const RegisteredPrefixId* m_kekRegId;
+ const RegisteredPrefixId* m_kdkRegId;
+};
+
+} // namespace nac
+} // namespace ndn
+
+#endif // NDN_NAC_ACCESS_MANAGER_HPP