blob: 2c4742498724110a78e2550ab2045ca9e582957a [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 Zhang08e0e982017-03-01 10:10:42 -080025#include <boost/program_options/options_description.hpp>
26#include <boost/program_options/variables_map.hpp>
27#include <boost/program_options/parsers.hpp>
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080028#include <ndn-cxx/security/verification-helpers.hpp>
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080029
30namespace ndn {
31namespace ndncert {
32
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070033static void startApplication();
34
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080035int nStep;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070036Face face;
37security::v2::KeyChain keyChain;
38std::string challengeType;
Zhiyi Zhang36706832019-07-04 21:33:03 -070039int validityPeriod = -1;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070040ClientModule 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
Zhiyi Zhang36706832019-07-04 21:33:03 -070082captureValidityPeriod()
83{
84 if (validityPeriod <= 0) {
85 std::cerr << "Step " << nStep++
86 << ": Please type in your expected validity period of your certificate."
87 << " Type the number of hours (168 for week, 730 for month, 8760 for year). The CA may change the validity"
88 << " period if your expected period is too long." << std::endl;
89 std::string periodStr = "";
90 do {
91 getline(std::cin, periodStr);
92 try {
93 validityPeriod = std::stoi(periodStr);
94 }
95 catch (const std::exception& e) {
96 validityPeriod = -1;
97 }
98 if (validityPeriod > 0) {
99 break;
100 }
101 std::cerr << "Invalid period time. Please input the period again." << std::endl;
102 } while (true);
103 }
104}
105
106static void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700107onNackCb()
108{
109 std::cerr << "Got NACK\n";
110}
111
112static void
113timeoutCb()
114{
115 std::cerr << "Interest sent time out\n";
116}
117
118static void
119downloadCb(const Data& reply)
120{
121 client.onDownloadResponse(reply);
122 std::cerr << "Step " << nStep++
123 << ": DONE! Certificate has already been installed to local keychain\n";
124 return;
125}
126
127static void
128challengeCb(const Data& reply)
129{
130 client.onChallengeResponse(reply);
131 if (client.getApplicationStatus() == STATUS_SUCCESS) {
132 std::cerr << "DONE! Certificate has already been issued \n";
133 face.expressInterest(*client.generateDownloadInterest(), bind(&downloadCb, _2),
134 bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700135 return;
136 }
137
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700138 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700139 auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
140 client.getChallengeStatus());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700141 if (requirement.size() > 0) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800142 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700143 std::string redo = "";
144 std::list<std::string> capturedParams;
145 do {
146 capturedParams = captureParams(requirement);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700147 std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700148 getline(std::cin, redo);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700149 boost::algorithm::to_lower(redo);
150 } while (redo != "ok");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700151 auto it1 = capturedParams.begin();
152 auto it2 = requirement.begin();
153 for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
154 it2->second.put("", *it1);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800155 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800156 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700157 face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestJson(client.getApplicationStatus(),
158 client.getChallengeStatus(),
159 requirement)),
160 bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700161}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800162
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700163static void
164newCb(const Data& reply)
165{
Zhiyi Zhang36706832019-07-04 21:33:03 -0700166 int challengeIndex = 0;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700167 auto challengeList = client.onNewResponse(reply);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700168 if (challengeList.size() < 1) {
169 std::cerr << "There is no available challenge provided by the CA. Exit" << std::endl;
170 return;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800171 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700172 else if (challengeList.size() > 1) {
173 int count = 0;
174 std::string choice = "";
175 std::cerr << "Step " << nStep++ << ": Please type in the challenge index that you want to perform\n";
176 do {
177 count = 0;
178 for (auto item : challengeList) {
179 std::cerr << "\t" << count++ << " : "<< item << std::endl;
180 }
181 getline(std::cin, choice);
182 try {
183 challengeIndex = std::stoi(choice);
184 }
185 catch (const std::exception& e) {
186 challengeIndex = -1;
187 }
188 if (challengeIndex >= 0 && challengeIndex < count) {
189 break;
190 }
191 std::cerr << "Your input index is out of range. Please type in again." << std::endl;
192 } while (true);
193 }
194 auto it = challengeList.begin();
195 std::advance(it, challengeIndex);
196 unique_ptr<ChallengeModule> challenge = ChallengeModule::createChallengeModule(*it);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700197 if (challenge != nullptr) {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700198 challengeType = *it;
199 std::cerr << "The challenge has been selected: " << challengeType << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800200 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700201 else {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700202 std::cerr << "Error. Cannot load selected Challenge Module. Exit." << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700203 return;
204 }
205 auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
206 client.getChallengeStatus());
207 if (requirement.size() > 0) {
208 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
209 std::string redo = "";
210 std::list<std::string> capturedParams;
211 do {
212 capturedParams = captureParams(requirement);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700213 std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700214 getline(std::cin, redo);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700215 boost::algorithm::to_lower(redo);
216 } while (redo != "ok");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700217 auto it1 = capturedParams.begin();
218 auto it2 = requirement.begin();
219 for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
220 it2->second.put("", *it1);
221 }
222 }
Zhiyi Zhang36706832019-07-04 21:33:03 -0700223 face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestJson(client.getApplicationStatus(),
224 client.getChallengeStatus(),
225 requirement)),
226 bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700227}
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800228
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700229static void
230probeInfoCb(const Data& reply)
231{
232 auto contentJson = ClientModule::getJsonFromData(reply);
233 auto caItem = ClientConfig::extractCaItem(contentJson);
234
235 std::cerr << "Will install new trust anchor, please double check the identity info: \n"
236 << "This trust anchor packet is signed by " << reply.getSignature().getKeyLocator() << std::endl
237 << "The signing certificate is " << caItem.m_anchor << std::endl;
238 std::cerr << "Do you trust the information? Type in YES or NO" << std::endl;
239
240 std::string answer;
241 getline(std::cin, answer);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700242 boost::algorithm::to_lower(answer);
243 if (answer == "yes") {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700244 client.onProbeInfoResponse(reply);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700245 std::cerr << "You answered YES: new CA has been installed" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700246 startApplication();
247 }
248 else {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700249 std::cerr << "New CA will not be installed" << std::endl;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700250 return;
251 }
252}
253
254static void
255probeCb(const Data& reply)
256{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700257 client.onProbeResponse(reply);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700258 captureValidityPeriod();
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700259 auto probeToken = make_shared<Data>(reply);
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700260 auto now = time::system_clock::now();
Zhiyi Zhang36706832019-07-04 21:33:03 -0700261 std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
262 face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700263 Name(), probeToken),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700264 bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700265}
266
267static void
268startApplication()
269{
270 nStep = 0;
271 auto caList = client.getClientConf().m_caItems;
272 int count = 0;
273 for (auto item : caList) {
274 std::cerr << "***************************************\n"
275 << "Index: " << count++ << "\n"
276 << "CA prefix:" << item.m_caName << "\n"
277 << "Introduction: " << item.m_caInfo << "\n"
278 << "***************************************\n";
279 }
280 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
281 std::cerr << "Step "
282 << nStep++ << ": Please type in the CA INDEX that you want to apply"
283 << " or type in NONE if your expected CA is not in the list\n";
284
Zhiyi Zhang36706832019-07-04 21:33:03 -0700285 std::string caIndexS, caIndexSLower;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700286 getline(std::cin, caIndexS);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700287 caIndexSLower = caIndexS;
288 boost::algorithm::to_lower(caIndexSLower);
289 if (caIndexSLower == "none") {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700290 std::cerr << "Step " << nStep << ": Please type in the CA Name\n";
291 face.expressInterest(*client.generateProbeInfoInterest(Name(caIndexS)),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700292 bind(&probeInfoCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700293 }
294 else {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700295 int caIndex;
296 try {
297 caIndex = std::stoi(caIndexS);
298 }
299 catch (const std::exception& e) {
300 std::cerr << "Your input is neither NONE nor a valid index. Exit" << std::endl;
301 return;
302 }
303 if (caIndex < 0 || caIndex >= count) {
304 std::cerr << "Your input is not an existing index. Exit" << std::endl;
305 return;
306 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700307 auto targetCaItem = caVector[caIndex];
308
309 if (targetCaItem.m_probe != "") {
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700310 std::cerr << "Step " << nStep++ << ": Please provide information for name assignment" << std::endl;
311 std::vector<std::string> probeFields = ClientModule::parseProbeComponents(targetCaItem.m_probe);
312 std::string redo = "";
313 std::list<std::string> capturedParams;
314 do {
315 capturedParams = captureParams(probeFields);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700316 std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700317 getline(std::cin, redo);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700318 boost::algorithm::to_lower(redo);
319 } while (redo != "ok");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700320 std::string probeInfo;
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700321 for (const auto& item : capturedParams) {
322 probeInfo += item;
323 probeInfo += ":";
324 }
325 probeInfo = probeInfo.substr(0, probeInfo.size() - 1);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700326 face.expressInterest(*client.generateProbeInterest(targetCaItem, probeInfo),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700327 bind(&probeCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700328 }
329 else {
330 std::cerr << "Step " << nStep++ << ": Please type in the identity name you want to get (with CA prefix)\n";
331 std::string identityNameStr;
332 getline(std::cin, identityNameStr);
Zhiyi Zhang36706832019-07-04 21:33:03 -0700333 captureValidityPeriod();
334 std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700335 auto now = time::system_clock::now();
Zhiyi Zhang36706832019-07-04 21:33:03 -0700336 face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700337 Name(identityNameStr)),
Zhiyi Zhang36706832019-07-04 21:33:03 -0700338 bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700339 }
340 }
341}
342
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800343
344int
345main(int argc, char* argv[])
346{
347 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 Zhang08e0e982017-03-01 10:10:42 -0800350 description.add_options()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700351 ("help,h", "produce help message")
Zhiyi Zhang36706832019-07-04 21:33:03 -0700352 ("config-file,c", po::value<std::string>(&configFilePath), "config file name")
353 ("validity-period,v", po::value<int>(&validityPeriod)->default_value(-1), "the validity period of your certificate being requested");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800354 po::positional_options_description p;
355
356 po::variables_map vm;
357 try {
358 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
359 po::notify(vm);
360 }
361 catch (const std::exception& e) {
362 std::cerr << "ERROR: " << e.what() << std::endl;
363 return 1;
364 }
365 if (vm.count("help") != 0) {
366 std::cerr << description << std::endl;
367 return 0;
368 }
Zhiyi Zhangd8993b92019-07-04 21:58:10 -0700369 try {
370 client.getClientConf().load(configFilePath);
371 }
372 catch (const std::exception& e) {
373 std::cerr << "Cannot load the configuration file: " << e.what() << std::endl;
374 return 1;
375 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700376 startApplication();
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800377 face.processEvents();
378 return 0;
379}
380
381} // namespace ndncert
382} // namespace ndn
383
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800384int main(int argc, char* argv[])
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800385{
386 return ndn::ndncert::main(argc, argv);
387}