Skip to content

Commit

Permalink
programs: ndncertclient possession challenge
Browse files Browse the repository at this point in the history
  • Loading branch information
yoursunny committed Dec 5, 2020
1 parent 65521fb commit c54a769
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 57 deletions.
7 changes: 7 additions & 0 deletions programs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@

This directory contains several self-contained programs using NDNph.
All commands accept `-h` command line flag for usage instructions.

Environment variables:

* `NDNPH_UPLINK_UDP`: IPv4 address of the uplink router.
Default to `127.0.0.1`.
* `NDNPH_KEYCHAIN`: filesystem path of KeyChain storage.
This is required in programs that access the persistent KeyChain.
14 changes: 13 additions & 1 deletion programs/cli-common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ openKeyChain()
if (!ready) {
const char* env = getenv("NDNPH_KEYCHAIN");
if (env == nullptr) {
fprintf(stderr, "NDNPH_KEYCHAIN environment variable missing\n");
fprintf(stderr,
"KeyChain path missing: set NDNPH_KEYCHAIN=/path/to/keychain environment variable\n");
exit(1);
}

Expand Down Expand Up @@ -79,6 +80,17 @@ checkKeyChainId(const std::string& id)
return id;
}

/** @brief Load a key from the KeyChain. */
inline void
loadKey(ndnph::Region& region, const std::string& id, ndnph::EcPrivateKey& pvt,
ndnph::EcPublicKey& pub)
{
if (!ndnph::ec::load(openKeyChain(), id.data(), region, pvt, pub)) {
fprintf(stderr, "Key [%s] not found in KeyChain\n", id.data());
exit(1);
}
}

/** @brief Load a certificate from the KeyChain. */
inline ndnph::Data
loadCertificate(ndnph::Region& region, const std::string& id)
Expand Down
18 changes: 6 additions & 12 deletions programs/keychain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ keygen(int argc, char** argv)
bool ok = ndnph::ec::generate(region, name, pvt, pub, keyChain, (id + "_key").data());
if (!ok) {
fprintf(stderr, "EC generate error\n");
exit(4);
exit(1);
}

auto cert = pub.selfSign(region, ndnph::ValidityPeriod::getMax(), pvt);
if (!keyChain.certs.set((id + "_cert").data(), cert, region)) {
fprintf(stderr, "Save certificate error\n");
exit(4);
exit(1);
}

cli_common::output(cert);
Expand Down Expand Up @@ -69,10 +69,7 @@ certsign(int argc, char** argv)

ndnph::EcPrivateKey issuerPvt;
ndnph::EcPublicKey issuerPub;
if (!ndnph::ec::load(keyChain, (id + "_key").data(), region, issuerPvt, issuerPub)) {
fprintf(stderr, "Issuer key not found\n");
exit(4);
}
cli_common::loadKey(region, id + "_key", issuerPvt, issuerPub);

ndnph::EcPublicKey pub;
cli_common::inputCertificate(region, &pub);
Expand All @@ -96,7 +93,7 @@ certimport(int argc, char** argv)
auto cert = cli_common::inputCertificate(region, nullptr);
if (!keyChain.certs.set((id + "_cert").data(), cert, region)) {
fprintf(stderr, "Save certificate error\n");
exit(4);
exit(1);
}
return true;
}
Expand Down Expand Up @@ -138,18 +135,15 @@ usage()
" Issue certificate, signing with private key ID.\n"
"\n"
"ndnph-keychain certimport ID < issued-cert.data \n"
" Install certificate to ID.\n"
"\n"
"Required environment variable: NDNPH_KEYCHAIN=/path/to/keychain\n"
"ID can only have digits and lower case letters.\n");
" Install certificate to ID.\n");
}

int
main(int argc, char** argv)
{
if (!execute(argc, argv)) {
usage();
return 2;
return 1;
}
return 0;
}
77 changes: 39 additions & 38 deletions programs/ndncertclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,40 @@

ndnph::StaticRegion<65536> region;
ndnph::Face& face = cli_common::openUplink();
ndnph::KeyChain& keyChain = cli_common::openKeyChain();

const char* profileFilename = nullptr;
ndnph::Component identitySuffix;
std::string identitySlot;
std::string possessionSlot;

ndnph::ndncert::client::CaProfile profile;
ndnph::EcPrivateKey myPvt;
ndnph::EcPublicKey myPub;
ndnph::EcPrivateKey possessionPvt;
bool running = true;

static bool
parseArgs(int argc, char** argv)
{
int c;
while ((c = getopt(argc, argv, "P:s:")) != -1) {
while ((c = getopt(argc, argv, "P:i:E:")) != -1) {
switch (c) {
case 'P': {
profileFilename = optarg;
break;
}
case 's': {
identitySuffix = ndnph::Component::parse(region, optarg);
if (!identitySuffix) {
return false;
}
case 'i': {
identitySlot = cli_common::checkKeyChainId(optarg);
break;
}
case 'E': {
possessionSlot = cli_common::checkKeyChainId(optarg);
break;
}
}
}

return argc - optind == 0 && profileFilename != nullptr;
return argc - optind == 0 && profileFilename != nullptr && !identitySlot.empty();
}

static bool
Expand All @@ -58,58 +62,55 @@ loadCaProfile()
return profile.fromData(region, data);
}

static bool
makeMyKeyPair()
{
ndnph::StaticRegion<2048> temp;
ndnph::Name identity = profile.prefix.getPrefix(-1);
if (!!identitySuffix) {
identity = identity.append(temp, identitySuffix);
} else {
identity = identity.append<ndnph::convention::Timestamp>(temp, ndnph::convention::TimeValue());
}
assert(!!identity);

return ndnph::ec::generate(region, identity, myPvt, myPub);
}

static void
clientCallback(void*, ndnph::Data cert)
{
running = false;
if (!cert) {
exit(5);
exit(1);
}
std::cout << cert.getName() << std::endl;
cli_common::output(cert);
}

int
main(int argc, char** argv)
{
if (!parseArgs(argc, argv)) {
fprintf(stderr,
"ndnph-ndncertclient -P CA-PROFILE [-s IDENTITY-SUFFIX]\n"
" CA-PROFILE is a CA profile filename\n"
" IDENTITY-SUFFIX is the last component of requested identity\n"
"Note: this program demonstrates protocol operations but cannot persist keys\n");
return 2;
fprintf(stderr, "ndnph-ndncertclient -P CA-PROFILE -i IDENTITY-ID [-E POSSESSION-ID]\n"
" CA-PROFILE is a CA profile filename.\n"
" IDENTITY-ID is a KeyChain slot of the requester keypair.\n"
" Generate a keypair with the 'ndnph-keychain keygen' command.\n"
" -E enables proof of possession challenge.\n"
" POSSESSION-ID is a KeyChain slot for the existing keypair.\n"
"\n"
" New certificate from NDNCERT is written to stdout.\n"
" Import to the KeyChain with the 'ndnph-keychain certimport' command.\n");
return 1;
}

if (!loadCaProfile()) {
return 4;
fprintf(stderr, "Error loading CA profile\n");
return 1;
}
std::cout << profile.prefix << std::endl;
cli_common::loadKey(region, identitySlot + "_key", myPvt, myPub);

if (!makeMyKeyPair()) {
return 5;
ndnph::ndncert::client::ChallengeList challenges{};
ndnph::ndncert::client::NopChallenge nopChallenge;
challenges[0] = &nopChallenge;
std::unique_ptr<ndnph::ndncert::client::PossessionChallenge> possessionChallenge;
if (!possessionSlot.empty()) {
ndnph::EcPublicKey possessionPub;
cli_common::loadKey(region, possessionSlot + "_key", possessionPvt, possessionPub);
auto possessionCert = cli_common::loadCertificate(region, possessionSlot + "_cert");
possessionChallenge.reset(
new ndnph::ndncert::client::PossessionChallenge(possessionCert, possessionPvt));
challenges[1] = possessionChallenge.get();
}
std::cout << myPvt.getName() << std::endl;

ndnph::ndncert::client::NopChallenge nopChallenge;
ndnph::ndncert::Client::requestCertificate({
.face = face,
.profile = profile,
.challenges = { &nopChallenge },
.challenges = challenges,
.pub = myPub,
.pvt = myPvt,
.cb = clientCallback,
Expand Down
53 changes: 53 additions & 0 deletions programs/ndncertclient.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# ndnph-ndncertclient

Here are some example transactions using CA implementation from NDNts `@ndn/keychain-cli` package.

## "nop" challenge

```bash
export NDNTS_KEYCHAIN=/tmp/ca-keychain
export NDNTS_NFDREG=1
export NDNPH_KEYCHAIN=/tmp/req-keychain
nfd-start
# The CA host must have NFD.
# If the client is on a different host, use NDNPH_UPLINK_UDP environ to connect to the NFD
# on the CA host, or install NFD locally and add routes for /authority and /requester prefixes.

# generate CA key
CACERT=$(ndntssec gen-key /authority)

# make CA profile
ndntssec ndncert03-make-profile --out /tmp/ca.data --prefix /authority/CA --cert $CACERT --valid-days 60

# start CA with "nop" challenge
ndntssec ndncert03-ca --profile /tmp/ca.data --store /tmp/ca-repo --challenge nop

# generate key pair
ndnph-keychain keygen k0 /requester/0 >/dev/null

# request certificate via NDNCERT
ndnph-ndncertclient -P /tmp/ca.data -i k0 >/tmp/k0-cert.data

# import and display the certificate
ndnph-keychain certimport k0 </tmp/k0-cert.data
ndnph-keychain certinfo k0
```

## "possession" challenge

```bash
# first complete all steps in the "nop" challenge

# start CA with "possession" challenge, accepting existing certificates issued by the same CA
ndntssec ndncert03-ca --profile /tmp/ca.data --store /tmp/ca-repo --challenge possession

# generate key pair
ndnph-keychain keygen k1 /requester/1 >/dev/null

# request certificate via NDNCERT
ndnph-ndncertclient -P /tmp/ca.data -i k1 -E k0 >/tmp/k1-cert.data

# import and display the certificate
ndnph-keychain certimport k1 </tmp/k1-cert.data
ndnph-keychain certinfo k1
```
10 changes: 4 additions & 6 deletions programs/pingclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@ main(int argc, char** argv)
{
if (!parseArgs(argc, argv)) {
fprintf(stderr, "ndnph-pingclient [-i INTERVAL] PREFIX\n"
" PREFIX should have 'ping' suffix to interact with ndn-tools ndnpingserver\n"
" INTERVAL must be between 1 and 60000 milliseconds\n"
" INTERVAL should be no less than RTT, or all requests will timeout\n"
"\n"
"Optional environment variable: NDNPH_UPLINK_UDP=192.0.2.1\n");
return 2;
" PREFIX should have 'ping' suffix to interact with ndn-tools ndnpingserver.\n"
" INTERVAL must be between 1 and 60000 milliseconds.\n"
" INTERVAL should be no less than RTT, or all requests will timeout.\n");
return 1;
}

for (;;) {
Expand Down

0 comments on commit c54a769

Please sign in to comment.