blob: 8e2da5e7665aa2cca2319e3249d363abe008eb8d [file] [log] [blame]
Zhiyi Zhang08e0e982017-03-01 10:10:42 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
swa77020643ac2020-03-26 02:24:45 -07002/**
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -07003 * Copyright (c) 2017-2020, 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
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070021#include "challenge-module.hpp"
22#include "client-module.hpp"
23#include "protocol-detail/info.hpp"
24#include <ndn-cxx/security/verification-helpers.hpp>
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -070025#include <boost/asio.hpp>
Davide Pesaventob48bbda2020-07-27 19:41:37 -040026#include <boost/program_options/options_description.hpp>
27#include <boost/program_options/parsers.hpp>
28#include <boost/program_options/variables_map.hpp>
Zhiyi Zhang48f23782020-09-28 12:11:24 -070029#include <iostream>
Zhiyi Zhang48f23782020-09-28 12:11:24 -070030#include <string>
31
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080032namespace ndn {
33namespace ndncert {
34
Zhiyi Zhang48f23782020-09-28 12:11:24 -070035static void
36startApplication();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070037
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080038int nStep;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070039Face face;
tylerliu182bc532020-09-25 01:54:45 -070040security::v2::KeyChain keyChain;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070041std::string challengeType;
Zhiyi Zhang36706832019-07-04 21:33:03 -070042int validityPeriod = -1;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070043ClientModule client(keyChain);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080044
Zhiyi Zhang46049832020-09-28 17:08:12 -070045static void
46captureParams(std::vector<std::tuple<std::string, std::string>>& requirement)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080047{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070048 std::list<std::string> results;
Zhiyi Zhang46049832020-09-28 17:08:12 -070049 for (auto& item : requirement) {
50 std::cerr << std::get<1>(item) << std::endl;
51 std::string captured;
52 getline(std::cin, captured);
53 std::get<1>(item) = captured;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080054 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070055 std::cerr << "Got it. This is what you've provided:" << std::endl;
Zhiyi Zhang46049832020-09-28 17:08:12 -070056 for (const auto& item : requirement) {
57 std::cerr << std::get<0>(item) << " : " << std::get<1>(item) << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080058 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070059}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080060
Zhiyi Zhang9829da92020-09-30 16:19:34 -070061static std::vector<std::tuple<std::string, std::string>>
62captureParams(const std::list<std::string>& requirement)
Zhiyi Zhang547c8512019-06-18 23:46:14 -070063{
Zhiyi Zhang9829da92020-09-30 16:19:34 -070064 std::vector<std::tuple<std::string, std::string>> results;
Zhiyi Zhang547c8512019-06-18 23:46:14 -070065 for (const auto& item : requirement) {
66 std::cerr << "Please provide the argument: " << item << " : " << std::endl;
Zhiyi Zhang9829da92020-09-30 16:19:34 -070067 std::string captured;
68 getline(std::cin, captured);
69 results.push_back(std::make_tuple(item, captured));
Zhiyi Zhang547c8512019-06-18 23:46:14 -070070 }
71 std::cerr << "Got it. This is what you've provided:" << std::endl;
Zhiyi Zhang9829da92020-09-30 16:19:34 -070072 for (const auto& item : results) {
73 std::cerr << std::get<0>(item) << " : " << std::get<1>(item) << std::endl;
Zhiyi Zhang547c8512019-06-18 23:46:14 -070074 }
75 return results;
76}
77
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070078static void
Zhiyi Zhang36706832019-07-04 21:33:03 -070079captureValidityPeriod()
80{
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -070081 if (validityPeriod > 0) {
82 return;
83 }
84 std::cerr << "Step " << nStep++
85 << ": Please type in your expected validity period of your certificate."
86 << " Type the number of hours (168 for week, 730 for month, 8760 for year)."
87 << " The CA may reject your application if your expected period is too long." << std::endl;
88 std::string periodStr = "";
89 getline(std::cin, periodStr);
90 try {
91 validityPeriod = std::stoi(periodStr);
92 }
93 catch (const std::exception& e) {
94 validityPeriod = -1;
Zhiyi Zhang36706832019-07-04 21:33:03 -070095 }
96}
97
98static void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070099onNackCb()
100{
101 std::cerr << "Got NACK\n";
102}
103
104static void
105timeoutCb()
106{
107 std::cerr << "Interest sent time out\n";
108}
109
110static void
swa770cf1d8f72020-04-21 23:12:39 -0700111certFetchCb(const Data& reply)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700112{
swa770cf1d8f72020-04-21 23:12:39 -0700113 client.onCertFetchResponse(reply);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700114 std::cerr << "Step " << nStep++
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700115 << ": DONE! Certificate has already been installed to local keychain\n"
Zhiyi Zhangef6b36a2020-09-22 21:20:59 -0700116 << "Certificate Name: " << reply.getName().toUri() << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700117}
118
119static void
120challengeCb(const Data& reply)
121{
122 client.onChallengeResponse(reply);
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700123 if (client.getApplicationStatus() == Status::SUCCESS) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 std::cerr << "DONE! Certificate has already been issued \n";
swa770cf1d8f72020-04-21 23:12:39 -0700125 face.expressInterest(*client.generateCertFetchInterest(), bind(&certFetchCb, _2),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700126 bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700127 return;
128 }
129
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700130 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhang46049832020-09-28 17:08:12 -0700131 auto requirement = challenge->getRequestedParameterList(client.getApplicationStatus(),
132 client.getChallengeStatus());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700133 if (requirement.size() > 0) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800134 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
Zhiyi Zhang46049832020-09-28 17:08:12 -0700135 captureParams(requirement);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800136 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700137 face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestTLV(client.getApplicationStatus(),
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700138 client.getChallengeStatus(),
Zhiyi Zhang46049832020-09-28 17:08:12 -0700139 std::move(requirement))),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700140 bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700141}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800142
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700143static void
144newCb(const Data& reply)
145{
Zhiyi Zhang36706832019-07-04 21:33:03 -0700146 int challengeIndex = 0;
tylerliu0e176c32020-09-29 11:39:46 -0700147 auto challengeList = client.onNewRenewRevokeResponse(reply);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700148 if (challengeList.size() < 1) {
149 std::cerr << "There is no available challenge provided by the CA. Exit" << std::endl;
150 return;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800151 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700152 else if (challengeList.size() > 1) {
153 int count = 0;
154 std::string choice = "";
155 std::cerr << "Step " << nStep++ << ": Please type in the challenge index that you want to perform\n";
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700156 count = 0;
157 for (auto item : challengeList) {
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700158 std::cerr << "\t" << count++ << " : " << item << std::endl;
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700159 }
160 getline(std::cin, choice);
161 try {
162 challengeIndex = std::stoi(choice);
163 }
164 catch (const std::exception& e) {
165 challengeIndex = -1;
166 }
167 if (challengeIndex < 0 || challengeIndex >= count) {
168 std::cerr << "Your input index is out of range. Exit." << std::endl;
169 return;
170 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700171 }
172 auto it = challengeList.begin();
173 std::advance(it, challengeIndex);
174 unique_ptr<ChallengeModule> challenge = ChallengeModule::createChallengeModule(*it);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700175 if (challenge != nullptr) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700176 challengeType = *it;
177 std::cerr << "The challenge has been selected: " << challengeType << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800178 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700179 else {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700180 std::cerr << "Error. Cannot load selected Challenge Module. Exit." << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700181 return;
182 }
Zhiyi Zhang46049832020-09-28 17:08:12 -0700183 auto requirement = challenge->getRequestedParameterList(client.getApplicationStatus(),
184 client.getChallengeStatus());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700185 if (requirement.size() > 0) {
Zhiyi Zhang46049832020-09-28 17:08:12 -0700186 std::cerr << "Step " << nStep++ << ": Please provide parameters used for Identity Verification Challenge\n";
187 captureParams(requirement);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700188 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700189 face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestTLV(client.getApplicationStatus(),
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700190 client.getChallengeStatus(),
Zhiyi Zhang46049832020-09-28 17:08:12 -0700191 std::move(requirement))),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700192 bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700193}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800194
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700195static void
Suyong Won19fba4d2020-05-09 13:39:46 -0700196InfoCb(const Data& reply)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700197{
Suyong Won19fba4d2020-05-09 13:39:46 -0700198 const Block& contentBlock = reply.getContent();
199
200 if (!client.verifyInfoResponse(reply)) {
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700201 std::cerr << "The fetched CA information cannot be trusted because its integrity is broken" << std::endl;
202 return;
203 }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700204 auto caItem = INFO::decodeDataContentToCaProfile(contentBlock);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700205
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700206 std::cerr << "Will use a new trust anchor, please double check the identity info: \n"
Zhiyi Zhangef6b36a2020-09-22 21:20:59 -0700207 << "This trust anchor information is signed by " << reply.getSignature().getKeyLocator()
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400208 << std::endl
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700209 << "The certificate is " << *caItem.m_cert << std::endl
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400210 << "Do you trust the information? Type in YES or NO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700211
212 std::string answer;
213 getline(std::cin, answer);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700214 boost::algorithm::to_lower(answer);
215 if (answer == "yes") {
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700216 std::cerr << "You answered YES: new CA will be used" << std::endl;
Suyong Won19fba4d2020-05-09 13:39:46 -0700217 client.addCaFromInfoResponse(reply);
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700218 // client.getClientConf().save(std::string(SYSCONFDIR) + "/ndncert/client.conf");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700219 startApplication();
220 }
221 else {
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700222 std::cerr << "You answered NO: new CA will not be used" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700223 return;
224 }
225}
226
227static void
228probeCb(const Data& reply)
229{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700230 client.onProbeResponse(reply);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700231 captureValidityPeriod();
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700232 if (validityPeriod <= 0) {
233 std::cerr << "Invalid period time. Exit." << std::endl;
234 return;
235 }
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700236 auto now = time::system_clock::now();
Zhiyi Zhang36706832019-07-04 21:33:03 -0700237 std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
Zhiyi Zhangb8bbc642020-09-29 14:08:26 -0700238 auto interest = client.generateNewInterest(now, now + time::hours(validityPeriod), Name());
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700239 if (interest != nullptr) {
240 face.expressInterest(*interest, bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
241 }
242 else {
243 std::cerr << "Cannot generate the Interest for NEW step. Exit" << std::endl;
244 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700245}
246
247static void
248startApplication()
249{
250 nStep = 0;
251 auto caList = client.getClientConf().m_caItems;
252 int count = 0;
253 for (auto item : caList) {
254 std::cerr << "***************************************\n"
255 << "Index: " << count++ << "\n"
Suyong Won256c9062020-05-11 02:45:56 -0700256 << "CA prefix:" << item.m_caPrefix << "\n"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700257 << "Introduction: " << item.m_caInfo << "\n"
258 << "***************************************\n";
259 }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700260 std::vector<CaConfigItem> caVector{std::begin(caList), std::end(caList)};
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700261 std::cerr << "Step "
262 << nStep++ << ": Please type in the CA INDEX that you want to apply"
263 << " or type in NONE if your expected CA is not in the list\n";
264
Zhiyi Zhang36706832019-07-04 21:33:03 -0700265 std::string caIndexS, caIndexSLower;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700266 getline(std::cin, caIndexS);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700267 caIndexSLower = caIndexS;
268 boost::algorithm::to_lower(caIndexSLower);
269 if (caIndexSLower == "none") {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700270 std::cerr << "Step " << nStep << ": Please type in the CA Name\n";
Zhiyi Zhangcaab5462019-10-18 13:41:02 -0700271 std::string expectedCAName;
272 getline(std::cin, expectedCAName);
swa77020643ac2020-03-26 02:24:45 -0700273 face.expressInterest(*client.generateInfoInterest(Name(expectedCAName)),
Suyong Won19fba4d2020-05-09 13:39:46 -0700274 bind(&InfoCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700275 }
276 else {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700277 int caIndex;
278 try {
279 caIndex = std::stoi(caIndexS);
280 }
281 catch (const std::exception& e) {
282 std::cerr << "Your input is neither NONE nor a valid index. Exit" << std::endl;
283 return;
284 }
285 if (caIndex < 0 || caIndex >= count) {
286 std::cerr << "Your input is not an existing index. Exit" << std::endl;
287 return;
288 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700289 auto targetCaItem = caVector[caIndex];
290
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700291 if (!targetCaItem.m_probeParameterKeys.empty()) {
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700292 std::cerr << "Step " << nStep++ << ": Please provide information for name assignment" << std::endl;
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700293 auto capturedParams = captureParams(targetCaItem.m_probeParameterKeys);
294 face.expressInterest(*client.generateProbeInterest(targetCaItem, std::move(capturedParams)),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700295 bind(&probeCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700296 }
297 else {
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700298 std::cerr << "Step " << nStep++ << ": Please type in the full identity name you want to get (with CA prefix)\n";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700299 std::string identityNameStr;
300 getline(std::cin, identityNameStr);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700301 captureValidityPeriod();
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700302 if (validityPeriod <= 0) {
303 std::cerr << "Invalid period time. Exit." << std::endl;
304 return;
305 }
306 Name idName(identityNameStr);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700307 std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700308 auto now = time::system_clock::now();
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700309 auto interest = client.generateNewInterest(now, now + time::hours(validityPeriod), idName);
310 if (interest != nullptr) {
311 face.expressInterest(*interest, bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
312 }
313 else {
314 std::cerr << "Cannot generate the Interest for NEW step. Exit" << std::endl;
315 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700316 }
317 }
318}
319
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700320static void
321handleSignal(const boost::system::error_code& error, int signalNum)
322{
323 if (error) {
324 return;
325 }
326 const char* signalName = ::strsignal(signalNum);
327 std::cerr << "Exiting on signal ";
328 if (signalName == nullptr) {
329 std::cerr << signalNum;
330 }
331 else {
332 std::cerr << signalName;
333 }
334 std::cerr << std::endl;
335 client.endSession();
336 face.getIoService().stop();
337}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800338
339int
340main(int argc, char* argv[])
341{
Zhiyi Zhangad9e04f2020-03-27 12:04:31 -0700342 boost::asio::signal_set terminateSignals(face.getIoService());
343 terminateSignals.add(SIGINT);
344 terminateSignals.add(SIGTERM);
345 terminateSignals.async_wait(handleSignal);
346
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800347 namespace po = boost::program_options;
348 std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
Zhiyi Zhang36706832019-07-04 21:33:03 -0700349 po::options_description description("General Usage\n ndncert-client [-h] [-c] [-v]\n");
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700350 description.add_options()("help,h", "produce help message")("config-file,c", po::value<std::string>(&configFilePath), "configuration file name")("validity-period,v", po::value<int>(&validityPeriod)->default_value(-1),
351 "desired validity period (hours) of the certificate being requested");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800352 po::positional_options_description p;
353
354 po::variables_map vm;
355 try {
356 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
357 po::notify(vm);
358 }
359 catch (const std::exception& e) {
360 std::cerr << "ERROR: " << e.what() << std::endl;
361 return 1;
362 }
363 if (vm.count("help") != 0) {
364 std::cerr << description << std::endl;
365 return 0;
366 }
Zhiyi Zhangd8993b92019-07-04 21:58:10 -0700367 try {
368 client.getClientConf().load(configFilePath);
369 }
370 catch (const std::exception& e) {
371 std::cerr << "Cannot load the configuration file: " << e.what() << std::endl;
372 return 1;
373 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700374 startApplication();
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800375 face.processEvents();
376 return 0;
377}
378
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700379} // namespace ndncert
380} // namespace ndn
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800381
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700382int
383main(int argc, char* argv[])
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800384{
385 return ndn::ndncert::main(argc, argv);
386}