Skip to content

Commit

Permalink
enforce CK and app data segmentation
Browse files Browse the repository at this point in the history
  • Loading branch information
tianyuan129 committed Jul 26, 2023
1 parent 5c379f2 commit 5d989b8
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 201 deletions.
65 changes: 41 additions & 24 deletions examples/kp-producer-example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received copies of the GNU General Public License along with
* NAC-ABE, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
* NAC-ABE, e.g., in COPYING.md file. If not,ndn see <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of NAC-ABE authors and contributors.
*/
Expand All @@ -22,6 +22,7 @@
#include <ndn-cxx/security/certificate.hpp>
#include <ndn-cxx/security/key-chain.hpp>
#include <ndn-cxx/security/signing-helpers.hpp>
#include <ndn-cxx/util/time.hpp>

#include <producer.hpp> // or <nac-abe/producer.hpp>

Expand All @@ -48,34 +49,50 @@ class Producer
{
const std::string plainText = "Hello world";
const std::vector<std::string> attributes = {"attribute"};
auto longlivedData = std::make_shared<ndn::Data>();
longlivedData->setFreshnessPeriod(ndn::time::hours(1));

std::shared_ptr<ndn::Data> contentData, ckData;
std::vector<std::shared_ptr<ndn::Data>> contentData, ckData;
std::tie(contentData, ckData) = m_producer.produce("/randomData", attributes,
{reinterpret_cast<const uint8_t*>(plainText.data()),
plainText.size()}, m_signingInfo);

std::cout << "Content data name: " << contentData->getName() << std::endl;
plainText.size()}, m_signingInfo,
longlivedData, longlivedData, 50);

std::cout << "Content data object name: " << contentData.at(0)->getName().getPrefix(-1) << std::endl;
m_face.setInterestFilter(m_producerCert.getIdentity(),
[=] (const auto&, const auto& interest) {
std::cout << ">> I: " << interest << std::endl;
if (interest.getName().isPrefixOf(m_cert.getName())) {
m_face.put(m_cert);
}
if (interest.getName().isPrefixOf(contentData->getName())) {
std::cout << "<< D: " << contentData->getName() << std::endl;
m_face.put(*contentData);
}
if (interest.getName().isPrefixOf(ckData->getName())) {
std::cout << "<< D: " << ckData->getName() << std::endl;
m_face.put(*ckData);
}
},
[this] (const auto& prefix, const std::string& reason) {
std::cerr << "ERROR: Failed to register prefix '" << prefix
<< "' with the local forwarder (" << reason << ")" << std::endl;
m_face.shutdown();
});
[=] (const auto&, const auto& interest) {
std::cout << ">> I: " << interest << std::endl;
if (interest.getName().isPrefixOf(m_cert.getName())) {
m_face.put(m_cert);
}
for (auto seg : contentData) {
bool exactSeg = interest.getName() == seg->getName();
bool probeSeg = (interest.getName() == seg->getName().getPrefix(-1)) &&
interest.getCanBePrefix();
if (exactSeg || probeSeg) {
std::cout << "<< D: " << seg->getName() << std::endl;
m_face.put(*seg);
std::cout << seg->getContent().size() << " bytes" << std::endl;
break;
}
}
for (auto seg : ckData) {
bool exactSeg = interest.getName() == seg->getName();
bool probeSeg = (interest.getName() == seg->getName().getPrefix(-1)) &&
interest.getCanBePrefix();
if (exactSeg || probeSeg) {
std::cout << "<< D: " << seg->getName() << std::endl;
m_face.put(*seg);
std::cout << seg->getContent().size() << " bytes" << std::endl;
break;
}
}
},
[this] (const auto& prefix, const std::string& reason) {
std::cerr << "ERROR: Failed to register prefix '" << prefix
<< "' with the local forwarder (" << reason << ")" << std::endl;
m_face.shutdown();
});

m_face.processEvents();
}
Expand Down
24 changes: 14 additions & 10 deletions src/cache-producer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,41 +30,45 @@ CacheProducer::clearCache()
m_kpKeyCache.clear();
}

std::tuple<std::shared_ptr<Data>, std::shared_ptr<Data>>
std::tuple<std::vector<std::shared_ptr<Data>>, std::vector<std::shared_ptr<Data>>>
CacheProducer::produce(const Name& dataName, const Policy& accessPolicy,
span<const uint8_t> content, const security::SigningInfo& info,
std::shared_ptr<Data> ckTemplate, shared_ptr<Data> dataTemplate)
std::shared_ptr<Data> ckTemplate, shared_ptr<Data> dataTemplate,
size_t maxSegmentSize)
{
if (m_cpKeyCache.count(accessPolicy) == 0) {
auto k = ckDataGen(accessPolicy, info, ckTemplate);
if (k.first == nullptr || k.second == nullptr) {
return std::make_tuple(nullptr, nullptr);
if (k.first == nullptr || k.second.size() == 0) {
return std::make_tuple(std::vector<std::shared_ptr<Data>>(), std::vector<std::shared_ptr<Data>>());
}
m_cpKeyCache.emplace(accessPolicy, k);
}

auto& key = m_cpKeyCache.at(accessPolicy);
auto data = Producer::produce(key.first, key.second->getName(), dataName, content, info, dataTemplate);
Name ckObjName = key.second.at(0)->getName().getPrefix(-1);
auto data = Producer::produce(key.first, ckObjName, dataName, content, info, dataTemplate);
return std::make_tuple(data, key.second);
}

std::tuple<std::shared_ptr<Data>, std::shared_ptr<Data>>
std::tuple<std::vector<std::shared_ptr<Data>>, std::vector<std::shared_ptr<Data>>>
CacheProducer::produce(const Name& dataName, const std::vector<std::string>& attributes,
span<const uint8_t> content, const security::SigningInfo& info,
std::shared_ptr<Data> ckTemplate, shared_ptr<Data> dataTemplate)
std::shared_ptr<Data> ckTemplate, shared_ptr<Data> dataTemplate,
size_t maxSegmentSize)
{
std::stringstream ss;
for (auto& i : attributes) ss << i << "|";
auto attStr = ss.str();
if (m_kpKeyCache.count(attStr) == 0) {
auto k = ckDataGen(attributes, info, ckTemplate);
if (k.first == nullptr || k.second == nullptr) {
return std::make_tuple(nullptr, nullptr);
if (k.first == nullptr || k.second.size() == 0) {
return std::make_tuple(std::vector<std::shared_ptr<Data>>(), std::vector<std::shared_ptr<Data>>());
}
m_kpKeyCache.emplace(attStr, k);
}
auto& key = m_kpKeyCache.at(attStr);
auto data = Producer::produce(key.first, key.second->getName(), dataName, content, info, dataTemplate);
Name ckObjName = key.second.at(0)->getName().getPrefix(-1);
auto data = Producer::produce(key.first, ckObjName, dataName, content, info, dataTemplate);
return std::make_tuple(data, key.second);
}

Expand Down
14 changes: 8 additions & 6 deletions src/cache-producer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ class CacheProducer : public Producer
* @param info The signing parameters
* @return The encrypted data and the encrypted CK data
*/
std::tuple<std::shared_ptr<Data>, std::shared_ptr<Data>>
std::tuple<std::vector<std::shared_ptr<Data>>, std::vector<std::shared_ptr<Data>>>
produce(const Name& dataName, const Policy& accessPolicy,
span<const uint8_t> content, const security::SigningInfo& info,
std::shared_ptr<Data> ckTemplate = getDefaultCkTemplate(),
shared_ptr<Data> dataTemplate = getDefaultEncryptedDataTemplate()) override;
shared_ptr<Data> dataTemplate = getDefaultEncryptedDataTemplate(),
size_t maxSegmentSize = 1500) override;

/**
* @brief Produce KP-encrypted Data and corresponding encrypted CK Data
Expand All @@ -69,15 +70,16 @@ class CacheProducer : public Producer
* @param info The signing parameters
* @return The encrypted data and the encrypted CK data
*/
std::tuple<std::shared_ptr<Data>, std::shared_ptr<Data>>
std::tuple<std::vector<std::shared_ptr<Data>>, std::vector<std::shared_ptr<Data>>>
produce(const Name& dataName, const std::vector<std::string>& attributes,
span<const uint8_t> content, const security::SigningInfo& info,
std::shared_ptr<Data> ckTemplate = getDefaultCkTemplate(),
shared_ptr<Data> dataTemplate = getDefaultEncryptedDataTemplate()) override;
shared_ptr<Data> dataTemplate = getDefaultEncryptedDataTemplate(),
size_t maxSegmentSize = 1500) override;

PUBLIC_WITH_TESTS_ELSE_PRIVATE:
std::map<std::string, std::pair<std::shared_ptr<algo::ContentKey>, std::shared_ptr<Data>>> m_cpKeyCache;
std::map<std::string, std::pair<std::shared_ptr<algo::ContentKey>, std::shared_ptr<Data>>> m_kpKeyCache;
std::map<std::string, std::pair<std::shared_ptr<algo::ContentKey>, std::vector<std::shared_ptr<Data>>>> m_cpKeyCache;
std::map<std::string, std::pair<std::shared_ptr<algo::ContentKey>, std::vector<std::shared_ptr<Data>>>> m_kpKeyCache;
};

} // namespace nacabe
Expand Down
111 changes: 74 additions & 37 deletions src/consumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ Consumer::obtainDecryptionKey()
interest.setCanBePrefix(true);

auto fetcher = util::SegmentFetcher::start(m_face, interest, m_validator);
fetcher->afterSegmentValidated.connect([](Data data) {
NDN_LOG_DEBUG("Validated " << data.getName());
fetcher->afterSegmentValidated.connect([](Data seg) {
NDN_LOG_DEBUG("Validated " << seg.getName());
});
fetcher->onComplete.connect([this] (ConstBufferPtr contentBuffer) {
NDN_LOG_DEBUG("SegmentFetcher completed with total fetched size of " << contentBuffer->size());
Expand Down Expand Up @@ -119,16 +119,34 @@ Consumer::consume(const Interest& dataInterest,
std::string nackMessage = "Nack for " + dataInterest.getName().toUri() + " data fetch with reason ";
std::string timeoutMessage = "Timeout for " + dataInterest.getName().toUri() + " data fetch";

auto dataCallback = [this, consumptionCb, errorCallback] (const Interest&, const Data& data) {
m_validator.validate(data,
[this, consumptionCb, errorCallback] (const Data& data) {
NDN_LOG_INFO("Encrypted data conforms to trust schema");
decryptContent(data, consumptionCb, errorCallback);
},
[] (auto&&, const ndn::security::ValidationError& error) {
NDN_THROW(std::runtime_error("Encrypted data cannot be authenticated: " + error.getInfo()));
}
);
auto dataCallback = [=] (const Interest&, const Data& data) {
Name dataName = data.getName();
// if segmentation
if (dataName.get(-1).isSegment()) {
auto fetcher = util::SegmentFetcher::start(m_face, dataInterest, m_validator);
fetcher->afterSegmentValidated.connect([](Data seg) {
NDN_LOG_DEBUG("Validated " << seg.getName());
});
fetcher->onComplete.connect([=] (ConstBufferPtr contentBuffer) {
NDN_LOG_DEBUG("SegmentFetcher completed with total fetched size of " << contentBuffer->size());
decryptContent(dataName.getPrefix(-1), Block(contentBuffer), consumptionCb, errorCallback);
});
fetcher->onError.connect([] (uint32_t errorCode, const std::string& errorMsg) {
NDN_LOG_ERROR("Error occurs in segment fetching: " << errorMsg);
});
}
// if no segmentation
else {
m_validator.validate(data,
[=] (const Data& data) {
NDN_LOG_INFO("Encrypted data conforms to trust schema");
decryptContent(data.getName(), data.getContent(), consumptionCb, errorCallback);
},
[] (auto&&, const ndn::security::ValidationError& error) {
NDN_THROW(std::runtime_error("Encrypted data cannot be authenticated: " + error.getInfo()));
}
);
}
};

NDN_LOG_INFO(m_cert.getIdentity() << " Ask for data " << dataInterest.getName() );
Expand All @@ -140,42 +158,61 @@ Consumer::consume(const Interest& dataInterest,
}

void
Consumer::decryptContent(const Data& data,
Consumer::decryptContent(const Name& dataObjName,
const Block& content,
const ConsumptionCallback& successCallBack,
const ErrorCallback& errorCallback)
{
// get encrypted content
NDN_LOG_INFO(m_cert.getIdentity() << " Get content data " << data.getName());
Block encryptedContent = data.getContent();
encryptedContent.parse();
auto encryptedContentTLV = encryptedContent.get(TLV_EncryptedContent);
NDN_LOG_INFO(m_cert.getIdentity() << " Get content data " << dataObjName);
content.parse();
auto encryptedContentTLV = content.get(TLV_EncryptedContent);

NDN_LOG_INFO("Encrypted Content size is " << encryptedContentTLV.value_size());
auto cipherText = std::make_shared<algo::CipherText>();
cipherText->m_content = Buffer(encryptedContentTLV.value(), encryptedContentTLV.value_size());
cipherText->m_plainTextSize = readNonNegativeInteger(encryptedContent.get(TLV_PlainTextSize));
cipherText->m_plainTextSize = readNonNegativeInteger(content.get(TLV_PlainTextSize));

Name ckName(encryptedContent.get(tlv::Name));
Name ckName(content.get(tlv::Name));
NDN_LOG_INFO("CK Name is " << ckName);
Interest ckInterest(ckName);
ckInterest.setCanBePrefix(true);

std::string nackMessage = "Nack for " + ckName.toUri() + " content key fetch with reason ";
std::string timeoutMessage = "Timeout for " + ckName.toUri() + " content key fetch";

auto dataCallback = [this, cipherText, successCallBack, errorCallback] (const Interest&, const Data& data) {
m_validator.validate(data,
[this, cipherText, successCallBack, errorCallback] (const Data& data) {
NDN_LOG_INFO("Content key conforms to trust schema");
onCkeyData(data, cipherText, successCallBack, errorCallback);
},
[this] (auto&&, const ndn::security::ValidationError& error) {
NDN_THROW(std::runtime_error("Fetched content key cannot be authenticated: " + error.getInfo()));
}
);
auto dataCallback = [=] (const Interest&, const Data& data) {
Name dataName = data.getName();
// if segmentation
if (dataName.get(-1).isSegment()) {
auto fetcher = util::SegmentFetcher::start(m_face, ckInterest, m_validator);
fetcher->afterSegmentValidated.connect([](Data seg) {
NDN_LOG_DEBUG("Validated " << seg.getName());
});
fetcher->onComplete.connect([=] (ConstBufferPtr contentBuffer) {
NDN_LOG_DEBUG("SegmentFetcher completed with total fetched size of " << contentBuffer->size());
onCkeyData(dataName.getPrefix(-1), Block(contentBuffer), cipherText, successCallBack, errorCallback);
});
fetcher->onError.connect([] (uint32_t errorCode, const std::string& errorMsg) {
NDN_LOG_ERROR("Error occurs in segment fetching: " << errorMsg);
});
}
// if no segmentation
else {
m_validator.validate(data,
[=] (const Data& data) {
NDN_LOG_INFO("Content key conforms to trust schema");
onCkeyData(data.getName(), data.getContent(), cipherText, successCallBack, errorCallback);
},
[] (auto&&, const ndn::security::ValidationError& error) {
NDN_THROW(std::runtime_error("Fetched content key cannot be authenticated: " + error.getInfo()));
}
);
}
};

NDN_LOG_INFO(m_cert.getIdentity() << " Ask for data " << ckInterest.getName() );
// probe segmentation
NDN_LOG_INFO(m_cert.getIdentity() << " Ask for data " << ckInterest.getName());
m_face.expressInterest(ckInterest,
dataCallback,
std::bind(&Consumer::handleNack, this, _1, _2, errorCallback, nackMessage),
Expand All @@ -184,15 +221,15 @@ Consumer::decryptContent(const Data& data,
}

void
Consumer::onCkeyData(const Data& data, std::shared_ptr<algo::CipherText> cipherText,
const ConsumptionCallback& successCallBack,
const ErrorCallback& errorCallback)
Consumer::onCkeyData(const Name& ckObjName, const Block& content,
std::shared_ptr<algo::CipherText> cipherText,
const ConsumptionCallback& successCallBack,
const ErrorCallback& errorCallback)
{
NDN_LOG_INFO(m_cert.getIdentity() << " Get CKEY data " << data.getName());
Block ckContent = data.getContent();
ckContent.parse();
NDN_LOG_INFO(m_cert.getIdentity() << " Get CKEY data " << ckObjName);
content.parse();

auto encryptedAESKeyTLV = ckContent.get(TLV_EncryptedAesKey);
auto encryptedAESKeyTLV = content.get(TLV_EncryptedAesKey);
cipherText->m_contentKey = std::make_shared<algo::ContentKey>();
cipherText->m_contentKey->m_encAesKey = Buffer(encryptedAESKeyTLV.value(), encryptedAESKeyTLV.value_size());

Expand Down
7 changes: 5 additions & 2 deletions src/consumer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,15 @@ class Consumer

private:
void
decryptContent(const Data& data,
decryptContent(const Name& dataObjName,
const Block& content,
const ConsumptionCallback& successCallBack,
const ErrorCallback& errorCallback);

void
onCkeyData(const Data& data, std::shared_ptr<algo::CipherText> cipherText,
onCkeyData(const Name& ckObjName,
const Block& content,
std::shared_ptr<algo::CipherText> cipherText,
const ConsumptionCallback& successCallBack,
const ErrorCallback& errorCallback);

Expand Down
Loading

0 comments on commit 5d989b8

Please sign in to comment.