blob: 7e9c5a419740fb4e4340bbb3b8055a6ae95c7d66 [file] [log] [blame]
Zhiyi Zhang08e0e982017-03-01 10:10:42 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -08003 * Copyright (c) 2017-2018, 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 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++
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -080055 << "DONE! Certificate has already been installed to local keychain\n";
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 != "") {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -080079 std::cerr << "Step " << nStep++ << ": Probe Requirement-" << caItem.m_probe << std::endl;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080080 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 {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -080087 std::cerr << "Step " << nStep++ << ": Please type in the identity name\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080088 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) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800116 std::cerr << "***************************************\n"
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800117 << "Index: " << count++ << "\n"
118 << "CA prefix:" << name << "\n"
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800119 << "***************************************\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800120 }
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800121 std::cerr << "Select an index to apply for a certificate\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800122
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 Zhang916ba2d2018-02-01 18:16:27 -0800140 std::cerr << "DONE! Certificate has already been issued \n";
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
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800150 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800151 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
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800172 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)" << std::endl;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800173 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 {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800192 std::cerr << "Step " << nStep++ << ": Please select one challenge from following types\n";
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800193 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) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800203 std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800204 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()
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800231 ("help,h",
232 "produce help message")
233 ("intra-node,i",
234 "optional, if specified, switch on the intra-node mode")
235 ("config-file,f", po::value<std::string>(&configFilePath),
236 "config file name");
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800237 po::positional_options_description p;
238
239 po::variables_map vm;
240 try {
241 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
242 po::notify(vm);
243 }
244 catch (const std::exception& e) {
245 std::cerr << "ERROR: " << e.what() << std::endl;
246 return 1;
247 }
248 if (vm.count("help") != 0) {
249 std::cerr << description << std::endl;
250 return 0;
251 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800252 if (vm.count("intra-node") != 0) {
253 isIntra = true;
254 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800255
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800256 nStep = 0;
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800257 Face face;
258 security::v2::KeyChain keyChain;
259 ClientModule client(face, keyChain);
260 client.getClientConf().load(configFilePath);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800261 ClientTool tool(client);
262
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800263 if (isIntra) {
264 client.requestLocalhostList([&](const ClientConfig& config) {
265 auto caList = config.m_caItems;
266 int count = 0;
267 for (auto item : caList) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800268 std::cerr << "***************************************\n"
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800269 << "Index: " << count++ << "\n"
270 << "CA prefix:" << item.m_caName << "\n"
271 << "Introduction: " << item.m_caInfo << "\n"
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800272 << "***************************************\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800273 }
274 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800275 std::cerr << "Step " << nStep++
276 << ": Please type in the CA namespace index that you want to apply\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800277 std::string caIndexS;
278 getline(std::cin, caIndexS);
279 int caIndex = std::stoi(caIndexS);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800280
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800281 BOOST_ASSERT(caIndex <= count);
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800282
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800283 auto targetCaItem = caVector[caIndex];
284 if (targetCaItem.m_probe != "") {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800285 std::cerr << "Step " << nStep++ << ": Probe Requirement-" << targetCaItem.m_probe << std::endl;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800286 std::string probeInfo;
287 getline(std::cin, probeInfo);
288 client.sendProbe(targetCaItem, probeInfo,
289 bind(&ClientTool::newCb, &tool, _1),
290 bind(&ClientTool::errorCb, &tool, _1));
291 }
292 else {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800293 std::cerr << "Step " << nStep++ << ": Please type in the identity name\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800294 std::string nameComponent;
295 getline(std::cin, nameComponent);
296 Name identityName = targetCaItem.m_caName.getPrefix(-1);
297 identityName.append(nameComponent);
298 client.sendNew(targetCaItem, identityName,
299 bind(&ClientTool::newCb, &tool, _1),
300 bind(&ClientTool::errorCb, &tool, _1));
301 }
302 },
303 bind(&ClientTool::errorCb, &tool, _1));
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800304 }
305 else {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800306 // Inter-node Application
307 bool listFirst = false;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800308 auto caList = client.getClientConf().m_caItems;
309 int count = 0;
310 for (auto item : caList) {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800311 std::cerr << "***************************************\n"
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800312 << "Index: " << count++ << "\n"
313 << "CA prefix:" << item.m_caName << "\n"
314 << "Introduction: " << item.m_caInfo << "\n"
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800315 << "***************************************\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800316 }
317 std::vector<ClientCaItem> caVector{std::begin(caList), std::end(caList)};
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800318 std::cerr << "Step " << nStep++ << ": Please type in the CA namespace index that you want to apply\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800319
320 std::string caIndexS;
321 getline(std::cin, caIndexS);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800322 int caIndex = std::stoi(caIndexS);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800323 BOOST_ASSERT(caIndex <= count);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800324 auto targetCaItem = caVector[caIndex];
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800325
326 if (targetCaItem.m_isListEnabled) {
327 std::cerr << "This CA provides several sub-namepace CAs \n"
328 << "Do you want to (A) get a certificate from " << targetCaItem.m_caName << "directly? \n"
329 << "Or (B) get a certificate from one of its sub-namespace CAs? \n"
330 << "Please type in your choice (A or B) \n";
331 std::string listOption;
332 getline(std::cin, listOption);
333 if (listOption == "A" || listOption == "a") {
334 listFirst = false;
335 }
336 else if (listOption == "B" || listOption == "b") {
337 listFirst = true;
338 std::string additionalInfo = "";
339 if (targetCaItem.m_targetedList != "") {
340 std::cerr << "Step " << nStep++
341 << ": Enter nothing if you want to see all available sub-namespace CAs"
342 << " or follow the instruction to get a recommended CA\n"
343 << "\t" << targetCaItem.m_targetedList << std::endl;
344 getline(std::cin, additionalInfo);
345 }
346 client.requestList(targetCaItem, additionalInfo,
347 bind(&ClientTool::listCb, &tool, _1, _2, _3, targetCaItem),
348 bind(&ClientTool::errorCb, &tool, _1));
349 }
350 else {
351 std::cerr << "Your input is not an option." << std::endl;
352 return 1;
353 }
354 }
355 if (!listFirst) {
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800356 if (targetCaItem.m_probe != "") {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800357 std::cerr << "Step " << nStep++ << ": Probe Requirement-" << targetCaItem.m_probe << std::endl;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800358 std::string probeInfo;
359 getline(std::cin, probeInfo);
360 client.sendProbe(targetCaItem, probeInfo,
361 bind(&ClientTool::newCb, &tool, _1),
362 bind(&ClientTool::errorCb, &tool, _1));
363 }
364 else {
Zhiyi Zhang916ba2d2018-02-01 18:16:27 -0800365 std::cerr << "Step " << nStep++ << ": Please type in the identity name\n";
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800366 std::string nameComponent;
367 getline(std::cin, nameComponent);
368 Name identityName = targetCaItem.m_caName.getPrefix(-1);
369 identityName.append(nameComponent);
370 client.sendNew(targetCaItem, identityName,
371 bind(&ClientTool::newCb, &tool, _1),
372 bind(&ClientTool::errorCb, &tool, _1));
373 }
374 }
Zhiyi Zhang08e0e982017-03-01 10:10:42 -0800375 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800376
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}