Named Data Networking Attribute-based Encryption Support Library: NAC-ABE
The publication of this work is NAC: Automating Access Control via Named Data on IEEE MILCOM 2018. To cite the work, you can use the following Bibtex entry.
@inproceedings{zhang2018nac,
title={NAC: Automating access control via Named Data},
author={Zhang, Zhiyi and Yu, Yingdi and Ramani, Sanjeev Kaushik and Afanasyev, Alex and Zhang, Lixia},
booktitle={MILCOM 2018-2018 IEEE Military Communications Conference (MILCOM)},
pages={626--633},
year={2018},
organization={IEEE}
}
NAC-ABE is implemented over the Named Data Networking. To install the NAC-ABE library, you need to first install ndn-cxx library.
To work with the version 0.1.0, please checkout ndn-cxx-0.7.0
and install.
To work with the master version, please checkout ndn-cxx-0.8.0
and install.
NAC-ABE is using cryptography support provided by library openabe. To install openabe, you can visit the website.
(As noticed in July 7, 2020) When installing the OpenABE, there could be some issues installing gTest (on Ubuntu) or Bison 3.3 (on MacOS). While waiting for the OpenABE maintainer to fix them, as a quick solution, you can fix these issues manually.
Really simple to make it using waf.
git clone https://github.com/UCLA-IRL/NAC-ABE.git
NAC-ABE support building by both CMake and waf build.
We start by configuring:
# in the root directory of NAC-ABE
mkdir build && cd build
cmake ..
Or if you want to enable tests:
mkdir build && cd build
cmake -DHAVE_TESTS=True ..
Then compile and install:
make
make install
We start by configuring:
# in the root directory of NAC-ABE
./waf configure
or if you want to enable tests.
./waf configure --with-tests
Then compile with
./waf
And install by
./waf install
To run tests, you must have -DHAVE_TESTS=True
when you config the project.
# in the build directory of NAC-ABE
#run all the tests (including integrate test)
cp ../examples/trust-schema.conf trust-schema.conf
./tests/unit-tests
To run example, you must have -DBUILD_EXAMPLES=True
when you config the project.
# in the examples directory of NAC-ABE
# nfd-start & (if your NFD has not started)
bash run-examples.sh ../build
The library mainly provide supports for four roles in an NDN based ABE scenario.
- Attribute Authority. The party who owns the system master key. It publishes the public parameters to the system and generate decryption keys for decryptors.
- Data owner. The party who decides how encryptors should encrypt their data.
- Encryptor. The party who follows data owner's decision and produce encrypted data.
- Decryptor. The party who get decryption keys from the attribute authority and consume encrypted data.
These four parties are implemented in five classes in the library: CpAttributeAuthority
, KpAttributeAuthority
, DataOwner
, producer
, and consumer
.
For now, Both Ciphertext Policy Attribute-based Encryption (CP-ABE) and Key Policy Attribute-based Encryption (KP-ABE) is supported.
From the perspective of the data flow:
- Content is encrypted by the content KEY (CK), which is a symmetric AES key
- CK is encrypted by the attribute policy (EKEY)
- CK can only be decrypted when the attributes (DKEY) can satisfy the EKEY
- Decryptor obtains an DKEY from attribute authorities
- Encryptor knows which EKEY to use from the data owner
// obtain or create a certificate for attribute authority for CP-ABE
CpAttributeAuthority aa(aaCert, face, keychain);
// obtain or create a certificate for attribute authority for KP-ABE
KpAttributeAuthority aa(aaCert, face, keychain);
Add a new decryptor and its corresponding attribute list into authority:
// obtain the decryptor's certificate for CP-ABE
std::list<std::string> attrList = {"ucla", "professor"};
aa.addNewPolicy(decryptorCertificate, attrList);
// obtain the decryptor's certificate for KP-ABE
String policy = "ucla and cs and (exam or quiz);
aa.addNewPolicy(decryptorCertificate, policy);
After starting an attribute authority and added policies for decryptors,
the attribute authority will listen to the prefix /<attribute authority prefix>/DKEY
to answer possible attribute request from decryptors.
When a request arrives, the attribute authority will first use a known decryptor cetificate to verify the request, then locates the attribtue lists of this decryptor.
After that, the attribute authority will create a new AES key to cpEncrypt the attributes, then use the decryptor's RSA public key from the certificate to cpEncrypt the AES key.
The encrypted attributes will be returned back to the decryptor.
// obtain or create a certificate for data owner
DataOwner dataOwner = DataOwner(dataOwnerCert, face, keychain);
Command a data producer to apply certain policy when producing certain Data packets.
//for CP-ABE
dataOwner.commandProducerPolicy(Name("/producer"), Name("/healthdata"), "ucla and professor", successCallback, failCallback);
//for KP-ABE
dataOwner.commandProducerPolicy(Name("/producer"), Name("/healthdata"), {"ucla", "cs", "exam"}, successCallback, failCallback);
To command a data producer, the data owner will use its own private key to sign the command Interest.
The command Interest is of format: /<producer prefix>/SET_POLICY/<data prefix block>/<policy string>
.
Producer producer = Producer(face, keychain, producerCert, aaCert, dataOwnerCert);
After starting a encryptor, the encryptor will automatically fetch the public parameters from the attribute authority.
After starting a encryptor, the encryptor will listen to the prefix /<producer prefix>/SET_POLICY
for the data owner to command the policy.
When a command Interest arrives, the encryptor will verify the command Interest with the data owner's certificate.
std::shared_ptr<Data> contentData, ckData;
std::tie(contentData, ckData) = producer.produce(dataName, PLAIN_TEXT);
This function will automatically find the policy that is previously obtained from the command issued by the data owner.
The encryptor can also produce a new data using a new policy:
std::shared_ptr<Data> contentData, ckData;
// for CP-ABE
std::tie(contentData, ckData) = producer.produce(dataName, "ucla and professor", PLAIN_TEXT);
// for KP-ABE
std::tie(contentData, ckData) = producer.produce(dataName, {"ucla", "cs", "exam"}, PLAIN_TEXT);
To produce multiple data under the same content key (thus the same policy), use the content key generation API:
std::shared_ptr<ContentKey> contentKey;
std::shared_ptr<Data> ckData;
// for CP-ABE
std::tie(contentKey, ckData) = producer.ckDataGen(dataName, "ucla and professor", PLAIN_TEXT);
// for KP-ABE
std::tie(contentKey, ckData) = producer.ckDataGen(dataName, {"ucla","cs","exam"}, PLAIN_TEXT);
Then, for each Data:
std::shared_ptr<Data> contentData;
// for CP-ABE
contentData = producer.produce(contentKey, ckData.getName(), dataName, "ucla and professor", PLAIN_TEXT);
// for KP-ABE
contentData = producer.produce(contentKey, ckData.getName(), dataName, {"ucla","cs","exam"}, PLAIN_TEXT);
You can also use CacheProducer, which will automatically cache and reuse all the keys generated.
Consumer consumer = Consumer(face, keyChain, consumerCert, aaCert);
After starting a decryptor, the decryptor will automatically fetch the public parameters from the attribute authority.
consumer.obtainDecryptionKey();
This function will fetch the DKEY from the attribute authority.
consumer.consume(dataName, successCallback, failCallback);
This function will fetch the content Data packet by the name, fetch the corresponding encrypted CK Data packet. Then it uses its decryption key (DKEY) to cpDecrypt the CK, then use the CK to cpDecrypt the content Data packet.
If you have any problems or want to do bug report. Please submit a GitHub issue.