blob: 50057ae9667f5684025a83e43d2667d83a55c0e4 [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{
216 std::cerr << "Step " << nStep++
217 << ": Please type in your expected validity period of your certificate."
218 << " Type in a number in unit of hour. The CA may change the validity"
219 << " period if your expected period is too long." << std::endl;
220 std::string periodStr;
221 getline(std::cin, periodStr);
222 int hours = std::stoi(periodStr);
223 face.expressInterest(*client.generateNewInterest(time::system_clock::now(),
224 time::system_clock::now() + time::hours(hours)),
225 bind(&newCb, _2),
226 bind(&onNackCb),
227 bind(&timeoutCb));
228}
229
230static void
231startApplication()
232{
233 nStep = 0;
234 auto caList = client.getClientConf().m_caItems;
235 int count = 0;
236 for (auto item : caList) {
237 std::cerr << "***************************************\n"
238 << "Index: " << count++ << "\n"
239 << "CA prefix:" << item.m_caName << "\n"
240 << "Introduction: " << item.m_caInfo << "\n"
241 << "***************************************\n";
242 }
243 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
244 std::cerr << "Step "
245 << nStep++ << ": Please type in the CA INDEX that you want to apply"
246 << " or type in NONE if your expected CA is not in the list\n";
247
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700248 std::string caIndexS, caIndexSUpper;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700249 getline(std::cin, caIndexS);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700250 caIndexSUpper = caIndexS;
251 std::transform(caIndexSUpper.begin(), caIndexSUpper.end(), caIndexSUpper.begin(), ::toupper);
252 if (caIndexSUpper == "NONE") {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700253 std::cerr << "Step " << nStep << ": Please type in the CA Name\n";
254 face.expressInterest(*client.generateProbeInfoInterest(Name(caIndexS)),
255 bind(&probeInfoCb, _2),
256 bind(&onNackCb),
257 bind(&timeoutCb));
258 }
259 else {
260 int caIndex = std::stoi(caIndexS);
261 BOOST_ASSERT(caIndex <= count);
262 auto targetCaItem = caVector[caIndex];
263
264 if (targetCaItem.m_probe != "") {
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700265 std::cerr << "Step " << nStep++ << ": Please provide information for name assignment" << std::endl;
266 std::vector<std::string> probeFields = ClientModule::parseProbeComponents(targetCaItem.m_probe);
267 std::string redo = "";
268 std::list<std::string> capturedParams;
269 do {
270 capturedParams = captureParams(probeFields);
271 std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
272 getline(std::cin, redo);
273 std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
274 } while (redo == "REDO");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700275 std::string probeInfo;
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700276 for (const auto& item : capturedParams) {
277 probeInfo += item;
278 probeInfo += ":";
279 }
280 probeInfo = probeInfo.substr(0, probeInfo.size() - 1);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700281 face.expressInterest(*client.generateProbeInterest(targetCaItem, probeInfo),
282 bind(&probeCb, _2),
283 bind(&onNackCb),
284 bind(&timeoutCb));
285 }
286 else {
287 std::cerr << "Step " << nStep++ << ": Please type in the identity name you want to get (with CA prefix)\n";
288 std::string identityNameStr;
289 getline(std::cin, identityNameStr);
290 std::cerr << "Step "
291 << nStep++ << ": Please type in your expected validity period of your certificate."
292 << "Type in a number in unit of hour."
293 << " The CA may change the validity period if your expected period is too long.\n";
294 std::string periodStr;
295 getline(std::cin, periodStr);
296 int hours = std::stoi(periodStr);
297 face.expressInterest(*client.generateNewInterest(time::system_clock::now(),
298 time::system_clock::now() + time::hours(hours),
299 Name(identityNameStr)),
300 bind(&newCb, _2),
301 bind(&onNackCb),
302 bind(&timeoutCb));
303 }
304 }
305}
306
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800307
308int
309main(int argc, char* argv[])
310{
311 namespace po = boost::program_options;
312 std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700313 po::options_description description("General Usage\n ndncert-client [-h] [-f]\n");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800314 description.add_options()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700315 ("help,h", "produce help message")
316 ("config-file,f", po::value<std::string>(&configFilePath), "config file name");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800317 po::positional_options_description p;
318
319 po::variables_map vm;
320 try {
321 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
322 po::notify(vm);
323 }
324 catch (const std::exception& e) {
325 std::cerr << "ERROR: " << e.what() << std::endl;
326 return 1;
327 }
328 if (vm.count("help") != 0) {
329 std::cerr << description << std::endl;
330 return 0;
331 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800332 client.getClientConf().load(configFilePath);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700333 startApplication();
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800334 face.processEvents();
335 return 0;
336}
337
338} // namespace ndncert
339} // namespace ndn
340
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800341int main(int argc, char* argv[])
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800342{
343 return ndn::ndncert::main(argc, argv);
344}