Encryptor
Change-Id: Ie9d12038160ec17489a6dae5e6002728d6815ddf
diff --git a/src/encryptor.cpp b/src/encryptor.cpp
new file mode 100644
index 0000000..26b9a73
--- /dev/null
+++ b/src/encryptor.cpp
@@ -0,0 +1,189 @@
+/* -*- 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 "encryptor.hpp"
+
+#include <ndn-cxx/util/logger.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nac {
+
+NDN_LOG_INIT(nac.Encryptor);
+
+const size_t N_RETRIES = 3;
+
+Encryptor::Encryptor(const Name& accessPrefix,
+ const Name& ckPrefix, SigningInfo ckDataSigningInfo,
+ const ErrorCallback& onFailure,
+ Validator& validator, KeyChain& keyChain, Face& face)
+ : m_accessPrefix{accessPrefix}
+ , m_ckPrefix{ckPrefix}
+ , m_ckBits{AES_KEY_SIZE}
+ , m_ckDataSigningInfo{std::move(ckDataSigningInfo)}
+ , m_isKekRetrievalInProgress(false)
+ , m_ckRegId{nullptr}
+ , m_keyChain{keyChain}
+ , m_face{face}
+{
+ regenerateCk(onFailure);
+
+ 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 CK 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_ckRegId = m_face.setInterestFilter(Name(ckPrefix).append(CK), serveFromIms, handleError);
+}
+
+Encryptor::~Encryptor()
+{
+ m_face.unsetInterestFilter(m_ckRegId);
+ if (m_kekPendingInterest != nullptr) {
+ m_face.removePendingInterest(m_kekPendingInterest);
+ }
+}
+
+void
+Encryptor::regenerateCk(const ErrorCallback& onFailure)
+{
+ m_ckName = m_ckPrefix;
+ m_ckName
+ .append(CK)
+ .appendVersion(); // version = ID of CK
+ random::generateSecureBytes(m_ckBits.data(), m_ckBits.size());
+
+ NDN_LOG_DEBUG("Generating new CK: " << m_ckName);
+
+ // one implication: if CK updated before KEK fetched, KDK for the old CK will not be published
+ if (!m_kek) {
+ m_isKekRetrievalInProgress = true;
+ fetchKekAndPublishCkData([&] {
+ NDN_LOG_DEBUG("KEK retrieved and published");
+ m_isKekRetrievalInProgress = false;
+ },
+ [&] (const ErrorCode&, const std::string& msg) {
+ NDN_LOG_ERROR("Failed to retrieved KEK: " + msg);
+ m_isKekRetrievalInProgress = false;
+ },
+ N_RETRIES);
+ }
+ else {
+ makeAndPublishCkData(onFailure);
+ }
+}
+
+EncryptedContent
+Encryptor::encrypt(const uint8_t* data, size_t size)
+{
+ // Generate initial vector
+ auto iv = make_shared<Buffer>(AES_IV_SIZE);
+ random::generateSecureBytes(iv->data(), iv->size());
+
+ OBufferStream os;
+ security::transform::bufferSource(data, size)
+ >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
+ CipherOperator::ENCRYPT,
+ m_ckBits.data(), m_ckBits.size(), iv->data(), iv->size())
+ >> security::transform::streamSink(os);
+
+ EncryptedContent content;
+ content.setIv(iv);
+ content.setPayload(os.buf());
+ content.setKeyLocator(m_ckName);
+
+ return content;
+}
+
+void
+Encryptor::fetchKekAndPublishCkData(const std::function<void()>& onReady,
+ const ErrorCallback& onFailure,
+ size_t nTriesLeft)
+{
+ // interest for <access-prefix>/KEK to retrieve <access-prefix>/KEK/<key-id> KekData
+
+ NDN_LOG_DEBUG("Fetching KEK " << Name(m_accessPrefix).append(KEK));
+
+ BOOST_ASSERT(m_kekPendingInterest == nullptr);
+ m_kekPendingInterest =
+ m_face.expressInterest(Interest(Name(m_accessPrefix).append(KEK))
+ .setMustBeFresh(true)
+ .setCanBePrefix(true),
+ [=] (const Interest&, const Data& kek) {
+ m_kekPendingInterest = nullptr;
+ // @todo verify if the key is legit
+ m_kek = kek;
+ makeAndPublishCkData(onFailure);
+ // otherwise, failure has been already declared
+ },
+ [=] (const Interest& i, const lp::Nack& nack) {
+ m_kekPendingInterest = nullptr;
+ onFailure(ErrorCode::KekRetrievalFailure,
+ "Retrieval of KEK [" + i.getName().toUri() + "] failed. "
+ "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
+ },
+ [=] (const Interest& i) {
+ m_kekPendingInterest = nullptr;
+ if (nTriesLeft > 1) {
+ fetchKekAndPublishCkData(onReady, onFailure, nTriesLeft - 1);
+ }
+ else {
+ onFailure(ErrorCode::KekRetrievalTimeout,
+ "Retrieval of KEK [" + i.getName().toUri() + "] timed out");
+ }
+ });
+}
+
+void
+Encryptor::makeAndPublishCkData(const ErrorCallback& onFailure)
+{
+ try {
+ PublicKey kek;
+ kek.loadPkcs8(m_kek->getContent().value(), m_kek->getContent().value_size());
+
+ EncryptedContent content;
+ content.setPayload(kek.encrypt(m_ckBits.data(), m_ckBits.size()));
+
+ auto ckData = make_shared<Data>(Name(m_ckName).append(ENCRYPTED_BY).append(m_kek->getName()));
+ ckData->setContent(content.wireEncode());
+ // FreshnessPeriod can serve as a soft access control for revoking access
+ ckData->setFreshnessPeriod(DEFAULT_CK_FRESHNESS_PERIOD);
+ m_keyChain.sign(*ckData, m_ckDataSigningInfo);
+ m_ims.insert(*ckData);
+
+ NDN_LOG_DEBUG("Publishing CK data: " << ckData->getName());
+ }
+ catch (const std::runtime_error& error) {
+ onFailure(ErrorCode::EncryptionFailure, "Failed to encrypt generated CK with KEK " + m_kek->getName().toUri());
+ }
+}
+
+} // namespace nac
+} // namespace ndn