blob: a8f3abeaface007544eb9f14da65a5223e7ef848 [file] [log] [blame]
Zhiyi Zhang08e0e982017-03-01 10:10:42 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoe5b43692021-11-15 22:05:03 -05002/*
Davide Pesavento3e481842023-11-11 18:01:32 -05003 * Copyright (c) 2017-2023, Regents of the University of California.
Zhiyi Zhang08e0e982017-03-01 10:10:42 -08004 *
5 * This file is part of ndncert, a certificate management system based on NDN.
6 *
7 * ndncert is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License along with
16 * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ndncert authors and contributors.
19 */
20
tylerliu4140fe82021-01-27 15:45:44 -080021#include "requester-request.hpp"
Davide Pesaventoe5b43692021-11-15 22:05:03 -050022
Davide Pesavento0dc02012021-11-23 22:55:03 -050023#include <ndn-cxx/face.hpp>
24#include <ndn-cxx/security/key-chain.hpp>
Zhiyi Zhang90c75782020-10-06 15:04:03 -070025#include <ndn-cxx/security/verification-helpers.hpp>
Davide Pesaventoe5b43692021-11-15 22:05:03 -050026
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -070027#include <boost/asio.hpp>
Davide Pesaventob48bbda2020-07-27 19:41:37 -040028#include <boost/program_options/options_description.hpp>
29#include <boost/program_options/parsers.hpp>
30#include <boost/program_options/variables_map.hpp>
Davide Pesaventoe5b43692021-11-15 22:05:03 -050031
Zhiyi Zhang48f23782020-09-28 12:11:24 -070032#include <iostream>
Zhiyi Zhang48f23782020-09-28 12:11:24 -070033
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040034namespace ndncert::requester {
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080035
Zhiyi Zhang48f23782020-09-28 12:11:24 -070036static void
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040037selectCaProfile(const std::string& configFilePath);
Davide Pesaventoe5b43692021-11-15 22:05:03 -050038
tylerliufeabfdc2020-10-03 15:09:58 -070039static void
40runProbe(CaProfile profile);
Davide Pesaventoe5b43692021-11-15 22:05:03 -050041
tylerliufeabfdc2020-10-03 15:09:58 -070042static void
43runNew(CaProfile profile, Name identityName);
Davide Pesaventoe5b43692021-11-15 22:05:03 -050044
tylerliufeabfdc2020-10-03 15:09:58 -070045static void
46runChallenge(const std::string& challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070047
Davide Pesaventoe5b43692021-11-15 22:05:03 -050048static size_t nStep = 1;
Tianyuan Yuca23bb02022-03-09 14:09:14 -080049static const std::string defaultChallenge = "email";
Davide Pesavento0dc02012021-11-23 22:55:03 -050050static ndn::Face face;
51static ndn::KeyChain keyChain;
52static std::shared_ptr<Request> requesterState;
Tianyuan Yuca23bb02022-03-09 14:09:14 -080053static std::shared_ptr<std::multimap<std::string, std::string>> capturedProbeParams;
Tianyuan Yu48903c22022-05-02 15:30:07 -070054static std::shared_ptr<Certificate> trustedCert;
Tianyuan Yuca23bb02022-03-09 14:09:14 -080055static Name newlyCreatedIdentityName;
56static Name newlyCreatedKeyName;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080057
Zhiyi Zhang46049832020-09-28 17:08:12 -070058static void
tylerliu40226332020-11-11 15:37:16 -080059captureParams(std::multimap<std::string, std::string>& requirement)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080060{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070061 std::list<std::string> results;
Zhiyi Zhang46049832020-09-28 17:08:12 -070062 for (auto& item : requirement) {
63 std::cerr << std::get<1>(item) << std::endl;
64 std::string captured;
65 getline(std::cin, captured);
66 std::get<1>(item) = captured;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080067 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070068 std::cerr << "Got it. This is what you've provided:" << std::endl;
Zhiyi Zhang46049832020-09-28 17:08:12 -070069 for (const auto& item : requirement) {
70 std::cerr << std::get<0>(item) << " : " << std::get<1>(item) << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080071 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070072}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080073
tylerliu40226332020-11-11 15:37:16 -080074static std::multimap<std::string, std::string>
Zhiyi Zhang2de4ea32020-12-13 17:45:25 -080075captureParams(const std::vector<std::string>& requirement)
Zhiyi Zhang547c8512019-06-18 23:46:14 -070076{
tylerliu40226332020-11-11 15:37:16 -080077 std::multimap<std::string, std::string> results;
tylerliufeabfdc2020-10-03 15:09:58 -070078 for (const auto& r : requirement) {
tylerliu40226332020-11-11 15:37:16 -080079 results.emplace(r, "Please input: " + r);
Zhiyi Zhang547c8512019-06-18 23:46:14 -070080 }
tylerliufeabfdc2020-10-03 15:09:58 -070081 captureParams(results);
Zhiyi Zhang547c8512019-06-18 23:46:14 -070082 return results;
83}
84
tylerliufeabfdc2020-10-03 15:09:58 -070085static int
Tianyuan Yuca23bb02022-03-09 14:09:14 -080086captureValidityPeriod(time::hours maxValidityPeriod)
Zhiyi Zhang36706832019-07-04 21:33:03 -070087{
Zhiyi Zhang837406d2020-10-05 22:01:31 -070088 std::cerr << "\n***************************************\n"
89 << "Step " << nStep++
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -070090 << ": Please type in your expected validity period of your certificate."
91 << " Type the number of hours (168 for week, 730 for month, 8760 for year)."
Tianyuan Yuca23bb02022-03-09 14:09:14 -080092 << " The CA may reject your application if your expected period is too long."
93 << " The maximum validity period allowed by this CA is " << maxValidityPeriod << "."<< std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -080094 size_t count = 0;
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040095 while (count < 3) {
tylerliufeabfdc2020-10-03 15:09:58 -070096 std::string periodStr = "";
97 getline(std::cin, periodStr);
98 try {
Zhiyi Zhang837406d2020-10-05 22:01:31 -070099 return std::stoul(periodStr);
tylerliufeabfdc2020-10-03 15:09:58 -0700100 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500101 catch (const std::exception&) {
tylerliufeabfdc2020-10-03 15:09:58 -0700102 std::cerr << "Your input is invalid. Try again: " << std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800103 count++;
tylerliufeabfdc2020-10-03 15:09:58 -0700104 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700105 }
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800106 std::cerr << "Invalid input for too many times, exit. " << std::endl;
107 exit(1);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700108}
109
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800110static Name
111captureKeyName(ndn::security::pib::Identity& identity, ndn::security::pib::Key& defaultKey)
112{
113 size_t count = 0;
114 std::cerr << "***************************************\n"
115 << "Step " << nStep++ << ": KEY SELECTION" << std::endl;
116 for (const auto& key : identity.getKeys()) {
117 std::cerr << "> Index: " << count++ << std::endl
118 << ">> Key Name:";
119 if (key == defaultKey) {
120 std::cerr << " +->* ";
121 }
122 else {
123 std::cerr << " +-> ";
124 }
125 std::cerr << key.getName() << std::endl;
126 }
127
128 std::cerr << "Please type in the key's index that you want to certify or type in NEW if you want to certify a new key:\n";
129 std::string indexStr = "";
130 std::string indexStrLower = "";
131 size_t keyIndex;
132 getline(std::cin, indexStr);
133
134 indexStrLower = indexStr;
135 boost::algorithm::to_lower(indexStrLower);
136 if (indexStrLower == "new") {
137 auto newlyCreatedKey = keyChain.createKey(identity);
138 newlyCreatedKeyName = newlyCreatedKey.getName();
139 std::cerr << "New key generated: " << newlyCreatedKeyName << std::endl;
140 return newlyCreatedKeyName;
141 }
142 else {
143 try {
144 keyIndex = std::stoul(indexStr);
145 }
146 catch (const std::exception&) {
147 std::cerr << "Your input is neither NEW nor a valid index. Exit" << std::endl;
148 exit(1);
149 }
150
151 if (keyIndex >= count) {
152 std::cerr << "Your input is not an existing index. Exit" << std::endl;
153 exit(1);
154 }
155 else {
156 auto itemIterator = identity.getKeys().begin();
157 std::advance(itemIterator, keyIndex);
158 auto targetKeyItem = *itemIterator;
159 return targetKeyItem.getName();
160 }
161 }
162}
163
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400164[[noreturn]] static void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700165onNackCb()
166{
167 std::cerr << "Got NACK\n";
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800168 exit(1);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700169}
170
171static void
172timeoutCb()
173{
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400174 std::cerr << "Interest timeout\n";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700175}
176
177static void
swa770cf1d8f72020-04-21 23:12:39 -0700178certFetchCb(const Data& reply)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700179{
tylerliu4140fe82021-01-27 15:45:44 -0800180 auto item = Request::onCertFetchResponse(reply);
tylerliufeabfdc2020-10-03 15:09:58 -0700181 if (item) {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700182 keyChain.addCertificate(keyChain.getPib().getIdentity(item->getIdentity()).getKey(item->getKeyName()), *item);
tylerliufeabfdc2020-10-03 15:09:58 -0700183 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700184 std::cerr << "\n***************************************\n"
185 << "Step " << nStep++
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800186 << ": DONE\nCertificate with Name: " << reply.getName()
187 << " has been installed to your local keychain\n"
188 << "Exit now" << std::endl;
Davide Pesavento3e481842023-11-11 18:01:32 -0500189 face.getIoContext().stop();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190}
191
192static void
193challengeCb(const Data& reply)
194{
tylerliufeabfdc2020-10-03 15:09:58 -0700195 try {
tylerliu4140fe82021-01-27 15:45:44 -0800196 requesterState->onChallengeResponse(reply);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700197 }
198 catch (const std::exception& e) {
tylerliufeabfdc2020-10-03 15:09:58 -0700199 std::cerr << "Error when decoding challenge step: " << e.what() << std::endl;
200 exit(1);
201 }
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800202 if (requesterState->m_status == Status::SUCCESS) {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700203 std::cerr << "Certificate has already been issued, downloading certificate..." << std::endl;
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500204 face.expressInterest(*requesterState->genCertFetchInterest(),
205 [] (const auto&, const auto& data) { certFetchCb(data); },
206 [] (auto&&...) { onNackCb(); },
207 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700208 return;
209 }
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800210 runChallenge(requesterState->m_challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700211}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800212
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700213static void
214newCb(const Data& reply)
215{
tylerliufeabfdc2020-10-03 15:09:58 -0700216 std::list<std::string> challengeList;
217 try {
tylerliu4140fe82021-01-27 15:45:44 -0800218 challengeList = requesterState->onNewRenewRevokeResponse(reply);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700219 }
220 catch (const std::exception& e) {
tylerliufeabfdc2020-10-03 15:09:58 -0700221 std::cerr << "Error on decoding NEW step reply because: " << e.what() << std::endl;
222 exit(1);
223 }
224
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700225 size_t challengeIndex = 0;
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400226 if (challengeList.empty()) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700227 std::cerr << "There is no available challenge provided by the CA. Exit" << std::endl;
tylerliufeabfdc2020-10-03 15:09:58 -0700228 exit(1);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800229 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800230
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400231 auto item = std::find(challengeList.begin(), challengeList.end(), defaultChallenge);
232 if (item != challengeList.end()) {
233 runChallenge(defaultChallenge);
234 }
235 else {
236 // default challenge not available
237 std::cerr << "\n***************************************\n"
238 << "Step " << nStep++
239 << ": CHALLENGE SELECTION" << std::endl;
240 size_t count = 0;
241 std::string choice = "";
242 for (const auto& item : challengeList) {
243 std::cerr << "> Index: " << count++ << std::endl
244 << ">> Challenge: " << item << std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800245 }
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400246 std::cerr << "Please type in the index of the challenge that you want to perform:" << std::endl;
247 size_t inputCount = 0;
248 while (inputCount < 3) {
249 getline(std::cin, choice);
250 try {
251 challengeIndex = std::stoul(choice);
252 }
253 catch (const std::exception&) {
254 std::cerr << "Your input is not valid. Try again:" << std::endl;
255 inputCount++;
256 continue;
257 }
258 if (challengeIndex >= count) {
259 std::cerr << "Your input index is out of range. Try again:" << std::endl;
260 inputCount++;
261 continue;
262 }
263 break;
264 }
265 if (inputCount == 3) {
266 std::cerr << "Invalid input for too many times, exit. " << std::endl;
267 exit(1);
268 }
269
270 auto it = challengeList.begin();
271 std::advance(it, challengeIndex);
272 std::cerr << "The challenge has been selected: " << *it << std::endl;
273 runChallenge(*it);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700274 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700275}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800276
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700277static void
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500278infoCb(const Data& reply, const Name& certFullName)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700279{
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400280 std::optional<CaProfile> profile;
tylerliufeabfdc2020-10-03 15:09:58 -0700281 try {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700282 if (certFullName.empty()) {
Tianyuan Yu48903c22022-05-02 15:30:07 -0700283 if (trustedCert && !ndn::security::verifySignature(reply, *trustedCert)) {
284 NDN_THROW(std::runtime_error("Cannot verify replied Data packet signature."));
285 }
tylerliu4140fe82021-01-27 15:45:44 -0800286 profile = Request::onCaProfileResponse(reply);
tylerliufeabfdc2020-10-03 15:09:58 -0700287 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700288 else {
tylerliu4140fe82021-01-27 15:45:44 -0800289 profile = Request::onCaProfileResponseAfterRedirection(reply, certFullName);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700290 }
291 }
292 catch (const std::exception& e) {
293 std::cerr << "The fetched CA information cannot be used because: " << e.what() << std::endl;
294 return;
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700295 }
Tianyuan Yu48903c22022-05-02 15:30:07 -0700296 if (!trustedCert)
297 {
298 std::cerr << "\n***************************************\n"
299 << "Step " << nStep++
300 << ": Will use the following CA, please double check the identity info:" << std::endl
301 << "> CA name: " << profile->caPrefix << std::endl
302 << "> This CA information is signed by: " << reply.getSignatureInfo().getKeyLocator() << std::endl
303 << "> The certificate:" << std::endl << *profile->cert << std::endl
304 << "Do you trust the information? Type in YES or NO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700305
Tianyuan Yu48903c22022-05-02 15:30:07 -0700306 std::string answer;
307 getline(std::cin, answer);
308 boost::algorithm::to_lower(answer);
309 if (answer == "yes") {
310 std::cerr << "You answered YES: new CA " << profile->caPrefix << " will be used" << std::endl;
311 trustedCert = profile->cert;
312 runProbe(*profile);
313 }
314 else {
315 std::cerr << "You answered NO: new CA " << profile->caPrefix << " will not be used" << std::endl;
316 exit(0);
317 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700318 }
319 else {
Tianyuan Yu48903c22022-05-02 15:30:07 -0700320 runProbe(*profile);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700321 }
322}
323
324static void
tylerliufeabfdc2020-10-03 15:09:58 -0700325probeCb(const Data& reply, CaProfile profile)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700326{
tylerliub47dad72020-10-08 21:36:55 -0700327 std::vector<std::pair<Name, int>> names;
tylerliufeabfdc2020-10-03 15:09:58 -0700328 std::vector<Name> redirects;
tylerliufeabfdc2020-10-03 15:09:58 -0700329 try {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800330 Request::onProbeResponse(reply, profile, names, redirects);
tylerliufeabfdc2020-10-03 15:09:58 -0700331 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800332 catch (const std::exception& e) {
333 std::cerr << "The probed CA response cannot be used because: " << e.what() << std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800334 exit(1);
tylerliufeabfdc2020-10-03 15:09:58 -0700335 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800336
337 size_t count = 0;
338 Name selectedName;
339 Name redirectedCaFullName;
340 // always prefer redirection over direct assignment
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400341 if (!redirects.empty()) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800342 if (redirects.size() < 2) {
343 redirectedCaFullName = redirects.front();
344 }
345 else {
346 std::cerr << "\n***************************************\n"
347 << "Step " << nStep++
348 << " Choose another trusted CA suggested by the CA: " << std::endl;
349 for (const auto& redirect : redirects) {
350 std::cerr << "> Index: " << count++ << std::endl
351 << ">> Suggested CA: " << ndn::security::extractIdentityFromCertName(redirect.getPrefix(-1))
352 << std::endl;
tylerliud82cc5c2020-12-21 23:58:39 -0800353 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800354 std::cerr << "Please type in the index of your choice:" << std::endl;
355 size_t index = 0;
356 try {
357 std::string input;
358 getline(std::cin, input);
359 index = std::stoul(input);
360 }
361 catch (const std::exception&) {
362 std::cerr << "Your input is Invalid. Exit" << std::endl;
363 exit(1);
364 }
365 if (index >= redirects.size()) {
366 std::cerr << "Your input is not an existing index. Exit" << std::endl;
367 exit(1);
368 }
369 redirectedCaFullName = redirects[index];
tylerliud82cc5c2020-12-21 23:58:39 -0800370 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500371 auto redirectedCaName = ndn::security::extractIdentityFromCertName(redirectedCaFullName.getPrefix(-1));
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800372 std::cerr << "You will be redirected to CA: " << redirectedCaName << std::endl;
Zhiyi Zhangfbcab842020-10-07 15:17:13 -0700373 face.expressInterest(
tylerliu4140fe82021-01-27 15:45:44 -0800374 *Request::genCaProfileDiscoveryInterest(redirectedCaName),
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800375 [&, redirectedCaFullName] (const auto&, const auto& data) {
tylerliu4140fe82021-01-27 15:45:44 -0800376 auto fetchingInterest = Request::genCaProfileInterestFromDiscoveryResponse(data);
Zhiyi Zhangfbcab842020-10-07 15:17:13 -0700377 face.expressInterest(*fetchingInterest,
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800378 [=] (const auto&, const auto& data2) { infoCb(data2, redirectedCaFullName); },
379 [] (auto&&...) { onNackCb(); },
380 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhangfbcab842020-10-07 15:17:13 -0700381 },
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500382 [] (auto&&...) { onNackCb(); },
383 [] (auto&&...) { timeoutCb(); });
tylerliufeabfdc2020-10-03 15:09:58 -0700384 }
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400385 else if (!names.empty()) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800386 if (names.size() < 2) {
387 selectedName = names.front().first;
388 }
389 else {
390 std::cerr << "\n***************************************\n"
391 << "Step " << nStep++
392 << ": You can either select one of the following names suggested by the CA: " << std::endl;
393 for (const auto& name : names) {
394 std::cerr << "> Index: " << count++ << std::endl
395 << ">> Suggested name: " << name.first << std::endl
396 << ">> Corresponding max suffix length: " << name.second << std::endl;
397 }
398 std::cerr << "Please type in the index of your choice:" << std::endl;
399 size_t index = 0;
400 try {
401 std::string input;
402 getline(std::cin, input);
403 index = std::stoul(input);
404 }
405 catch (const std::exception&) {
406 std::cerr << "Your input is invalid. Exit" << std::endl;
407 exit(1);
408 }
409 if (index >= names.size()) {
410 std::cerr << "Your input is not an existing index. Exit" << std::endl;
411 exit(1);
412 }
413 selectedName = names[index].first;
414 }
415 std::cerr << "You are applying for name: " << selectedName << std::endl;
416 runNew(profile, selectedName);
417 }
418 else {
419 std::cerr << "Neither name assignment nor redirection is available, exit." << std::endl;
420 exit(1);
421 }
tylerliufeabfdc2020-10-03 15:09:58 -0700422}
423
424static void
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400425selectCaProfile(const std::string& configFilePath)
tylerliufeabfdc2020-10-03 15:09:58 -0700426{
Zhiyi Zhanga16b7582020-10-29 18:59:46 -0700427 ProfileStorage profileStorage;
tylerliufeabfdc2020-10-03 15:09:58 -0700428 try {
Zhiyi Zhanga16b7582020-10-29 18:59:46 -0700429 profileStorage.load(configFilePath);
tylerliufeabfdc2020-10-03 15:09:58 -0700430 }
431 catch (const std::exception& e) {
432 std::cerr << "Cannot load the configuration file: " << e.what() << std::endl;
433 exit(1);
434 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700435 size_t count = 0;
436 std::cerr << "***************************************\n"
437 << "Step " << nStep++ << ": CA SELECTION" << std::endl;
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400438 for (const auto& item : profileStorage.getKnownProfiles()) {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700439 std::cerr << "> Index: " << count++ << std::endl
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800440 << ">> CA prefix:" << item.caPrefix << std::endl
441 << ">> Introduction: " << item.caInfo << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700442 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700443 std::cerr << "Please type in the CA's index that you want to apply or type in NONE if your expected CA is not in the list:\n";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700444
Zhiyi Zhang36706832019-07-04 21:33:03 -0700445 std::string caIndexS, caIndexSLower;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700446 getline(std::cin, caIndexS);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700447 caIndexSLower = caIndexS;
448 boost::algorithm::to_lower(caIndexSLower);
Tianyuan Yu48903c22022-05-02 15:30:07 -0700449 Name selectedCaName;
Zhiyi Zhang36706832019-07-04 21:33:03 -0700450 if (caIndexSLower == "none") {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700451 std::cerr << "\n***************************************\n"
452 << "Step " << nStep << ": ADD NEW CA\nPlease type in the CA's Name:" << std::endl;
Tianyuan Yu48903c22022-05-02 15:30:07 -0700453 std::string targetCaName;
454 getline(std::cin, targetCaName);
455 try {
456 selectedCaName = Name(targetCaName);
457 }
458 catch (const std::exception&) {
459 std::cerr << "Your input is not a valid name. Exit" << std::endl;
460 exit(1);
461 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700462 }
463 else {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700464 size_t caIndex;
Zhiyi Zhang36706832019-07-04 21:33:03 -0700465 try {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700466 caIndex = std::stoul(caIndexS);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700467 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500468 catch (const std::exception&) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700469 std::cerr << "Your input is neither NONE nor a valid index. Exit" << std::endl;
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800470 exit(1);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700471 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700472 if (caIndex >= count) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700473 std::cerr << "Your input is not an existing index. Exit" << std::endl;
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800474 exit(1);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700475 }
Zhiyi Zhang84e11842020-11-19 20:03:23 -0800476 auto itemIterator = profileStorage.getKnownProfiles().cbegin();
tylerliufeabfdc2020-10-03 15:09:58 -0700477 std::advance(itemIterator, caIndex);
478 auto targetCaItem = *itemIterator;
Tianyuan Yu48903c22022-05-02 15:30:07 -0700479 trustedCert = targetCaItem.cert;
480 selectedCaName = targetCaItem.caPrefix;
tylerliufeabfdc2020-10-03 15:09:58 -0700481 }
Tianyuan Yu48903c22022-05-02 15:30:07 -0700482 face.expressInterest(
483 *Request::genCaProfileDiscoveryInterest(selectedCaName),
484 [&] (const auto&, const auto& data) {
485 auto fetchingInterest = Request::genCaProfileInterestFromDiscoveryResponse(data);
486 face.expressInterest(*fetchingInterest,
487 [] (const auto&, const auto& data2) { infoCb(data2, {}); },
488 [] (auto&&...) { onNackCb(); },
489 [] (auto&&...) { timeoutCb(); });
490 },
491 [] (auto&&...) { onNackCb(); },
492 [] (auto&&...) { timeoutCb(); });
tylerliufeabfdc2020-10-03 15:09:58 -0700493}
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700494
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700495static void
496runProbe(CaProfile profile)
tylerliufeabfdc2020-10-03 15:09:58 -0700497{
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800498 if (!capturedProbeParams) {
499 std::cerr << "\n***************************************\n"
500 << "Step " << nStep++ << ": Please provide information for name assignment" << std::endl;
501 auto captured = captureParams(profile.probeParameterKeys);
Tianyuan Yub5d3a4f2022-03-11 17:03:24 -0800502 auto it = captured.find(defaultChallenge);
503 if (it != captured.end()) {
504 it->second = boost::algorithm::to_lower_copy(it->second);
505 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800506 capturedProbeParams = std::make_shared<std::multimap<std::string, std::string>>(captured);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700507 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800508 face.expressInterest(*Request::genProbeInterest(profile, std::move(*capturedProbeParams)),
509 [profile] (const auto&, const auto& data) { probeCb(data, profile); },
510 [] (auto&&...) { onNackCb(); },
511 [] (auto&&...) { timeoutCb(); });
tylerliufeabfdc2020-10-03 15:09:58 -0700512}
513
514static void
515runNew(CaProfile profile, Name identityName)
516{
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800517 int validityPeriod = captureValidityPeriod(time::duration_cast<time::hours>(profile.maxValidityPeriod));
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700518 auto now = time::system_clock::now();
519 std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
tylerliu4140fe82021-01-27 15:45:44 -0800520 requesterState = std::make_shared<Request>(keyChain, profile, RequestType::NEW);
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800521
522 // generate a newly key pair or choose an existing key
523 const auto& pib = keyChain.getPib();
524 ndn::security::pib::Identity identity;
525 ndn::security::pib::Key defaultKey;
526 try {
527 identity = pib.getIdentity(identityName);
528 }
529 catch (const ndn::security::Pib::Error&) {
530 identity = keyChain.createIdentity(identityName);
531 newlyCreatedIdentityName = identity.getName();
532 }
533 try {
534 defaultKey = identity.getDefaultKey();
535 }
536 catch (const ndn::security::Pib::Error&) {
537 defaultKey = keyChain.createKey(identity);
538 newlyCreatedKeyName = defaultKey.getName();
539 }
540
541 Name keyName;
542 if (newlyCreatedIdentityName.empty()) {
543 keyName = captureKeyName(identity, defaultKey);
544 }
545 else {
546 keyName = defaultKey.getName();
547 }
548 auto interest = requesterState->genNewInterest(keyName, now, now + time::hours(validityPeriod));
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700549 if (interest != nullptr) {
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500550 face.expressInterest(*interest,
551 [] (const auto&, const auto& data) { newCb(data); },
552 [] (auto&&...) { onNackCb(); },
553 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700554 }
555 else {
556 std::cerr << "Cannot generate the Interest for NEW step. Exit" << std::endl;
557 }
tylerliufeabfdc2020-10-03 15:09:58 -0700558}
559
560static void
561runChallenge(const std::string& challengeType)
562{
tylerliu40226332020-11-11 15:37:16 -0800563 std::multimap<std::string, std::string> requirement;
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700564 try {
tylerliu4140fe82021-01-27 15:45:44 -0800565 requirement = requesterState->selectOrContinueChallenge(challengeType);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700566 }
567 catch (const std::exception& e) {
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500568 std::cerr << "Error. Cannot successfully load the Challenge Module with error: " << e.what()
569 << "\nExit." << std::endl;
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700570 exit(1);
571 }
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400572 if (!requirement.empty()) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800573 if (requesterState->m_status == Status::BEFORE_CHALLENGE && challengeType == defaultChallenge) {
574 requirement.find(challengeType)->second = capturedProbeParams->find(defaultChallenge)->second;
575 }
576 else {
577 std::cerr << "\n***************************************\n"
578 << "Step " << nStep
579 << ": Please provide parameters used for Identity Verification Challenge" << std::endl;
580 captureParams(requirement);
581 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700582 }
tylerliu4140fe82021-01-27 15:45:44 -0800583 face.expressInterest(*requesterState->genChallengeInterest(std::move(requirement)),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500584 [] (const auto&, const auto& data) { challengeCb(data); },
585 [] (auto&&...) { onNackCb(); },
586 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700587}
588
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700589static void
590handleSignal(const boost::system::error_code& error, int signalNum)
591{
592 if (error) {
593 return;
594 }
595 const char* signalName = ::strsignal(signalNum);
596 std::cerr << "Exiting on signal ";
597 if (signalName == nullptr) {
598 std::cerr << signalNum;
599 }
600 else {
601 std::cerr << signalName;
602 }
603 std::cerr << std::endl;
tylerliufeabfdc2020-10-03 15:09:58 -0700604 if (requesterState) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800605 if (!newlyCreatedIdentityName.empty()) {
606 auto identity = keyChain.getPib().getIdentity(newlyCreatedIdentityName);
607 keyChain.deleteIdentity(identity);
608 }
609 else if (!newlyCreatedKeyName.empty()) {
610 auto identity = keyChain.getPib().getIdentity(ndn::security::extractIdentityFromKeyName(newlyCreatedKeyName));
611 keyChain.deleteKey(identity, identity.getKey(newlyCreatedKeyName));
612 }
tylerliufeabfdc2020-10-03 15:09:58 -0700613 }
Davide Pesavento3e481842023-11-11 18:01:32 -0500614 face.getIoContext().stop();
tylerliufeabfdc2020-10-03 15:09:58 -0700615 exit(1);
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700616}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800617
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500618static int
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800619main(int argc, char* argv[])
620{
Davide Pesavento3e481842023-11-11 18:01:32 -0500621 boost::asio::signal_set terminateSignals(face.getIoContext());
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700622 terminateSignals.add(SIGINT);
623 terminateSignals.add(SIGTERM);
624 terminateSignals.async_wait(handleSignal);
625
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800626 namespace po = boost::program_options;
Zhiyi Zhang840afd92020-10-21 13:24:08 -0700627 std::string configFilePath = std::string(NDNCERT_SYSCONFDIR) + "/ndncert/client.conf";
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500628 po::options_description description("Usage: ndncert-client [-h] [-c FILE]\n");
629 description.add_options()
630 ("help,h", "produce help message")
631 ("config-file,c", po::value<std::string>(&configFilePath), "configuration file name")
632 ;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800633 po::positional_options_description p;
634
635 po::variables_map vm;
636 try {
637 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
638 po::notify(vm);
639 }
640 catch (const std::exception& e) {
641 std::cerr << "ERROR: " << e.what() << std::endl;
642 return 1;
643 }
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500644
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800645 if (vm.count("help") != 0) {
646 std::cerr << description << std::endl;
647 return 0;
648 }
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500649
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700650 selectCaProfile(configFilePath);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800651 face.processEvents();
652 return 0;
653}
654
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400655} // namespace ndncert::requester
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800656
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700657int
658main(int argc, char* argv[])
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800659{
Davide Pesavento0dc02012021-11-23 22:55:03 -0500660 return ndncert::requester::main(argc, argv);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800661}