blob: 66a81ef4e876709261c50aa0f542d1bb02940142 [file] [log] [blame]
Zhiyi Zhang08e0e982017-03-01 10:10:42 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07003 * Copyright (c) 2017-2019, 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
21#include "client-module.hpp"
22#include "challenge-module.hpp"
Zhiyi Zhangb6fab0f2017-09-21 16:26:27 -070023#include <iostream>
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080024#include <string>
Zhiyi Zhang547c8512019-06-18 23:46:14 -070025#include <algorithm>
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080026#include <boost/program_options/options_description.hpp>
27#include <boost/program_options/variables_map.hpp>
28#include <boost/program_options/parsers.hpp>
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080029#include <ndn-cxx/security/verification-helpers.hpp>
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080030
31namespace ndn {
32namespace ndncert {
33
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070034static void startApplication();
35
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080036int nStep;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070037Face face;
38security::v2::KeyChain keyChain;
39std::string challengeType;
40ClientModule client(keyChain);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080041
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070042static std::list<std::string>
43captureParams(const JsonSection& requirement)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080044{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070045 std::list<std::string> results;
Zhiyi Zhang547c8512019-06-18 23:46:14 -070046 for (const auto& item : requirement) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070047 std::cerr << item.second.get<std::string>("") << std::endl;
48 std::cerr << "Please provide the argument: " << item.first << " : " << std::endl;
49 std::string tempParam;
50 getline(std::cin, tempParam);
51 results.push_back(tempParam);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080052 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070053 std::cerr << "Got it. This is what you've provided:" << std::endl;
54 auto it1 = results.begin();
55 auto it2 = requirement.begin();
56 for (; it1 != results.end() && it2 != requirement.end(); it1++, it2++) {
57 std::cerr << it2->first << " : " << *it1 << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080058 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070059 return results;
60}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080061
Zhiyi Zhang547c8512019-06-18 23:46:14 -070062static std::list<std::string>
63captureParams(const std::vector<std::string>& requirement)
64{
65 std::list<std::string> results;
66 for (const auto& item : requirement) {
67 std::cerr << "Please provide the argument: " << item << " : " << std::endl;
68 std::string tempParam;
69 getline(std::cin, tempParam);
70 results.push_back(tempParam);
71 }
72 std::cerr << "Got it. This is what you've provided:" << std::endl;
73 auto it1 = results.begin();
74 auto it2 = requirement.begin();
75 for (; it1 != results.end() && it2 != requirement.end(); it1++, it2++) {
76 std::cerr << *it2 << " : " << *it1 << std::endl;
77 }
78 return results;
79}
80
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070081static void
82onNackCb()
83{
84 std::cerr << "Got NACK\n";
85}
86
87static void
88timeoutCb()
89{
90 std::cerr << "Interest sent time out\n";
91}
92
93static void
94downloadCb(const Data& reply)
95{
96 client.onDownloadResponse(reply);
97 std::cerr << "Step " << nStep++
98 << ": DONE! Certificate has already been installed to local keychain\n";
99 return;
100}
101
102static void
103challengeCb(const Data& reply)
104{
105 client.onChallengeResponse(reply);
106 if (client.getApplicationStatus() == STATUS_SUCCESS) {
107 std::cerr << "DONE! Certificate has already been issued \n";
108 face.expressInterest(*client.generateDownloadInterest(), bind(&downloadCb, _2),
109 bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700110 return;
111 }
112
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700113 auto challenge = ChallengeModule::createChallengeModule(challengeType);
114 auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(), client.getChallengeStatus());
115 if (requirement.size() > 0) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800116 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700117 std::string redo = "";
118 std::list<std::string> capturedParams;
119 do {
120 capturedParams = captureParams(requirement);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700121 std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700122 getline(std::cin, redo);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700123 std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 } while (redo == "REDO");
125 auto it1 = capturedParams.begin();
126 auto it2 = requirement.begin();
127 for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
128 it2->second.put("", *it1);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800129 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800130 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700131 face.expressInterest(*client.generateChallengeInterest(
132 challenge->genChallengeRequestJson(
133 client.getApplicationStatus(),
134 client.getChallengeStatus(),
135 requirement)),
136 bind(&challengeCb, _2),
137 bind(&onNackCb),
138 bind(&timeoutCb));
139}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800140
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700141static void
142newCb(const Data& reply)
143{
144 auto challengeList = client.onNewResponse(reply);
145 std::cerr << "Step " << nStep++ << ": Please type in the challenge ID from the following challenges\n";
146 for (auto item : challengeList) {
147 std::cerr << "\t" << item << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800148 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700149 std::string choice;
150 getline(std::cin, choice);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800151
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700152 auto challenge = ChallengeModule::createChallengeModule(choice);
153 if (challenge != nullptr) {
154 challengeType = choice;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800155 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700156 else {
157 std::cerr << "Cannot recognize the specified challenge. Exit";
158 return;
159 }
160 auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
161 client.getChallengeStatus());
162 if (requirement.size() > 0) {
163 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
164 std::string redo = "";
165 std::list<std::string> capturedParams;
166 do {
167 capturedParams = captureParams(requirement);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700168 std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700169 getline(std::cin, redo);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700170 std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700171 } while (redo == "REDO");
172 auto it1 = capturedParams.begin();
173 auto it2 = requirement.begin();
174 for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
175 it2->second.put("", *it1);
176 }
177 }
178 face.expressInterest(*client.generateChallengeInterest(
179 challenge->genChallengeRequestJson(
180 client.getApplicationStatus(),
181 client.getChallengeStatus(),
182 requirement)),
183 bind(&challengeCb, _2),
184 bind(&onNackCb),
185 bind(&timeoutCb));
186}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800187
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700188static void
189probeInfoCb(const Data& reply)
190{
191 auto contentJson = ClientModule::getJsonFromData(reply);
192 auto caItem = ClientConfig::extractCaItem(contentJson);
193
194 std::cerr << "Will install new trust anchor, please double check the identity info: \n"
195 << "This trust anchor packet is signed by " << reply.getSignature().getKeyLocator() << std::endl
196 << "The signing certificate is " << caItem.m_anchor << std::endl;
197 std::cerr << "Do you trust the information? Type in YES or NO" << std::endl;
198
199 std::string answer;
200 getline(std::cin, answer);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700201 std::transform(answer.begin(), answer.end(), answer.begin(), ::toupper);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700202 if (answer == "YES") {
203 client.onProbeInfoResponse(reply);
204 std::cerr << "You answered YES: new CA installed" << std::endl;
205 startApplication();
206 }
207 else {
208 std::cerr << "New CA not installed" << std::endl;
209 return;
210 }
211}
212
213static void
214probeCb(const Data& reply)
215{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700216 client.onProbeResponse(reply);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700217 std::cerr << "Step " << nStep++
218 << ": Please type in your expected validity period of your certificate."
219 << " Type in a number in unit of hour. The CA may change the validity"
220 << " period if your expected period is too long." << std::endl;
221 std::string periodStr;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700222 int hours = 0;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700223 getline(std::cin, periodStr);
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700224 hours = std::stoi(periodStr);
225 while (hours <= 0) {
226 std::cerr << "Invalid period time: " << "Please input the period again." << std::endl;
227 getline(std::cin, periodStr);
228 try {
229 hours = std::stoi(periodStr);
230 }
231 catch (const std::exception& e) {
232 hours = -1;
233 }
234 }
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700235 auto probeToken = make_shared<Data>(reply);
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700236 auto now = time::system_clock::now();
237 face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700238 Name(), probeToken),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700239 bind(&newCb, _2),
240 bind(&onNackCb),
241 bind(&timeoutCb));
242}
243
244static void
245startApplication()
246{
247 nStep = 0;
248 auto caList = client.getClientConf().m_caItems;
249 int count = 0;
250 for (auto item : caList) {
251 std::cerr << "***************************************\n"
252 << "Index: " << count++ << "\n"
253 << "CA prefix:" << item.m_caName << "\n"
254 << "Introduction: " << item.m_caInfo << "\n"
255 << "***************************************\n";
256 }
257 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
258 std::cerr << "Step "
259 << nStep++ << ": Please type in the CA INDEX that you want to apply"
260 << " or type in NONE if your expected CA is not in the list\n";
261
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700262 std::string caIndexS, caIndexSUpper;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700263 getline(std::cin, caIndexS);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700264 caIndexSUpper = caIndexS;
265 std::transform(caIndexSUpper.begin(), caIndexSUpper.end(), caIndexSUpper.begin(), ::toupper);
266 if (caIndexSUpper == "NONE") {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700267 std::cerr << "Step " << nStep << ": Please type in the CA Name\n";
268 face.expressInterest(*client.generateProbeInfoInterest(Name(caIndexS)),
269 bind(&probeInfoCb, _2),
270 bind(&onNackCb),
271 bind(&timeoutCb));
272 }
273 else {
274 int caIndex = std::stoi(caIndexS);
275 BOOST_ASSERT(caIndex <= count);
276 auto targetCaItem = caVector[caIndex];
277
278 if (targetCaItem.m_probe != "") {
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700279 std::cerr << "Step " << nStep++ << ": Please provide information for name assignment" << std::endl;
280 std::vector<std::string> probeFields = ClientModule::parseProbeComponents(targetCaItem.m_probe);
281 std::string redo = "";
282 std::list<std::string> capturedParams;
283 do {
284 capturedParams = captureParams(probeFields);
285 std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
286 getline(std::cin, redo);
287 std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
288 } while (redo == "REDO");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700289 std::string probeInfo;
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700290 for (const auto& item : capturedParams) {
291 probeInfo += item;
292 probeInfo += ":";
293 }
294 probeInfo = probeInfo.substr(0, probeInfo.size() - 1);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700295 face.expressInterest(*client.generateProbeInterest(targetCaItem, probeInfo),
296 bind(&probeCb, _2),
297 bind(&onNackCb),
298 bind(&timeoutCb));
299 }
300 else {
301 std::cerr << "Step " << nStep++ << ": Please type in the identity name you want to get (with CA prefix)\n";
302 std::string identityNameStr;
303 getline(std::cin, identityNameStr);
304 std::cerr << "Step "
305 << nStep++ << ": Please type in your expected validity period of your certificate."
306 << "Type in a number in unit of hour."
307 << " The CA may change the validity period if your expected period is too long.\n";
308 std::string periodStr;
309 getline(std::cin, periodStr);
310 int hours = std::stoi(periodStr);
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700311 auto now = time::system_clock::now();
312 face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700313 Name(identityNameStr)),
314 bind(&newCb, _2),
315 bind(&onNackCb),
316 bind(&timeoutCb));
317 }
318 }
319}
320
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800321
322int
323main(int argc, char* argv[])
324{
325 namespace po = boost::program_options;
326 std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700327 po::options_description description("General Usage\n ndncert-client [-h] [-f]\n");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800328 description.add_options()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700329 ("help,h", "produce help message")
330 ("config-file,f", po::value<std::string>(&configFilePath), "config file name");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800331 po::positional_options_description p;
332
333 po::variables_map vm;
334 try {
335 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
336 po::notify(vm);
337 }
338 catch (const std::exception& e) {
339 std::cerr << "ERROR: " << e.what() << std::endl;
340 return 1;
341 }
342 if (vm.count("help") != 0) {
343 std::cerr << description << std::endl;
344 return 0;
345 }
Zhiyi Zhangd8993b92019-07-04 21:58:10 -0700346 try {
347 client.getClientConf().load(configFilePath);
348 }
349 catch (const std::exception& e) {
350 std::cerr << "Cannot load the configuration file: " << e.what() << std::endl;
351 return 1;
352 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700353 startApplication();
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800354 face.processEvents();
355 return 0;
356}
357
358} // namespace ndncert
359} // namespace ndn
360
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800361int main(int argc, char* argv[])
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800362{
363 return ndn::ndncert::main(argc, argv);
364}