blob: 94fb2bc481f111473660c600fd6b162c02a94ebd [file] [log] [blame]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -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 "ca-module.hpp"
22#include "challenge-module.hpp"
23#include "logging.hpp"
24#include <ndn-cxx/face.hpp>
25#include <ndn-cxx/security/verification-helpers.hpp>
26#include <ndn-cxx/security/signing-helpers.hpp>
Junxiao Shi7c068032017-05-28 13:40:47 +000027#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080028
29namespace ndn {
30namespace ndncert {
31
32_LOG_INIT(ndncert.ca);
33
34CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
35 const std::string& configPath, const std::string& storageType)
36 : m_face(face)
37 , m_keyChain(keyChain)
38{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070039 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080040 m_config.load(configPath);
41 m_storage = CaStorage::createCaStorage(storageType);
42
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070043 // set default handler and callback
44 m_probeHandler = [&] (const std::string& probeInfo) {
45 return probeInfo;
46 };
47 m_requestUpdateCallback = [&] (const CertificateRequest& CertRequest) {
48 // do nothing
49 };
50
51 // register prefix
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080052 for (const auto& item : m_config.m_caItems) {
53 Name prefix = item.m_caName;
54 prefix.append("CA");
55 try {
56 const RegisteredPrefixId* prefixId = m_face.registerPrefix(prefix,
57 [&] (const Name& name) {
58 const InterestFilterId* filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
59 bind(&CaModule::handleProbe, this, _2, item));
60 m_interestFilterIds.push_back(filterId);
61
62 filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
63 bind(&CaModule::handleNew, this, _2, item));
64 m_interestFilterIds.push_back(filterId);
65
66 filterId = m_face.setInterestFilter(Name(name).append("_SELECT"),
67 bind(&CaModule::handleSelect, this, _2, item));
68 m_interestFilterIds.push_back(filterId);
69
70 filterId = m_face.setInterestFilter(Name(name).append("_VALIDATE"),
71 bind(&CaModule::handleValidate, this, _2, item));
72 m_interestFilterIds.push_back(filterId);
73
74 filterId = m_face.setInterestFilter(Name(name).append("_STATUS"),
75 bind(&CaModule::handleStatus, this, _2, item));
76 m_interestFilterIds.push_back(filterId);
77
78 filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
79 bind(&CaModule::handleDownload, this, _2, item));
80 m_interestFilterIds.push_back(filterId);
81
82 _LOG_TRACE("Prefix " << name << " got registered");
83 },
84 bind(&CaModule::onRegisterFailed, this, _2));
85 m_registeredPrefixIds.push_back(prefixId);
86 }
87 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -070088 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080089 }
90 }
91}
92
93CaModule::~CaModule()
94{
95 for (auto prefixId : m_interestFilterIds) {
96 m_face.unsetInterestFilter(prefixId);
97 }
98 for (auto prefixId : m_registeredPrefixIds) {
99 m_face.unregisterPrefix(prefixId, nullptr, nullptr);
100 }
101}
102
103void
104CaModule::handleProbe(const Interest& request, const CaItem& caItem)
105{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700106 // PROBE Naming Convention: /CA-prefix/CA/_PROBE/<Probe Information>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800107 _LOG_TRACE("Handle PROBE request");
108
109 std::string identifier;
110 try {
111 identifier = m_probeHandler(readString(request.getName().at(caItem.m_caName.size() + 2)));
112 }
113 catch (const std::exception& e) {
114 _LOG_TRACE("Cannot generate identifier for PROBE request " << e.what());
115 return;
116 }
117 Name identityName = caItem.m_caName;
118 identityName.append(identifier);
119
120 Data result;
121 result.setName(request.getName());
122 result.setContent(dataContentFromJson(genResponseProbeJson(identityName, "")));
123 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
124 m_face.put(result);
125
126 _LOG_TRACE("Handle PROBE: generate identity " << identityName);
127}
128
129void
130CaModule::handleNew(const Interest& request, const CaItem& caItem)
131{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700132 // NEW Naming Convention: /CA-prefix/CA/_NEW/<certificate-request>/[signature]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800133 _LOG_TRACE("Handle NEW request");
134
135 security::v2::Certificate clientCert;
136 try {
137 clientCert.wireDecode(request.getName().at(caItem.m_caName.size() + 2).blockFromValue());
138 }
139 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700140 _LOG_ERROR("Unrecognized certificate request " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800141 return;
142 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700143
144 if (!security::verifySignature(clientCert, clientCert)) {
145 _LOG_TRACE("Cert request with bad signature.");
146 return;
147 }
148 if (!security::verifySignature(request, clientCert)) {
149 _LOG_TRACE("Interest with bad signature.");
150 return;
151 }
152
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800153 std::string requestId = std::to_string(random::generateWord64());
154 CertificateRequest certRequest(caItem.m_caName, requestId, clientCert);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700155 certRequest.setStatus(ChallengeModule::WAIT_SELECTION);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800156 try {
157 m_storage->addRequest(certRequest);
158 }
159 catch (const std::exception& e) {
160 _LOG_TRACE("Cannot add new request instance " << e.what());
161 return;
162 }
163
164 Data result;
165 result.setName(request.getName());
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700166 result.setContent(dataContentFromJson(genResponseNewJson(requestId, certRequest.getStatus(),
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800167 caItem.m_supportedChallenges)));
168 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
169 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700170
171 m_requestUpdateCallback(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800172}
173
174void
175CaModule::handleSelect(const Interest& request, const CaItem& caItem)
176{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700177 // SELECT Naming Convention: /CA-prefix/CA/_SELECT/{Request-ID JSON}/<ChallengeID>/
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800178 // {Param JSON}/[Signature components]
179 _LOG_TRACE("Handle SELECT request");
180
181 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
182 if (certRequest.getRequestId().empty()) {
183 return;
184 }
185
186 if (!security::verifySignature(request, certRequest.getCert())) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700187 _LOG_TRACE("Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800188 return;
189 }
190
191 std::string challengeType;
192 try {
193 challengeType = readString(request.getName().at(caItem.m_caName.size() + 3));
194 }
195 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700196 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800197 return;
198 }
Zhiyi Zhange30eb352017-04-13 15:26:14 -0700199 _LOG_TRACE("SELECT request choosing challenge " << challengeType);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800200 auto challenge = ChallengeModule::createChallengeModule(challengeType);
201 if (challenge == nullptr) {
202 _LOG_TRACE("Unrecognized challenge type " << challengeType);
203 return;
204 }
205 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700206 if (certRequest.getStatus() == ChallengeModule::FAILURE) {
207 m_storage->deleteRequest(certRequest.getRequestId());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800208 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700209 else {
210 try {
211 m_storage->updateRequest(certRequest);
212 }
213 catch (const std::exception& e) {
214 _LOG_TRACE("Cannot update request instance " << e.what());
215 return;
216 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800217 }
218
219 Data result;
220 result.setName(request.getName());
221 result.setContent(dataContentFromJson(contentJson));
222 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
223 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700224
225 m_requestUpdateCallback(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800226}
227
228void
229CaModule::handleValidate(const Interest& request, const CaItem& caItem)
230{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700231 // VALIDATE Naming Convention: /CA-prefix/CA/_VALIDATE/{Request-ID JSON}/<ChallengeID>/
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800232 // {Param JSON}/[Signature components]
233 _LOG_TRACE("Handle VALIDATE request");
234
235 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
236 if (certRequest.getRequestId().empty()) {
237 return;
238 }
239
240 if (!security::verifySignature(request, certRequest.getCert())) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700241 _LOG_TRACE("Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800242 return;
243 }
244
245 std::string challengeType = certRequest.getChallengeType();
246 auto challenge = ChallengeModule::createChallengeModule(challengeType);
247 if (challenge == nullptr) {
248 _LOG_TRACE("Unrecognized challenge type " << challengeType);
249 return;
250 }
251 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700252 if (certRequest.getStatus() == ChallengeModule::FAILURE) {
253 m_storage->deleteRequest(certRequest.getRequestId());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800254 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700255 else {
256 try {
257 m_storage->updateRequest(certRequest);
258 }
259 catch (const std::exception& e) {
260 _LOG_TRACE("Cannot update request instance " << e.what());
261 return;
262 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800263 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800264 Data result;
265 result.setName(request.getName());
266 result.setContent(dataContentFromJson(contentJson));
267 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
268 m_face.put(result);
269
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700270 m_requestUpdateCallback(certRequest);
271
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800272 if (certRequest.getStatus() == ChallengeModule::SUCCESS) {
273 issueCertificate(certRequest, caItem);
274 }
275}
276
277void
278CaModule::handleStatus(const Interest& request, const CaItem& caItem)
279{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700280 // STATUS Naming Convention: /CA-prefix/CA/_STATUS/{Request-ID JSON}/[Signature components]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800281 _LOG_TRACE("Handle STATUS request");
282
283 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
284 if (certRequest.getRequestId().empty()) {
285 return;
286 }
287
288 if (!security::verifySignature(request, certRequest.getCert())) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700289 _LOG_TRACE("Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800290 return;
291 }
292
293 std::string challengeType = certRequest.getChallengeType();
294 auto challenge = ChallengeModule::createChallengeModule(challengeType);
295 if (challenge == nullptr) {
296 _LOG_TRACE("Unrecognized challenge type " << challengeType);
297 return;
298 }
299 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
300
301 Data result;
302 result.setName(request.getName());
303 result.setContent(dataContentFromJson(contentJson));
304 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
305 m_face.put(result);
306}
307
308void
309CaModule::handleDownload(const Interest& request, const CaItem& caItem)
310{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700311 // DOWNLOAD Naming Convention: /CA-prefix/CA/_DOWNLOAD/{Request-ID JSON}
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800312 _LOG_TRACE("Handle DOWNLOAD request");
313
314 JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caItem.m_caName.size() + 2);
315 std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
316 security::v2::Certificate signedCert;
317 try {
318 signedCert = m_storage->getCertificate(requestId);
319 }
320 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700321 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800322 return;
323 }
324
325 Data result;
326 result.setName(request.getName());
327 result.setContent(signedCert.wireEncode());
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700328 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800329 m_face.put(result);
330}
331
332void
333CaModule::issueCertificate(const CertificateRequest& certRequest, const CaItem& caItem)
334{
335 Name certName = certRequest.getCert().getKeyName();
336 certName.append("NDNCERT").appendVersion();
337 security::v2::Certificate newCert;
338 newCert.setName(certName);
339 newCert.setContent(certRequest.getCert().getContent());
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700340 _LOG_TRACE("cert request content " << certRequest.getCert());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800341 SignatureInfo signatureInfo;
342 security::ValidityPeriod period(time::system_clock::now(),
343 time::system_clock::now() + caItem.m_validityPeriod);
344 signatureInfo.setValidityPeriod(period);
345 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_CERT,
346 caItem.m_anchor, signatureInfo);
347 newCert.setFreshnessPeriod(caItem.m_freshnessPeriod);
348
349 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700350 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800351 try {
352 m_storage->addCertificate(certRequest.getRequestId(), newCert);
353 m_storage->deleteRequest(certRequest.getRequestId());
354 _LOG_TRACE("New Certificate Issued " << certName);
355 }
356 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700357 _LOG_ERROR("Cannot add issued cert and remove the request " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800358 return;
359 }
360}
361
362CertificateRequest
363CaModule::getCertificateRequest(const Interest& request, const Name& caName)
364{
365 JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caName.size() + 2);
366 std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
367 CertificateRequest certRequest;
368 try {
369 certRequest = m_storage->getRequest(requestId);
370 }
371 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700372 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800373 }
374 return certRequest;
375}
376
377void
378CaModule::onRegisterFailed(const std::string& reason)
379{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700380 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800381}
382
383Block
384CaModule::dataContentFromJson(const JsonSection& jsonSection)
385{
386 std::stringstream ss;
387 boost::property_tree::write_json(ss, jsonSection);
388 return makeStringBlock(ndn::tlv::Content, ss.str());
389}
390
391JsonSection
392CaModule::jsonFromNameComponent(const Name& name, int pos)
393{
394 std::string jsonString;
395 try {
396 jsonString = encoding::readString(name.at(pos));
397 }
398 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700399 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800400 return JsonSection();
401 }
402 std::istringstream ss(jsonString);
403 JsonSection json;
404 boost::property_tree::json_parser::read_json(ss, json);
405 return json;
406}
407
408} // namespace ndncert
409} // namespace ndn