blob: 01d8fba682964b92116b4c8ffb516f615fd5e3f4 [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 Pesavento9510c912024-02-25 17:50:05 -05003 * Copyright (c) 2017-2024, 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
Davide Pesavento9510c912024-02-25 17:50:05 -050027#include <boost/algorithm/string/case_conv.hpp>
28#include <boost/asio/signal_set.hpp>
Davide Pesaventob48bbda2020-07-27 19:41:37 -040029#include <boost/program_options/options_description.hpp>
30#include <boost/program_options/parsers.hpp>
31#include <boost/program_options/variables_map.hpp>
Davide Pesaventoe5b43692021-11-15 22:05:03 -050032
Zhiyi Zhang48f23782020-09-28 12:11:24 -070033#include <iostream>
Zhiyi Zhang48f23782020-09-28 12:11:24 -070034
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040035namespace ndncert::requester {
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080036
Zhiyi Zhang48f23782020-09-28 12:11:24 -070037static void
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040038selectCaProfile(const std::string& configFilePath);
Davide Pesaventoe5b43692021-11-15 22:05:03 -050039
tylerliufeabfdc2020-10-03 15:09:58 -070040static void
41runProbe(CaProfile profile);
Davide Pesaventoe5b43692021-11-15 22:05:03 -050042
tylerliufeabfdc2020-10-03 15:09:58 -070043static void
44runNew(CaProfile profile, Name identityName);
Davide Pesaventoe5b43692021-11-15 22:05:03 -050045
tylerliufeabfdc2020-10-03 15:09:58 -070046static void
47runChallenge(const std::string& challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070048
Davide Pesaventoe5b43692021-11-15 22:05:03 -050049static size_t nStep = 1;
Tianyuan Yuca23bb02022-03-09 14:09:14 -080050static const std::string defaultChallenge = "email";
Davide Pesavento0dc02012021-11-23 22:55:03 -050051static ndn::Face face;
52static ndn::KeyChain keyChain;
53static std::shared_ptr<Request> requesterState;
Tianyuan Yuca23bb02022-03-09 14:09:14 -080054static std::shared_ptr<std::multimap<std::string, std::string>> capturedProbeParams;
Tianyuan Yu48903c22022-05-02 15:30:07 -070055static std::shared_ptr<Certificate> trustedCert;
Tianyuan Yuca23bb02022-03-09 14:09:14 -080056static Name newlyCreatedIdentityName;
57static Name newlyCreatedKeyName;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080058
Zhiyi Zhang46049832020-09-28 17:08:12 -070059static void
tylerliu40226332020-11-11 15:37:16 -080060captureParams(std::multimap<std::string, std::string>& requirement)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080061{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062 std::list<std::string> results;
Zhiyi Zhang46049832020-09-28 17:08:12 -070063 for (auto& item : requirement) {
64 std::cerr << std::get<1>(item) << std::endl;
65 std::string captured;
66 getline(std::cin, captured);
67 std::get<1>(item) = captured;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080068 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070069 std::cerr << "Got it. This is what you've provided:" << std::endl;
Zhiyi Zhang46049832020-09-28 17:08:12 -070070 for (const auto& item : requirement) {
71 std::cerr << std::get<0>(item) << " : " << std::get<1>(item) << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080072 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070073}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080074
tylerliu40226332020-11-11 15:37:16 -080075static std::multimap<std::string, std::string>
Zhiyi Zhang2de4ea32020-12-13 17:45:25 -080076captureParams(const std::vector<std::string>& requirement)
Zhiyi Zhang547c8512019-06-18 23:46:14 -070077{
tylerliu40226332020-11-11 15:37:16 -080078 std::multimap<std::string, std::string> results;
tylerliufeabfdc2020-10-03 15:09:58 -070079 for (const auto& r : requirement) {
tylerliu40226332020-11-11 15:37:16 -080080 results.emplace(r, "Please input: " + r);
Zhiyi Zhang547c8512019-06-18 23:46:14 -070081 }
tylerliufeabfdc2020-10-03 15:09:58 -070082 captureParams(results);
Zhiyi Zhang547c8512019-06-18 23:46:14 -070083 return results;
84}
85
tylerliufeabfdc2020-10-03 15:09:58 -070086static int
Tianyuan Yuca23bb02022-03-09 14:09:14 -080087captureValidityPeriod(time::hours maxValidityPeriod)
Zhiyi Zhang36706832019-07-04 21:33:03 -070088{
Zhiyi Zhang837406d2020-10-05 22:01:31 -070089 std::cerr << "\n***************************************\n"
90 << "Step " << nStep++
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -070091 << ": Please type in your expected validity period of your certificate."
92 << " Type the number of hours (168 for week, 730 for month, 8760 for year)."
Tianyuan Yuca23bb02022-03-09 14:09:14 -080093 << " The CA may reject your application if your expected period is too long."
94 << " The maximum validity period allowed by this CA is " << maxValidityPeriod << "."<< std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -080095 size_t count = 0;
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040096 while (count < 3) {
tylerliufeabfdc2020-10-03 15:09:58 -070097 std::string periodStr = "";
98 getline(std::cin, periodStr);
99 try {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700100 return std::stoul(periodStr);
tylerliufeabfdc2020-10-03 15:09:58 -0700101 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500102 catch (const std::exception&) {
tylerliufeabfdc2020-10-03 15:09:58 -0700103 std::cerr << "Your input is invalid. Try again: " << std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800104 count++;
tylerliufeabfdc2020-10-03 15:09:58 -0700105 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700106 }
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800107 std::cerr << "Invalid input for too many times, exit. " << std::endl;
108 exit(1);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700109}
110
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800111static Name
112captureKeyName(ndn::security::pib::Identity& identity, ndn::security::pib::Key& defaultKey)
113{
114 size_t count = 0;
115 std::cerr << "***************************************\n"
116 << "Step " << nStep++ << ": KEY SELECTION" << std::endl;
117 for (const auto& key : identity.getKeys()) {
118 std::cerr << "> Index: " << count++ << std::endl
119 << ">> Key Name:";
120 if (key == defaultKey) {
121 std::cerr << " +->* ";
122 }
123 else {
124 std::cerr << " +-> ";
125 }
126 std::cerr << key.getName() << std::endl;
127 }
128
129 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";
130 std::string indexStr = "";
131 std::string indexStrLower = "";
132 size_t keyIndex;
133 getline(std::cin, indexStr);
134
135 indexStrLower = indexStr;
136 boost::algorithm::to_lower(indexStrLower);
137 if (indexStrLower == "new") {
138 auto newlyCreatedKey = keyChain.createKey(identity);
139 newlyCreatedKeyName = newlyCreatedKey.getName();
140 std::cerr << "New key generated: " << newlyCreatedKeyName << std::endl;
141 return newlyCreatedKeyName;
142 }
143 else {
144 try {
145 keyIndex = std::stoul(indexStr);
146 }
147 catch (const std::exception&) {
148 std::cerr << "Your input is neither NEW nor a valid index. Exit" << std::endl;
149 exit(1);
150 }
151
152 if (keyIndex >= count) {
153 std::cerr << "Your input is not an existing index. Exit" << std::endl;
154 exit(1);
155 }
156 else {
157 auto itemIterator = identity.getKeys().begin();
158 std::advance(itemIterator, keyIndex);
159 auto targetKeyItem = *itemIterator;
160 return targetKeyItem.getName();
161 }
162 }
163}
164
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400165[[noreturn]] static void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700166onNackCb()
167{
168 std::cerr << "Got NACK\n";
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800169 exit(1);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700170}
171
172static void
173timeoutCb()
174{
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400175 std::cerr << "Interest timeout\n";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700176}
177
178static void
swa770cf1d8f72020-04-21 23:12:39 -0700179certFetchCb(const Data& reply)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700180{
tylerliu4140fe82021-01-27 15:45:44 -0800181 auto item = Request::onCertFetchResponse(reply);
tylerliufeabfdc2020-10-03 15:09:58 -0700182 if (item) {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700183 keyChain.addCertificate(keyChain.getPib().getIdentity(item->getIdentity()).getKey(item->getKeyName()), *item);
tylerliufeabfdc2020-10-03 15:09:58 -0700184 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700185 std::cerr << "\n***************************************\n"
186 << "Step " << nStep++
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800187 << ": DONE\nCertificate with Name: " << reply.getName()
188 << " has been installed to your local keychain\n"
189 << "Exit now" << std::endl;
Davide Pesavento3e481842023-11-11 18:01:32 -0500190 face.getIoContext().stop();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700191}
192
193static void
194challengeCb(const Data& reply)
195{
tylerliufeabfdc2020-10-03 15:09:58 -0700196 try {
tylerliu4140fe82021-01-27 15:45:44 -0800197 requesterState->onChallengeResponse(reply);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700198 }
199 catch (const std::exception& e) {
tylerliufeabfdc2020-10-03 15:09:58 -0700200 std::cerr << "Error when decoding challenge step: " << e.what() << std::endl;
201 exit(1);
202 }
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800203 if (requesterState->m_status == Status::SUCCESS) {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700204 std::cerr << "Certificate has already been issued, downloading certificate..." << std::endl;
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500205 face.expressInterest(*requesterState->genCertFetchInterest(),
206 [] (const auto&, const auto& data) { certFetchCb(data); },
207 [] (auto&&...) { onNackCb(); },
208 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700209 return;
210 }
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800211 runChallenge(requesterState->m_challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700212}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800213
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700214static void
215newCb(const Data& reply)
216{
tylerliufeabfdc2020-10-03 15:09:58 -0700217 std::list<std::string> challengeList;
218 try {
tylerliu4140fe82021-01-27 15:45:44 -0800219 challengeList = requesterState->onNewRenewRevokeResponse(reply);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700220 }
221 catch (const std::exception& e) {
tylerliufeabfdc2020-10-03 15:09:58 -0700222 std::cerr << "Error on decoding NEW step reply because: " << e.what() << std::endl;
223 exit(1);
224 }
225
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700226 size_t challengeIndex = 0;
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400227 if (challengeList.empty()) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700228 std::cerr << "There is no available challenge provided by the CA. Exit" << std::endl;
tylerliufeabfdc2020-10-03 15:09:58 -0700229 exit(1);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800230 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800231
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400232 auto item = std::find(challengeList.begin(), challengeList.end(), defaultChallenge);
233 if (item != challengeList.end()) {
234 runChallenge(defaultChallenge);
235 }
236 else {
237 // default challenge not available
238 std::cerr << "\n***************************************\n"
239 << "Step " << nStep++
240 << ": CHALLENGE SELECTION" << std::endl;
241 size_t count = 0;
242 std::string choice = "";
243 for (const auto& item : challengeList) {
244 std::cerr << "> Index: " << count++ << std::endl
245 << ">> Challenge: " << item << std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800246 }
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400247 std::cerr << "Please type in the index of the challenge that you want to perform:" << std::endl;
248 size_t inputCount = 0;
249 while (inputCount < 3) {
250 getline(std::cin, choice);
251 try {
252 challengeIndex = std::stoul(choice);
253 }
254 catch (const std::exception&) {
255 std::cerr << "Your input is not valid. Try again:" << std::endl;
256 inputCount++;
257 continue;
258 }
259 if (challengeIndex >= count) {
260 std::cerr << "Your input index is out of range. Try again:" << std::endl;
261 inputCount++;
262 continue;
263 }
264 break;
265 }
266 if (inputCount == 3) {
267 std::cerr << "Invalid input for too many times, exit. " << std::endl;
268 exit(1);
269 }
270
271 auto it = challengeList.begin();
272 std::advance(it, challengeIndex);
273 std::cerr << "The challenge has been selected: " << *it << std::endl;
274 runChallenge(*it);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700275 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700276}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800277
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700278static void
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500279infoCb(const Data& reply, const Name& certFullName)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700280{
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400281 std::optional<CaProfile> profile;
tylerliufeabfdc2020-10-03 15:09:58 -0700282 try {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700283 if (certFullName.empty()) {
Tianyuan Yu48903c22022-05-02 15:30:07 -0700284 if (trustedCert && !ndn::security::verifySignature(reply, *trustedCert)) {
285 NDN_THROW(std::runtime_error("Cannot verify replied Data packet signature."));
286 }
tylerliu4140fe82021-01-27 15:45:44 -0800287 profile = Request::onCaProfileResponse(reply);
tylerliufeabfdc2020-10-03 15:09:58 -0700288 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700289 else {
tylerliu4140fe82021-01-27 15:45:44 -0800290 profile = Request::onCaProfileResponseAfterRedirection(reply, certFullName);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700291 }
292 }
293 catch (const std::exception& e) {
294 std::cerr << "The fetched CA information cannot be used because: " << e.what() << std::endl;
295 return;
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700296 }
Tianyuan Yu48903c22022-05-02 15:30:07 -0700297 if (!trustedCert)
298 {
299 std::cerr << "\n***************************************\n"
300 << "Step " << nStep++
301 << ": Will use the following CA, please double check the identity info:" << std::endl
302 << "> CA name: " << profile->caPrefix << std::endl
303 << "> This CA information is signed by: " << reply.getSignatureInfo().getKeyLocator() << std::endl
304 << "> The certificate:" << std::endl << *profile->cert << std::endl
305 << "Do you trust the information? Type in YES or NO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700306
Tianyuan Yu48903c22022-05-02 15:30:07 -0700307 std::string answer;
308 getline(std::cin, answer);
309 boost::algorithm::to_lower(answer);
310 if (answer == "yes") {
311 std::cerr << "You answered YES: new CA " << profile->caPrefix << " will be used" << std::endl;
312 trustedCert = profile->cert;
313 runProbe(*profile);
314 }
315 else {
316 std::cerr << "You answered NO: new CA " << profile->caPrefix << " will not be used" << std::endl;
317 exit(0);
318 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700319 }
320 else {
Tianyuan Yu48903c22022-05-02 15:30:07 -0700321 runProbe(*profile);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700322 }
323}
324
325static void
tylerliufeabfdc2020-10-03 15:09:58 -0700326probeCb(const Data& reply, CaProfile profile)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700327{
tylerliub47dad72020-10-08 21:36:55 -0700328 std::vector<std::pair<Name, int>> names;
tylerliufeabfdc2020-10-03 15:09:58 -0700329 std::vector<Name> redirects;
tylerliufeabfdc2020-10-03 15:09:58 -0700330 try {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800331 Request::onProbeResponse(reply, profile, names, redirects);
tylerliufeabfdc2020-10-03 15:09:58 -0700332 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800333 catch (const std::exception& e) {
334 std::cerr << "The probed CA response cannot be used because: " << e.what() << std::endl;
Zhiyi Zhang87ded732021-01-08 14:05:24 -0800335 exit(1);
tylerliufeabfdc2020-10-03 15:09:58 -0700336 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800337
338 size_t count = 0;
339 Name selectedName;
340 Name redirectedCaFullName;
341 // always prefer redirection over direct assignment
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400342 if (!redirects.empty()) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800343 if (redirects.size() < 2) {
344 redirectedCaFullName = redirects.front();
345 }
346 else {
347 std::cerr << "\n***************************************\n"
348 << "Step " << nStep++
349 << " Choose another trusted CA suggested by the CA: " << std::endl;
350 for (const auto& redirect : redirects) {
351 std::cerr << "> Index: " << count++ << std::endl
352 << ">> Suggested CA: " << ndn::security::extractIdentityFromCertName(redirect.getPrefix(-1))
353 << std::endl;
tylerliud82cc5c2020-12-21 23:58:39 -0800354 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800355 std::cerr << "Please type in the index of your choice:" << std::endl;
356 size_t index = 0;
357 try {
358 std::string input;
359 getline(std::cin, input);
360 index = std::stoul(input);
361 }
362 catch (const std::exception&) {
363 std::cerr << "Your input is Invalid. Exit" << std::endl;
364 exit(1);
365 }
366 if (index >= redirects.size()) {
367 std::cerr << "Your input is not an existing index. Exit" << std::endl;
368 exit(1);
369 }
370 redirectedCaFullName = redirects[index];
tylerliud82cc5c2020-12-21 23:58:39 -0800371 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500372 auto redirectedCaName = ndn::security::extractIdentityFromCertName(redirectedCaFullName.getPrefix(-1));
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800373 std::cerr << "You will be redirected to CA: " << redirectedCaName << std::endl;
Zhiyi Zhangfbcab842020-10-07 15:17:13 -0700374 face.expressInterest(
tylerliu4140fe82021-01-27 15:45:44 -0800375 *Request::genCaProfileDiscoveryInterest(redirectedCaName),
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800376 [&, redirectedCaFullName] (const auto&, const auto& data) {
tylerliu4140fe82021-01-27 15:45:44 -0800377 auto fetchingInterest = Request::genCaProfileInterestFromDiscoveryResponse(data);
Zhiyi Zhangfbcab842020-10-07 15:17:13 -0700378 face.expressInterest(*fetchingInterest,
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800379 [=] (const auto&, const auto& data2) { infoCb(data2, redirectedCaFullName); },
380 [] (auto&&...) { onNackCb(); },
381 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhangfbcab842020-10-07 15:17:13 -0700382 },
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500383 [] (auto&&...) { onNackCb(); },
384 [] (auto&&...) { timeoutCb(); });
tylerliufeabfdc2020-10-03 15:09:58 -0700385 }
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400386 else if (!names.empty()) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800387 if (names.size() < 2) {
388 selectedName = names.front().first;
389 }
390 else {
391 std::cerr << "\n***************************************\n"
392 << "Step " << nStep++
393 << ": You can either select one of the following names suggested by the CA: " << std::endl;
394 for (const auto& name : names) {
395 std::cerr << "> Index: " << count++ << std::endl
396 << ">> Suggested name: " << name.first << std::endl
397 << ">> Corresponding max suffix length: " << name.second << std::endl;
398 }
399 std::cerr << "Please type in the index of your choice:" << std::endl;
400 size_t index = 0;
401 try {
402 std::string input;
403 getline(std::cin, input);
404 index = std::stoul(input);
405 }
406 catch (const std::exception&) {
407 std::cerr << "Your input is invalid. Exit" << std::endl;
408 exit(1);
409 }
410 if (index >= names.size()) {
411 std::cerr << "Your input is not an existing index. Exit" << std::endl;
412 exit(1);
413 }
414 selectedName = names[index].first;
415 }
416 std::cerr << "You are applying for name: " << selectedName << std::endl;
417 runNew(profile, selectedName);
418 }
419 else {
420 std::cerr << "Neither name assignment nor redirection is available, exit." << std::endl;
421 exit(1);
422 }
tylerliufeabfdc2020-10-03 15:09:58 -0700423}
424
425static void
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400426selectCaProfile(const std::string& configFilePath)
tylerliufeabfdc2020-10-03 15:09:58 -0700427{
Zhiyi Zhanga16b7582020-10-29 18:59:46 -0700428 ProfileStorage profileStorage;
tylerliufeabfdc2020-10-03 15:09:58 -0700429 try {
Zhiyi Zhanga16b7582020-10-29 18:59:46 -0700430 profileStorage.load(configFilePath);
tylerliufeabfdc2020-10-03 15:09:58 -0700431 }
432 catch (const std::exception& e) {
433 std::cerr << "Cannot load the configuration file: " << e.what() << std::endl;
434 exit(1);
435 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700436 size_t count = 0;
437 std::cerr << "***************************************\n"
438 << "Step " << nStep++ << ": CA SELECTION" << std::endl;
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400439 for (const auto& item : profileStorage.getKnownProfiles()) {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700440 std::cerr << "> Index: " << count++ << std::endl
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800441 << ">> CA prefix:" << item.caPrefix << std::endl
442 << ">> Introduction: " << item.caInfo << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700443 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700444 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 -0700445
Zhiyi Zhang36706832019-07-04 21:33:03 -0700446 std::string caIndexS, caIndexSLower;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700447 getline(std::cin, caIndexS);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700448 caIndexSLower = caIndexS;
449 boost::algorithm::to_lower(caIndexSLower);
Tianyuan Yu48903c22022-05-02 15:30:07 -0700450 Name selectedCaName;
Zhiyi Zhang36706832019-07-04 21:33:03 -0700451 if (caIndexSLower == "none") {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700452 std::cerr << "\n***************************************\n"
453 << "Step " << nStep << ": ADD NEW CA\nPlease type in the CA's Name:" << std::endl;
Tianyuan Yu48903c22022-05-02 15:30:07 -0700454 std::string targetCaName;
455 getline(std::cin, targetCaName);
456 try {
457 selectedCaName = Name(targetCaName);
458 }
459 catch (const std::exception&) {
460 std::cerr << "Your input is not a valid name. Exit" << std::endl;
461 exit(1);
462 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700463 }
464 else {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700465 size_t caIndex;
Zhiyi Zhang36706832019-07-04 21:33:03 -0700466 try {
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700467 caIndex = std::stoul(caIndexS);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700468 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500469 catch (const std::exception&) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700470 std::cerr << "Your input is neither NONE nor a valid index. Exit" << std::endl;
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800471 exit(1);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700472 }
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700473 if (caIndex >= count) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700474 std::cerr << "Your input is not an existing index. Exit" << std::endl;
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800475 exit(1);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700476 }
Zhiyi Zhang84e11842020-11-19 20:03:23 -0800477 auto itemIterator = profileStorage.getKnownProfiles().cbegin();
tylerliufeabfdc2020-10-03 15:09:58 -0700478 std::advance(itemIterator, caIndex);
479 auto targetCaItem = *itemIterator;
Tianyuan Yu48903c22022-05-02 15:30:07 -0700480 trustedCert = targetCaItem.cert;
481 selectedCaName = targetCaItem.caPrefix;
tylerliufeabfdc2020-10-03 15:09:58 -0700482 }
Tianyuan Yu48903c22022-05-02 15:30:07 -0700483 face.expressInterest(
484 *Request::genCaProfileDiscoveryInterest(selectedCaName),
485 [&] (const auto&, const auto& data) {
486 auto fetchingInterest = Request::genCaProfileInterestFromDiscoveryResponse(data);
487 face.expressInterest(*fetchingInterest,
488 [] (const auto&, const auto& data2) { infoCb(data2, {}); },
489 [] (auto&&...) { onNackCb(); },
490 [] (auto&&...) { timeoutCb(); });
491 },
492 [] (auto&&...) { onNackCb(); },
493 [] (auto&&...) { timeoutCb(); });
tylerliufeabfdc2020-10-03 15:09:58 -0700494}
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700495
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700496static void
497runProbe(CaProfile profile)
tylerliufeabfdc2020-10-03 15:09:58 -0700498{
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800499 if (!capturedProbeParams) {
500 std::cerr << "\n***************************************\n"
501 << "Step " << nStep++ << ": Please provide information for name assignment" << std::endl;
502 auto captured = captureParams(profile.probeParameterKeys);
Tianyuan Yub5d3a4f2022-03-11 17:03:24 -0800503 auto it = captured.find(defaultChallenge);
504 if (it != captured.end()) {
505 it->second = boost::algorithm::to_lower_copy(it->second);
506 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800507 capturedProbeParams = std::make_shared<std::multimap<std::string, std::string>>(captured);
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700508 }
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800509 face.expressInterest(*Request::genProbeInterest(profile, std::move(*capturedProbeParams)),
510 [profile] (const auto&, const auto& data) { probeCb(data, profile); },
511 [] (auto&&...) { onNackCb(); },
512 [] (auto&&...) { timeoutCb(); });
tylerliufeabfdc2020-10-03 15:09:58 -0700513}
514
515static void
516runNew(CaProfile profile, Name identityName)
517{
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800518 int validityPeriod = captureValidityPeriod(time::duration_cast<time::hours>(profile.maxValidityPeriod));
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700519 auto now = time::system_clock::now();
520 std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
tylerliu4140fe82021-01-27 15:45:44 -0800521 requesterState = std::make_shared<Request>(keyChain, profile, RequestType::NEW);
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800522
523 // generate a newly key pair or choose an existing key
524 const auto& pib = keyChain.getPib();
525 ndn::security::pib::Identity identity;
526 ndn::security::pib::Key defaultKey;
527 try {
528 identity = pib.getIdentity(identityName);
529 }
530 catch (const ndn::security::Pib::Error&) {
531 identity = keyChain.createIdentity(identityName);
532 newlyCreatedIdentityName = identity.getName();
533 }
534 try {
535 defaultKey = identity.getDefaultKey();
536 }
537 catch (const ndn::security::Pib::Error&) {
538 defaultKey = keyChain.createKey(identity);
539 newlyCreatedKeyName = defaultKey.getName();
540 }
541
542 Name keyName;
543 if (newlyCreatedIdentityName.empty()) {
544 keyName = captureKeyName(identity, defaultKey);
545 }
546 else {
547 keyName = defaultKey.getName();
548 }
549 auto interest = requesterState->genNewInterest(keyName, now, now + time::hours(validityPeriod));
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700550 if (interest != nullptr) {
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500551 face.expressInterest(*interest,
552 [] (const auto&, const auto& data) { newCb(data); },
553 [] (auto&&...) { onNackCb(); },
554 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700555 }
556 else {
557 std::cerr << "Cannot generate the Interest for NEW step. Exit" << std::endl;
558 }
tylerliufeabfdc2020-10-03 15:09:58 -0700559}
560
561static void
562runChallenge(const std::string& challengeType)
563{
tylerliu40226332020-11-11 15:37:16 -0800564 std::multimap<std::string, std::string> requirement;
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700565 try {
tylerliu4140fe82021-01-27 15:45:44 -0800566 requirement = requesterState->selectOrContinueChallenge(challengeType);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700567 }
568 catch (const std::exception& e) {
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500569 std::cerr << "Error. Cannot successfully load the Challenge Module with error: " << e.what()
570 << "\nExit." << std::endl;
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700571 exit(1);
572 }
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400573 if (!requirement.empty()) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800574 if (requesterState->m_status == Status::BEFORE_CHALLENGE && challengeType == defaultChallenge) {
575 requirement.find(challengeType)->second = capturedProbeParams->find(defaultChallenge)->second;
576 }
577 else {
578 std::cerr << "\n***************************************\n"
579 << "Step " << nStep
580 << ": Please provide parameters used for Identity Verification Challenge" << std::endl;
581 captureParams(requirement);
582 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700583 }
tylerliu4140fe82021-01-27 15:45:44 -0800584 face.expressInterest(*requesterState->genChallengeInterest(std::move(requirement)),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500585 [] (const auto&, const auto& data) { challengeCb(data); },
586 [] (auto&&...) { onNackCb(); },
587 [] (auto&&...) { timeoutCb(); });
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700588}
589
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700590static void
591handleSignal(const boost::system::error_code& error, int signalNum)
592{
593 if (error) {
594 return;
595 }
596 const char* signalName = ::strsignal(signalNum);
597 std::cerr << "Exiting on signal ";
598 if (signalName == nullptr) {
599 std::cerr << signalNum;
600 }
601 else {
602 std::cerr << signalName;
603 }
604 std::cerr << std::endl;
tylerliufeabfdc2020-10-03 15:09:58 -0700605 if (requesterState) {
Tianyuan Yuca23bb02022-03-09 14:09:14 -0800606 if (!newlyCreatedIdentityName.empty()) {
607 auto identity = keyChain.getPib().getIdentity(newlyCreatedIdentityName);
608 keyChain.deleteIdentity(identity);
609 }
610 else if (!newlyCreatedKeyName.empty()) {
611 auto identity = keyChain.getPib().getIdentity(ndn::security::extractIdentityFromKeyName(newlyCreatedKeyName));
612 keyChain.deleteKey(identity, identity.getKey(newlyCreatedKeyName));
613 }
tylerliufeabfdc2020-10-03 15:09:58 -0700614 }
Davide Pesavento3e481842023-11-11 18:01:32 -0500615 face.getIoContext().stop();
tylerliufeabfdc2020-10-03 15:09:58 -0700616 exit(1);
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700617}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800618
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500619static int
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800620main(int argc, char* argv[])
621{
Davide Pesavento3e481842023-11-11 18:01:32 -0500622 boost::asio::signal_set terminateSignals(face.getIoContext());
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700623 terminateSignals.add(SIGINT);
624 terminateSignals.add(SIGTERM);
625 terminateSignals.async_wait(handleSignal);
626
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800627 namespace po = boost::program_options;
Zhiyi Zhang840afd92020-10-21 13:24:08 -0700628 std::string configFilePath = std::string(NDNCERT_SYSCONFDIR) + "/ndncert/client.conf";
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500629 po::options_description description("Usage: ndncert-client [-h] [-c FILE]\n");
630 description.add_options()
631 ("help,h", "produce help message")
632 ("config-file,c", po::value<std::string>(&configFilePath), "configuration file name")
633 ;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800634 po::positional_options_description p;
635
636 po::variables_map vm;
637 try {
638 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
639 po::notify(vm);
640 }
641 catch (const std::exception& e) {
642 std::cerr << "ERROR: " << e.what() << std::endl;
643 return 1;
644 }
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500645
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800646 if (vm.count("help") != 0) {
647 std::cerr << description << std::endl;
648 return 0;
649 }
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500650
Zhiyi Zhang837406d2020-10-05 22:01:31 -0700651 selectCaProfile(configFilePath);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800652 face.processEvents();
653 return 0;
654}
655
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400656} // namespace ndncert::requester
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800657
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700658int
659main(int argc, char* argv[])
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800660{
Davide Pesavento0dc02012021-11-23 22:55:03 -0500661 return ndncert::requester::main(argc, argv);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800662}