blob: b0d0e4479e91bd0c4ff187699f9bee8a5c4a7d7e [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{
95 // PROBE Naming Convention: /CA-prefix/_PROBE/<Probe Information>
96 _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{
121 // NEW Naming Convention: /CA-prefix/_NEW/<certificate-request>/[signature]
122 _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{
154 // SELECT Naming Convention: /CA-prefix/_SELECT/{Request-ID JSON}/<ChallengeID>/
155 // {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 }
176 auto challenge = ChallengeModule::createChallengeModule(challengeType);
177 if (challenge == nullptr) {
178 _LOG_TRACE("Unrecognized challenge type " << challengeType);
179 return;
180 }
181 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
182 try {
183 m_storage->updateRequest(certRequest);
184 }
185 catch (const std::exception& e) {
186 _LOG_TRACE("Cannot update request instance " << e.what());
187 return;
188 }
189
190 Data result;
191 result.setName(request.getName());
192 result.setContent(dataContentFromJson(contentJson));
193 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
194 m_face.put(result);
195}
196
197void
198CaModule::handleValidate(const Interest& request, const CaItem& caItem)
199{
200 // VALIDATE Naming Convention: /CA-prefix/_VALIDATE/{Request-ID JSON}/<ChallengeID>/
201 // {Param JSON}/[Signature components]
202 _LOG_TRACE("Handle VALIDATE request");
203
204 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
205 if (certRequest.getRequestId().empty()) {
206 return;
207 }
208
209 if (!security::verifySignature(request, certRequest.getCert())) {
210 _LOG_TRACE("Error: Interest with bad signature.");
211 return;
212 }
213
214 std::string challengeType = certRequest.getChallengeType();
215 auto challenge = ChallengeModule::createChallengeModule(challengeType);
216 if (challenge == nullptr) {
217 _LOG_TRACE("Unrecognized challenge type " << challengeType);
218 return;
219 }
220 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
221 try {
222 m_storage->updateRequest(certRequest);
223 }
224 catch (const std::exception& e) {
225 _LOG_TRACE("Cannot update request instance " << e.what());
226 return;
227 }
228
229 Data result;
230 result.setName(request.getName());
231 result.setContent(dataContentFromJson(contentJson));
232 m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
233 m_face.put(result);
234
235 if (certRequest.getStatus() == ChallengeModule::SUCCESS) {
236 issueCertificate(certRequest, caItem);
237 }
238}
239
240void
241CaModule::handleStatus(const Interest& request, const CaItem& caItem)
242{
243 // STATUS Naming Convention: /CA-prefix/_STATUS/{Request-ID JSON}/[Signature components]
244 _LOG_TRACE("Handle STATUS request");
245
246 CertificateRequest certRequest = getCertificateRequest(request, caItem.m_caName);
247 if (certRequest.getRequestId().empty()) {
248 return;
249 }
250
251 if (!security::verifySignature(request, certRequest.getCert())) {
252 _LOG_TRACE("Error: Interest with bad signature.");
253 return;
254 }
255
256 std::string challengeType = certRequest.getChallengeType();
257 auto challenge = ChallengeModule::createChallengeModule(challengeType);
258 if (challenge == nullptr) {
259 _LOG_TRACE("Unrecognized challenge type " << challengeType);
260 return;
261 }
262 JsonSection contentJson = challenge->handleChallengeRequest(request, certRequest);
263
264 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}
270
271void
272CaModule::handleDownload(const Interest& request, const CaItem& caItem)
273{
274 // DOWNLOAD Naming Convention: /CA-prefix/_DOWNLOAD/{Request-ID JSON}
275 _LOG_TRACE("Handle DOWNLOAD request");
276
277 JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caItem.m_caName.size() + 2);
278 std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
279 security::v2::Certificate signedCert;
280 try {
281 signedCert = m_storage->getCertificate(requestId);
282 }
283 catch (const std::exception& e) {
284 _LOG_TRACE("Error: " << e.what());
285 return;
286 }
287
288 Data result;
289 result.setName(request.getName());
290 result.setContent(signedCert.wireEncode());
291 m_keyChain.sign(result, signingWithSha256());
292 m_face.put(result);
293}
294
295void
296CaModule::issueCertificate(const CertificateRequest& certRequest, const CaItem& caItem)
297{
298 Name certName = certRequest.getCert().getKeyName();
299 certName.append("NDNCERT").appendVersion();
300 security::v2::Certificate newCert;
301 newCert.setName(certName);
302 newCert.setContent(certRequest.getCert().getContent());
303 SignatureInfo signatureInfo;
304 security::ValidityPeriod period(time::system_clock::now(),
305 time::system_clock::now() + caItem.m_validityPeriod);
306 signatureInfo.setValidityPeriod(period);
307 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_CERT,
308 caItem.m_anchor, signatureInfo);
309 newCert.setFreshnessPeriod(caItem.m_freshnessPeriod);
310
311 m_keyChain.sign(newCert, signingInfo);
312 try {
313 m_storage->addCertificate(certRequest.getRequestId(), newCert);
314 m_storage->deleteRequest(certRequest.getRequestId());
315 _LOG_TRACE("New Certificate Issued " << certName);
316 }
317 catch (const std::exception& e) {
318 _LOG_TRACE("Error: Cannot add issued cert and remove the request " << e.what());
319 return;
320 }
321}
322
323CertificateRequest
324CaModule::getCertificateRequest(const Interest& request, const Name& caName)
325{
326 JsonSection requestIdJson = jsonFromNameComponent(request.getName(), caName.size() + 2);
327 std::string requestId = requestIdJson.get(JSON_REQUEST_ID, "");
328 CertificateRequest certRequest;
329 try {
330 certRequest = m_storage->getRequest(requestId);
331 }
332 catch (const std::exception& e) {
333 _LOG_TRACE("Error: " << e.what());
334 }
335 return certRequest;
336}
337
338void
339CaModule::onRegisterFailed(const std::string& reason)
340{
341 _LOG_TRACE("Error: failed to register prefix in local hub's daemon, REASON: " << reason);
342}
343
344Block
345CaModule::dataContentFromJson(const JsonSection& jsonSection)
346{
347 std::stringstream ss;
348 boost::property_tree::write_json(ss, jsonSection);
349 return makeStringBlock(ndn::tlv::Content, ss.str());
350}
351
352JsonSection
353CaModule::jsonFromNameComponent(const Name& name, int pos)
354{
355 std::string jsonString;
356 try {
357 jsonString = encoding::readString(name.at(pos));
358 }
359 catch (const std::exception& e) {
360 _LOG_TRACE(e.what());
361 return JsonSection();
362 }
363 std::istringstream ss(jsonString);
364 JsonSection json;
365 boost::property_tree::json_parser::read_json(ss, json);
366 return json;
367}
368
369} // namespace ndncert
370} // namespace ndn