blob: a4b3368ab43667e050f2beaa71dbab09eaf4fc97 [file] [log] [blame]
Zhiyi Zhang08e0e982017-03-01 10:10:42 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2017, Regents of the University of California.
4 *
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 Zhang08e0e982017-03-01 10:10:42 -080023
Zhiyi Zhangb6fab0f2017-09-21 16:26:27 -070024#include <iostream>
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080025#include <string>
Zhiyi Zhangb6fab0f2017-09-21 16:26:27 -070026
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080027#include <boost/program_options/options_description.hpp>
28#include <boost/program_options/variables_map.hpp>
29#include <boost/program_options/parsers.hpp>
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080030#include <ndn-cxx/security/verification-helpers.hpp>
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080031
32namespace ndn {
33namespace ndncert {
34
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080035int nStep;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080036
37class ClientTool
38{
39public:
40 ClientTool(ClientModule& clientModule)
41 : client(clientModule)
42 {
43 }
44
45 void
46 errorCb(const std::string& errorInfo)
47 {
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080048 std::cerr << "Error: " << errorInfo << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -080049 }
50
51 void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080052 downloadCb(const shared_ptr<RequestState>& state)
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -070053 {
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080054 std::cerr << "Step " << nStep++
55 << "DONE! Certificate has already been installed to local keychain" << std::endl;
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -070056 return;
57 }
58
59 void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080060 anchorCb(const Interest& request, const Data& reply,
61 const ClientCaItem& anchorItem, const Name& assignedName)
62 {
63 auto contentJson = ClientModule::getJsonFromData(reply);
64 auto caItem = ClientConfig::extractCaItem(contentJson);
65
66 if (!security::verifySignature(caItem.m_anchor, anchorItem.m_anchor)) {
67 std::cerr << "Fail to verify fetched anchor" << std::endl;
68 return;
69 }
70 client.getClientConf().m_caItems.push_back(caItem);
71
72 if (assignedName.toUri() != "/") {
73 client.sendNew(caItem, assignedName,
74 bind(&ClientTool::newCb, this, _1),
75 bind(&ClientTool::errorCb, this, _1));
76 }
77 else {
78 if (caItem.m_probe != "") {
79 std::cerr <<"Step " << nStep++ << ": Probe Requirement-" << caItem.m_probe << std::endl;
80 std::string probeInfo;
81 getline(std::cin, probeInfo);
82 client.sendProbe(caItem, probeInfo,
83 bind(&ClientTool::newCb, this, _1),
84 bind(&ClientTool::errorCb, this, _1));
85 }
86 else {
87 std::cerr <<"Step " << nStep++ << ": Please type in the identity name" << std::endl;
88 std::string nameComponent;
89 getline(std::cin, nameComponent);
90 Name identityName = caItem.m_caName.getPrefix(-1);
91 identityName.append(nameComponent);
92 client.sendNew(caItem, identityName,
93 bind(&ClientTool::newCb, this, _1),
94 bind(&ClientTool::errorCb, this, _1));
95 }
96 }
97 }
98
99 void
100 listCb(const std::list<Name>& caList, const Name& assignedName, const Name& schema,
101 const ClientCaItem& caItem)
102 {
103 if (assignedName.toUri() != "" && caList.size() == 1) {
104 // with recommendation
105
106 std::cerr << "Get recommended CA: " << caList.front()
107 << "Get recommended Identity: " << assignedName << std::endl;
108 client.requestCaTrustAnchor(caList.front(),
109 bind(&ClientTool::anchorCb, this, _1, _2, caItem, assignedName),
110 bind(&ClientTool::errorCb, this, _1));
111 }
112 else {
113 // without recommendation
114 int count = 0;
115 for (auto name : caList) {
116 std::cerr << "***************************************" << "\n"
117 << "Index: " << count++ << "\n"
118 << "CA prefix:" << name << "\n"
119 << "***************************************" << std::endl;
120 }
121 std::cerr << "Select an index to apply for a certificate."<< std::endl;
122
123 std::string option;
124 getline(std::cin, option);
125 int caIndex = std::stoi(option);
126
127 std::vector<Name> caVector{std::begin(caList), std::end(caList)};
128 Name targetCaName = caVector[caIndex];
129
130 client.requestCaTrustAnchor(targetCaName,
131 bind(&ClientTool::anchorCb, this, _1, _2, caItem, Name("")),
132 bind(&ClientTool::errorCb, this, _1));
133 }
134 }
135
136 void
137 validateCb(const shared_ptr<RequestState>& state)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800138 {
139 if (state->m_status == ChallengeModule::SUCCESS) {
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800140 std::cerr << "DONE! Certificate has already been issued" << std::endl;
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700141 client.requestDownload(state,
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800142 bind(&ClientTool::downloadCb, this, _1),
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700143 bind(&ClientTool::errorCb, this, _1));
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800144 return;
145 }
146
147 auto challenge = ChallengeModule::createChallengeModule(state->m_challengeType);
148 auto requirementList = challenge->getRequirementForValidate(state->m_status);
149
150 std::cerr << "Step" << nStep++ << ": Please satisfy following instruction(s)" << std::endl;
151 for (auto requirement : requirementList) {
152 std::cerr << "\t" << requirement << std::endl;
153 }
154 std::list<std::string> paraList;
155 for (size_t i = 0; i < requirementList.size(); i++) {
156 std::string tempParam;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800157 getline(std::cin, tempParam);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800158 paraList.push_back(tempParam);
159 }
160 auto paramJson = challenge->genValidateParamsJson(state->m_status, paraList);
161 client.sendValidate(state, paramJson,
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800162 bind(&ClientTool::validateCb, this, _1),
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800163 bind(&ClientTool::errorCb, this, _1));
164 }
165
166 void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800167 selectCb(const shared_ptr<RequestState>& state)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800168 {
169 auto challenge = ChallengeModule::createChallengeModule(state->m_challengeType);
170 auto requirementList = challenge->getRequirementForValidate(state->m_status);
171
172 std::cerr << "Step" << nStep++ << ": Please satisfy following instruction(s)" << std::endl;
173 for (auto item : requirementList) {
174 std::cerr << "\t" << item << std::endl;
175 }
176 std::list<std::string> paraList;
177 for (size_t i = 0; i < requirementList.size(); i++) {
178 std::string tempParam;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800179 getline(std::cin, tempParam);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800180 paraList.push_back(tempParam);
181 }
182
183 auto paramJson = challenge->genValidateParamsJson(state->m_status, paraList);
184 client.sendValidate(state, paramJson,
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800185 bind(&ClientTool::validateCb, this, _1),
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800186 bind(&ClientTool::errorCb, this, _1));
187 }
188
189 void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800190 newCb(const shared_ptr<RequestState>& state)
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800191 {
192 std::cerr << "Step" << nStep++ << ": Please select one challenge from following types." << std::endl;
193 for (auto item : state->m_challengeList) {
194 std::cerr << "\t" << item << std::endl;
195 }
196 std::string choice;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800197 getline(std::cin, choice);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800198
199 auto challenge = ChallengeModule::createChallengeModule(choice);
200 auto requirementList = challenge->getRequirementForSelect();
201 std::list<std::string> paraList;
202 if (requirementList.size() != 0) {
203 std::cerr << "Step" << nStep++ << ": Please satisfy following instruction(s)" << std::endl;
204 for (auto item : requirementList) {
205 std::cerr << "\t" << item << std::endl;
206 }
207 for (size_t i = 0; i < requirementList.size(); i++) {
208 std::string tempParam;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800209 getline(std::cin, tempParam);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800210 paraList.push_back(tempParam);
211 }
212 }
213 auto paramJson = challenge->genSelectParamsJson(state->m_status, paraList);
214 client.sendSelect(state, choice, paramJson,
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800215 bind(&ClientTool::selectCb, this, _1),
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800216 bind(&ClientTool::errorCb, this, _1));
217 }
218
219public:
220 ClientModule& client;
221};
222
223int
224main(int argc, char* argv[])
225{
226 namespace po = boost::program_options;
227 std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800228 bool isIntra = false;
229 po::options_description description("General Usage\n ndncert-client [-h] [-i] [-f]\n");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800230 description.add_options()
231 ("help,h", "produce help message")
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800232 ("intra-node,i", "optional, if specified, switch on the intra-node mode")
233 ("config-file,f", po::value<std::string>(&configFilePath), "config file name")
234 ;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800235 po::positional_options_description p;
236
237 po::variables_map vm;
238 try {
239 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
240 po::notify(vm);
241 }
242 catch (const std::exception& e) {
243 std::cerr << "ERROR: " << e.what() << std::endl;
244 return 1;
245 }
246 if (vm.count("help") != 0) {
247 std::cerr << description << std::endl;
248 return 0;
249 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800250 if (vm.count("intra-node") != 0) {
251 isIntra = true;
252 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800253
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800254 nStep = 0;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800255 Face face;
256 security::v2::KeyChain keyChain;
257 ClientModule client(face, keyChain);
258 client.getClientConf().load(configFilePath);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800259 ClientTool tool(client);
260
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800261 if (isIntra) {
262 client.requestLocalhostList([&](const ClientConfig& config) {
263 auto caList = config.m_caItems;
264 int count = 0;
265 for (auto item : caList) {
266 std::cerr << "***************************************" << "\n"
267 << "Index: " << count++ << "\n"
268 << "CA prefix:" << item.m_caName << "\n"
269 << "Introduction: " << item.m_caInfo << "\n"
270 << "***************************************" << std::endl;
271 }
272 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
273 std::cerr << "Step" << nStep++
274 << ": Please type in the CA namespace index that you want to apply" << std::endl;
275 std::string caIndexS;
276 getline(std::cin, caIndexS);
277 int caIndex = std::stoi(caIndexS);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800278
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800279 BOOST_ASSERT(caIndex <= count);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800280
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800281 auto targetCaItem = caVector[caIndex];
282 if (targetCaItem.m_probe != "") {
283 std::cerr <<"Step" << nStep++ << ": Probe Requirement-" << targetCaItem.m_probe << std::endl;
284 std::string probeInfo;
285 getline(std::cin, probeInfo);
286 client.sendProbe(targetCaItem, probeInfo,
287 bind(&ClientTool::newCb, &tool, _1),
288 bind(&ClientTool::errorCb, &tool, _1));
289 }
290 else {
291 std::cerr <<"Step" << nStep++ << ": Please type in the identity name" << std::endl;
292 std::string nameComponent;
293 getline(std::cin, nameComponent);
294 Name identityName = targetCaItem.m_caName.getPrefix(-1);
295 identityName.append(nameComponent);
296 client.sendNew(targetCaItem, identityName,
297 bind(&ClientTool::newCb, &tool, _1),
298 bind(&ClientTool::errorCb, &tool, _1));
299 }
300 },
301 bind(&ClientTool::errorCb, &tool, _1));
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800302 }
303 else {
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800304 auto caList = client.getClientConf().m_caItems;
305 int count = 0;
306 for (auto item : caList) {
307 std::cerr << "***************************************" << "\n"
308 << "Index: " << count++ << "\n"
309 << "CA prefix:" << item.m_caName << "\n"
310 << "Introduction: " << item.m_caInfo << "\n"
311 << "***************************************" << std::endl;
312 }
313 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
314 std::cerr << "Step " << nStep++ << ": Please type in the CA namespace index that you want to apply" << std::endl;
315
316 std::string caIndexS;
317 getline(std::cin, caIndexS);
318
319 int caIndex = std::stoi(caIndexS);
320
321 BOOST_ASSERT(caIndex <= count);
322
323 auto targetCaItem = caVector[caIndex];
324 std::cerr << "You want a namespace with prefix (A) /ndn or (B) a sub-namespace of "
325 << targetCaItem.m_caName << "?" << std::endl;
326 std::string listOption;
327 getline(std::cin, listOption);
328 if (listOption == "A" || listOption == "a") {
329 // should directly send _PROBE or _NEW
330 if (targetCaItem.m_probe != "") {
331 std::cerr <<"Step " << nStep++ << ": Probe Requirement-" << targetCaItem.m_probe << std::endl;
332 std::string probeInfo;
333 getline(std::cin, probeInfo);
334 client.sendProbe(targetCaItem, probeInfo,
335 bind(&ClientTool::newCb, &tool, _1),
336 bind(&ClientTool::errorCb, &tool, _1));
337 }
338 else {
339 std::cerr <<"Step " << nStep++ << ": Please type in the identity name" << std::endl;
340 std::string nameComponent;
341 getline(std::cin, nameComponent);
342 Name identityName = targetCaItem.m_caName.getPrefix(-1);
343 identityName.append(nameComponent);
344 client.sendNew(targetCaItem, identityName,
345 bind(&ClientTool::newCb, &tool, _1),
346 bind(&ClientTool::errorCb, &tool, _1));
347 }
348 }
349 else if (listOption == "B" || listOption == "b") {
350 std::string additionalInfo = "";
351 if (targetCaItem.m_targetedList != "") {
352 std::cerr <<"Step " << nStep++
353 << ": Enter nothing if you want to see all available sub-namespace CAs"
354 << "or follow the instruction to get a recommended CA\n"
355 << "\t" << targetCaItem.m_targetedList << std::endl;
356 getline(std::cin, additionalInfo);
357 }
358 client.requestList(targetCaItem, additionalInfo,
359 bind(&ClientTool::listCb, &tool, _1, _2, _3, targetCaItem),
360 bind(&ClientTool::errorCb, &tool, _1));
361 }
362 else {
363 std::cerr << "Your input is not an option." << std::endl;
364 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800365 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800366
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800367 face.processEvents();
368 return 0;
369}
370
371} // namespace ndncert
372} // namespace ndn
373
374int
375main(int argc, char* argv[])
376{
377 return ndn::ndncert::main(argc, argv);
378}