blob: edd61dc7494bf8b1c0a2b750202d1b0667f7b628 [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>
27
28namespace ndn {
29namespace ndncert {
30
31_LOG_INIT(ndncert.ca);
32
33CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
34 const std::string& configPath, const std::string& storageType)
35 : m_face(face)
36 , m_keyChain(keyChain)
37{
38 m_config.load(configPath);
39 m_storage = CaStorage::createCaStorage(storageType);
40
41 for (const auto& item : m_config.m_caItems) {
42 Name prefix = item.m_caName;
43 prefix.append("CA");
44 try {
45 const RegisteredPrefixId* prefixId = m_face.registerPrefix(prefix,
46 [&] (const Name& name) {
47 const InterestFilterId* filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
48 bind(&CaModule::handleProbe, this, _2, item));
49 m_interestFilterIds.push_back(filterId);
50
51 filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
52 bind(&CaModule::handleNew, this, _2, item));
53 m_interestFilterIds.push_back(filterId);
54
55 filterId = m_face.setInterestFilter(Name(name).append("_SELECT"),
56 bind(&CaModule::handleSelect, this, _2, item));
57 m_interestFilterIds.push_back(filterId);
58
59 filterId = m_face.setInterestFilter(Name(name).append("_VALIDATE"),
60 bind(&CaModule::handleValidate, this, _2, item));
61 m_interestFilterIds.push_back(filterId);
62
63 filterId = m_face.setInterestFilter(Name(name).append("_STATUS"),
64 bind(&CaModule::handleStatus, this, _2, item));
65 m_interestFilterIds.push_back(filterId);
66
67 filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
68 bind(&CaModule::handleDownload, this, _2, item));
69 m_interestFilterIds.push_back(filterId);
70
71 _LOG_TRACE("Prefix " << name << " got registered");
72 },
73 bind(&CaModule::onRegisterFailed, this, _2));
74 m_registeredPrefixIds.push_back(prefixId);
75 }
76 catch (const std::exception& e) {
77 _LOG_TRACE("Error: " << e.what());
78 }
79 }
80}
81
82CaModule::~CaModule()
83{
84 for (auto prefixId : m_interestFilterIds) {
85 m_face.unsetInterestFilter(prefixId);
86 }
87 for (auto prefixId : m_registeredPrefixIds) {
88 m_face.unregisterPrefix(prefixId, nullptr, nullptr);
89 }
90}
91
92void
93CaModule::handleProbe(const Interest& request, const CaItem& caItem)
94{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -070095 // PROBE Naming Convention: /CA-prefix/CA/_PROBE/<Probe Information>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080096 _LOG_TRACE("Handle PROBE request");
97
98 std::string identifier;
99 try {
100 identifier = m_probeHandler(readString(request.getName().at(caItem.m_caName.size() + 2)));
101 }
102 catch (const std::exception& e) {
103 _LOG_TRACE("Cannot generate identifier for PROBE request " << e.what());
104 return;
105 }
106 Name identityName = caItem.m_caName;
107 identityName.append(identifier);
108
109 Data result;
110 result.setName(request.getName());
111 result.setContent(dataContentFromJson(genResponseProbeJson(identityName, "")));
112 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
113 m_face.put(result);
114
115 _LOG_TRACE("Handle PROBE: generate identity " << identityName);
116}
117
118void
119CaModule::handleNew(const Interest& request, const CaItem& caItem)
120{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700121 // NEW Naming Convention: /CA-prefix/CA/_NEW/<certificate-request>/[signature]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800122 _LOG_TRACE("Handle NEW request");
123
124 security::v2::Certificate clientCert;
125 try {
126 clientCert.wireDecode(request.getName().at(caItem.m_caName.size() + 2).blockFromValue());
127 }
128 catch (const std::exception& e) {
129 _LOG_TRACE("Unrecognized certificate request " << e.what());
130 return;
131 }
132 std::string requestId = std::to_string(random::generateWord64());
133 CertificateRequest certRequest(caItem.m_caName, requestId, clientCert);
134 try {
135 m_storage->addRequest(certRequest);
136 }
137 catch (const std::exception& e) {
138 _LOG_TRACE("Cannot add new request instance " << e.what());
139 return;
140 }
141
142 Data result;
143 result.setName(request.getName());
144 result.setContent(dataContentFromJson(genResponseNewJson(requestId,
145 ChallengeModule::WAIT_SELECTION,
146 caItem.m_supportedChallenges)));
147 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
148 m_face.put(result);
149}
150
151void
152CaModule::handleSelect(const Interest& request, const CaItem& caItem)
153{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700154 // SELECT Naming Convention: /CA-prefix/CA/_SELECT/{Request-ID JSON}/<ChallengeID>/
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800155 // {Param JSON}/[Signature components]
156 _LOG_TRACE("Handle SELECT request");
157
158 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
159 if (certRequest.getRequestId().empty()) {
160 return;
161 }
162
163 if (!security::verifySignature(request, certRequest.getCert())) {
164 _LOG_TRACE("Error: Interest with bad signature.");
165 return;
166 }
167
168 std::string challengeType;
169 try {
170 challengeType = readString(request.getName().at(caItem.m_caName.size() + 3));
171 }
172 catch (const std::exception& e) {
173 _LOG_TRACE(e.what());
174 return;
175 }
Zhiyi Zhange30eb352017-04-13 15:26:14 -0700176 _LOG_TRACE("SELECT request choosing challenge " << challengeType);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800177 auto challenge = ChallengeModule::createChallengeModule(challengeType);
178 if (challenge == nullptr) {
179 _LOG_TRACE("Unrecognized challenge type " << challengeType);
180 return;
181 }
182 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
183 try {
184 m_storage->updateRequest(certRequest);
185 }
186 catch (const std::exception& e) {
187 _LOG_TRACE("Cannot update request instance " << e.what());
188 return;
189 }
190
191 Data result;
192 result.setName(request.getName());
193 result.setContent(dataContentFromJson(contentJson));
194 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
195 m_face.put(result);
196}
197
198void
199CaModule::handleValidate(const Interest& request, const CaItem& caItem)
200{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700201 // VALIDATE Naming Convention: /CA-prefix/CA/_VALIDATE/{Request-ID JSON}/<ChallengeID>/
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800202 // {Param JSON}/[Signature components]
203 _LOG_TRACE("Handle VALIDATE request");
204
205 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
206 if (certRequest.getRequestId().empty()) {
207 return;
208 }
209
210 if (!security::verifySignature(request, certRequest.getCert())) {
211 _LOG_TRACE("Error: Interest with bad signature.");
212 return;
213 }
214
215 std::string challengeType = certRequest.getChallengeType();
216 auto challenge = ChallengeModule::createChallengeModule(challengeType);
217 if (challenge == nullptr) {
218 _LOG_TRACE("Unrecognized challenge type " << challengeType);
219 return;
220 }
221 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
222 try {
223 m_storage->updateRequest(certRequest);
224 }
225 catch (const std::exception& e) {
226 _LOG_TRACE("Cannot update request instance " << e.what());
227 return;
228 }
229
230 Data result;
231 result.setName(request.getName());
232 result.setContent(dataContentFromJson(contentJson));
233 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
234 m_face.put(result);
235
236 if (certRequest.getStatus() == ChallengeModule::SUCCESS) {
237 issueCertificate(certRequest, caItem);
238 }
239}
240
241void
242CaModule::handleStatus(const Interest& request, const CaItem& caItem)
243{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700244 // STATUS Naming Convention: /CA-prefix/CA/_STATUS/{Request-ID JSON}/[Signature components]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800245 _LOG_TRACE("Handle STATUS request");
246
247 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
248 if (certRequest.getRequestId().empty()) {
249 return;
250 }
251
252 if (!security::verifySignature(request, certRequest.getCert())) {
253 _LOG_TRACE("Error: Interest with bad signature.");
254 return;
255 }
256
257 std::string challengeType = certRequest.getChallengeType();
258 auto challenge = ChallengeModule::createChallengeModule(challengeType);
259 if (challenge == nullptr) {
260 _LOG_TRACE("Unrecognized challenge type " << challengeType);
261 return;
262 }
263 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
264
265 Data result;
266 result.setName(request.getName());
267 result.setContent(dataContentFromJson(contentJson));
268 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
269 m_face.put(result);
270}
271
272void
273CaModule::handleDownload(const Interest& request, const CaItem& caItem)
274{
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700275 // DOWNLOAD Naming Convention: /CA-prefix/CA/_DOWNLOAD/{Request-ID JSON}
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800276 _LOG_TRACE("Handle DOWNLOAD request");
277
278 JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caItem.m_caName.size() + 2);
279 std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
280 security::v2::Certificate signedCert;
281 try {
282 signedCert = m_storage->getCertificate(requestId);
283 }
284 catch (const std::exception& e) {
285 _LOG_TRACE("Error: " << e.what());
286 return;
287 }
288
289 Data result;
290 result.setName(request.getName());
291 result.setContent(signedCert.wireEncode());
Zhiyi Zhang4d89fe02017-04-28 18:51:51 -0700292 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800293 m_face.put(result);
294}
295
296void
297CaModule::issueCertificate(const CertificateRequest& certRequest, const CaItem& caItem)
298{
299 Name certName = certRequest.getCert().getKeyName();
300 certName.append("NDNCERT").appendVersion();
301 security::v2::Certificate newCert;
302 newCert.setName(certName);
303 newCert.setContent(certRequest.getCert().getContent());
304 SignatureInfo signatureInfo;
305 security::ValidityPeriod period(time::system_clock::now(),
306 time::system_clock::now() + caItem.m_validityPeriod);
307 signatureInfo.setValidityPeriod(period);
308 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_CERT,
309 caItem.m_anchor, signatureInfo);
310 newCert.setFreshnessPeriod(caItem.m_freshnessPeriod);
311
312 m_keyChain.sign(newCert, signingInfo);
313 try {
314 m_storage->addCertificate(certRequest.getRequestId(), newCert);
315 m_storage->deleteRequest(certRequest.getRequestId());
316 _LOG_TRACE("New Certificate Issued " << certName);
317 }
318 catch (const std::exception& e) {
319 _LOG_TRACE("Error: Cannot add issued cert and remove the request " << e.what());
320 return;
321 }
322}
323
324CertificateRequest
325CaModule::getCertificateRequest(const Interest& request, const Name& caName)
326{
327 JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caName.size() + 2);
328 std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
329 CertificateRequest certRequest;
330 try {
331 certRequest = m_storage->getRequest(requestId);
332 }
333 catch (const std::exception& e) {
334 _LOG_TRACE("Error: " << e.what());
335 }
336 return certRequest;
337}
338
339void
340CaModule::onRegisterFailed(const std::string& reason)
341{
342 _LOG_TRACE("Error: failed to register prefix in local hub's daemon, REASON: " << reason);
343}
344
345Block
346CaModule::dataContentFromJson(const JsonSection& jsonSection)
347{
348 std::stringstream ss;
349 boost::property_tree::write_json(ss, jsonSection);
350 return makeStringBlock(ndn::tlv::Content, ss.str());
351}
352
353JsonSection
354CaModule::jsonFromNameComponent(const Name& name, int pos)
355{
356 std::string jsonString;
357 try {
358 jsonString = encoding::readString(name.at(pos));
359 }
360 catch (const std::exception& e) {
361 _LOG_TRACE(e.what());
362 return JsonSection();
363 }
364 std::istringstream ss(jsonString);
365 JsonSection json;
366 boost::property_tree::json_parser::read_json(ss, json);
367 return json;
368}
369
370} // namespace ndncert
371} // namespace ndn