blob: b6233e903f99feafc487658fb1fe9bfb7fd890c1 [file] [log] [blame]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07003 * Copyright (c) 2017-2019, Regents of the University of California.
Zhiyi Zhangf5246c42017-01-26 09:39:20 -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 "ca-module.hpp"
22#include "challenge-module.hpp"
23#include "logging.hpp"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070024#include "crypto-support/enc-tlv.hpp"
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080025#include <ndn-cxx/util/io.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080026#include <ndn-cxx/security/verification-helpers.hpp>
27#include <ndn-cxx/security/signing-helpers.hpp>
Junxiao Shi7c068032017-05-28 13:40:47 +000028#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080029
30namespace ndn {
31namespace ndncert {
32
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070033static const int IS_SUBNAME_MIN_OFFSET = 5;
34
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080035_LOG_INIT(ndncert.ca);
36
37CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
38 const std::string& configPath, const std::string& storageType)
39 : m_face(face)
40 , m_keyChain(keyChain)
41{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070042 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080043 m_config.load(configPath);
44 m_storage = CaStorage::createCaStorage(storageType);
45
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080046 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080047}
48
49CaModule::~CaModule()
50{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070051 for (auto handle : m_interestFilterHandles) {
52 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080053 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070054 for (auto handle : m_registeredPrefixHandles) {
55 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080056 }
57}
58
59void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080060CaModule::registerPrefix()
61{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062 // register localhop discovery prefix
63 Name localhopProbePrefix("/localhop/CA/PROBE/INFO");
64 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopProbePrefix),
65 bind(&CaModule::onProbe, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080066 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070067 m_registeredPrefixHandles.push_back(prefixId);
68 _LOG_TRACE("Prefix " << localhopProbePrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080069
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070070 // register prefixes
71 Name prefix = m_config.m_caName;
72 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080073
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070074 prefixId = m_face.registerPrefix(prefix,
75 [&] (const Name& name) {
76 // register PROBE prefix
77 auto filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
78 bind(&CaModule::onProbe, this, _2));
79 m_interestFilterHandles.push_back(filterId);
80
81 // register NEW prefix
82 filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
83 bind(&CaModule::onNew, this, _2));
84 m_interestFilterHandles.push_back(filterId);
85
86 // register SELECT prefix
87 filterId = m_face.setInterestFilter(Name(name).append("_CHALLENGE"),
88 bind(&CaModule::onChallenge, this, _2));
89 m_interestFilterHandles.push_back(filterId);
90
91 // register DOWNLOAD prefix
92 filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
93 bind(&CaModule::onDownload, this, _2));
94 m_interestFilterHandles.push_back(filterId);
95 _LOG_TRACE("Prefix " << name << " got registered");
96 },
97 bind(&CaModule::onRegisterFailed, this, _2));
98 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080099}
100
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800101bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700102CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800103{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700104 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800105 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800106}
107
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800108bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700109CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800110{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800112 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800113}
114
115void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700116CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800117{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700118 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent|INFO]
119 _LOG_TRACE("Receive PROBE request");
120 JsonSection contentJson;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800121
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700122 // process PROBE INFO requests
123 if (readString(request.getName().at(-1)) == "INFO") {
124 contentJson = genProbeResponseJson();
125 }
126 else {
127 // if not a PROBE INFO, find an available name
128 std::string availableId = "";
129 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700130 if (parameterJson.empty()) {
131 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
132 return;
133 }
Yufeng Zhang424d0362019-06-12 16:48:27 -0700134
135 //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700136 if (m_config.m_probeHandler) {
137 try {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700138 availableId = m_config.m_probeHandler(parameterJson);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700139 }
140 catch (const std::exception& e) {
141 _LOG_TRACE("Cannot find PROBE input from PROBE parameters " << e.what());
142 return;
143 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800144 }
145 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700146 // if there is no app-specified name lookup, use a random name id
147 availableId = std::to_string(random::generateSecureWord64());
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800148 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700149 Name newIdentityName = m_config.m_caName;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700150 newIdentityName.append(availableId);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700151 _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
Yufeng Zhang424d0362019-06-12 16:48:27 -0700152 contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800153 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800154
155 Data result;
156 result.setName(request.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700157 result.setContent(dataContentFromJson(contentJson));
158 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800159 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700160 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800161}
162
163void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700164CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800165{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700166 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700167 // get ECDH pub key and cert request
168 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700169 if (parameterJson.empty()) {
170 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
171 return;
172 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700173 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
174
175 // get server's ECDH pub key
176 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
177 m_ecdh.deriveSecret(peerKeyBase64);
178 // generate salt for HKDF
179 auto saltInt = random::generateSecureWord64();
180 uint8_t salt[sizeof(saltInt)];
181 std::memcpy(salt, &saltInt, sizeof(saltInt));
182 // hkdf
183 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
184 salt, sizeof(saltInt), m_aesKey, 32);
185
186 // parse certificate request
187 std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
188 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800189 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190 std::stringstream ss(certRequestStr);
191 clientCert = io::load<security::v2::Certificate>(ss);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800192 }
193 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700194 _LOG_ERROR("Unrecognized certificate request " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800195 return;
196 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700197
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700198 // parse probe token if any
199 std::string probeTokenStr = parameterJson.get("probe-token", "");
200 shared_ptr<Data> probeToken = nullptr;
201 if (probeTokenStr != "") {
202 try {
203 std::stringstream ss(probeTokenStr);
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700204 probeToken = io::load<Data>(ss);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700205 }
206 catch (const std::exception& e) {
207 _LOG_ERROR("Unrecognized probe token " << e.what());
208 return;
209 }
210 }
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700211 if (probeToken == nullptr && m_config.m_probe != "") {
212 // the CA requires PROBE before NEW
213 _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
214 return;
215 }
216 else if (probeToken != nullptr) {
217 // check whether the carried probe token is a PROBE Data packet
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700218 Name prefix = m_config.m_caName;
219 prefix.append("CA").append("_PROBE");
220 if (!prefix.isPrefixOf(probeToken->getName())) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700221 _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700222 return;
223 }
224 }
225
226 // verify the self-signed certificate, the request, and the token
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700227 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
228 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
229 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
230 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
231 return;
232 }
233 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700234 _LOG_TRACE("Cert request with bad signature.");
235 return;
236 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700237 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700238 _LOG_TRACE("Interest with bad signature.");
239 return;
240 }
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700241 if (probeToken != nullptr) {
242 const auto& pib = m_keyChain.getPib();
243 const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
244 const auto& caCert = key.getDefaultCertificate();
245 if (!security::verifySignature(*probeToken, caCert)) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700246 _LOG_TRACE("PROBE Token with bad signature.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700247 return;
248 }
249 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700250
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700251 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800252 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700253 CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700254 if (probeToken != nullptr) {
255 certRequest.setProbeToken(probeToken);
256 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800257 try {
258 m_storage->addRequest(certRequest);
259 }
260 catch (const std::exception& e) {
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700261 _LOG_TRACE("Cannot add new request instance into the storage " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800262 return;
263 }
264
265 Data result;
266 result.setName(request.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700267 result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
268 std::to_string(saltInt),
269 certRequest,
270 m_config.m_supportedChallenges)));
271 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800272 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700273
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700274 if (m_config.m_statusUpdateCallback) {
275 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800276 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800277}
278
279void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700280CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800281{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700282 // get certificate request state
283 CertificateRequest certRequest = getCertificateRequest(request);
284 if (certRequest.m_requestId == "") {
285 // cannot get the request state
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800286 return;
287 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700288 // verify signature
289 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700290 _LOG_TRACE("Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800291 return;
292 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700293 // decrypt the parameters
294 auto paramJsonPayload = parseEncBlock(m_ecdh.context->sharedSecret,
295 m_ecdh.context->sharedSecretLen,
296 request.getApplicationParameters());
297 std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
298 std::istringstream ss(paramJsonStr);
299 JsonSection paramJson;
300 boost::property_tree::json_parser::read_json(ss, paramJson);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800301
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700302 // load the corresponding challenge module
303 std::string challengeType = paramJson.get<std::string>(JSON_CLIENT_SELECTED_CHALLENGE);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800304 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700305 JsonSection contentJson;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800306 if (challenge == nullptr) {
307 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700308 certRequest.m_status = STATUS_FAILURE;
309 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
310 contentJson = genChallengeResponseJson(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800311 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700312 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700313 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
314 // let challenge module handle the request
315 challenge->handleChallengeRequest(paramJson, certRequest);
316 if (certRequest.m_status == STATUS_FAILURE) {
317 // if challenge failed
318 m_storage->deleteRequest(certRequest.m_requestId);
319 contentJson = genChallengeResponseJson(certRequest);
320 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700321 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700322 else if (certRequest.m_status == STATUS_PENDING) {
323 // if challenge succeeded
324 auto issuedCert = issueCertificate(certRequest);
325 certRequest.m_cert = issuedCert;
326 certRequest.m_status = STATUS_SUCCESS;
327 try {
328 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
329 m_storage->deleteRequest(certRequest.m_requestId);
330 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
331 }
332 catch (const std::exception& e) {
333 _LOG_ERROR("Cannot add issued cert and remove the request " << e.what());
334 return;
335 }
336 if (m_config.m_statusUpdateCallback) {
337 m_config.m_statusUpdateCallback(certRequest);
338 }
339 contentJson = genChallengeResponseJson(certRequest);
340 contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
341 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800342 }
343 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700344 try {
345 m_storage->updateRequest(certRequest);
346 }
347 catch (const std::exception& e) {
348 _LOG_TRACE("Cannot update request instance " << e.what());
349 return;
350 }
351 contentJson = genChallengeResponseJson(certRequest);
352 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800353 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800354 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700355
356 Data result;
357 result.setName(request.getName());
358
359 // encrypt the content
360 std::stringstream ss2;
361 boost::property_tree::write_json(ss2, contentJson);
362 auto payload = ss2.str();
363 auto contentBlock = genEncBlock(tlv::Content, m_ecdh.context->sharedSecret,
364 m_ecdh.context->sharedSecretLen,
365 (const uint8_t*)payload.c_str(), payload.size());
366 result.setContent(contentBlock);
367 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
368 m_face.put(result);
369
370 if (m_config.m_statusUpdateCallback) {
371 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800372 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700373}
374
375void
376CaModule::onDownload(const Interest& request)
377{
378 auto requestId = readString(request.getName().at(-1));
379 security::v2::Certificate signedCert;
380 try {
381 signedCert = m_storage->getCertificate(requestId);
382 }
383 catch (const std::exception& e) {
384 _LOG_ERROR("Cannot read signed cert " << requestId << " from ca database " << e.what());
385 return;
386 }
387 Data result;
388 result.setName(request.getName());
389 result.setContent(signedCert.wireEncode());
390 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800391 m_face.put(result);
392}
393
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800394security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700395CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800396{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700397 auto expectedPeriod =
398 certRequest.m_cert.getValidityPeriod().getPeriod();
399
400 time::system_clock::TimePoint startingTime, endingTime;
401 if (expectedPeriod.first > time::system_clock::now()
402 && expectedPeriod.first < time::system_clock::now()
403 + m_config.m_validityPeriod)
404 {
405 startingTime = expectedPeriod.first;
406 }
407 else {
408 startingTime = time::system_clock::now();
409 }
410 if (expectedPeriod.second < time::system_clock::now() + m_config.m_validityPeriod) {
411 endingTime = expectedPeriod.second;
412 }
413 else {
414 endingTime = time::system_clock::now() + m_config.m_validityPeriod;
415 }
416 security::ValidityPeriod period(startingTime, endingTime);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800417 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700418
419 Name certName = certRequest.m_cert.getKeyName();
420 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800421 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700422 newCert.setContent(certRequest.m_cert.getContent());
423 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800424 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800425 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700426 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700427 m_config.m_caName, signatureInfo);
428 newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800429
430 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700431 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800432 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800433}
434
435CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700436CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800437{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700438 std::string requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700439 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800440 CertificateRequest certRequest;
441 try {
442 certRequest = m_storage->getRequest(requestId);
443 }
444 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700445 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800446 }
447 return certRequest;
448}
449
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700450/**
451 * @brief Generate JSON file to response PROBE insterest
452 *
453 * PROBE response JSON format:
454 * {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700455 * "name": "@p identifier"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700456 * }
457 */
458const JsonSection
Yufeng Zhang424d0362019-06-12 16:48:27 -0700459CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700460{
Yufeng Zhang424d0362019-06-12 16:48:27 -0700461 std::vector<std::string> fields;
462 std::string delimiter = ":";
463 size_t last = 0;
464 size_t next = 0;
465 while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
466 fields.push_back(m_probe.substr(last, next - last));
467 last = next + 1;
468 }
469 fields.push_back(m_probe.substr(last));
470
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700471 JsonSection root;
Yufeng Zhang424d0362019-06-12 16:48:27 -0700472
473 for (size_t i = 0; i < fields.size(); ++i) {
474 root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
475 }
476
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700477 root.put(JSON_CA_NAME, identifier.toUri());
478 return root;
479}
480
481/**
482 * @brief Generate JSON file to response NEW interest
483 *
484 * Target JSON format:
485 * {
486 * "ecdh-pub": "@p echdPub",
487 * "salt": "@p salt"
488 * "request-id": "@p requestId",
489 * "status": "@p status",
490 * "challenges": [
491 * {
492 * "challenge-id": ""
493 * },
494 * {
495 * "challenge-id": ""
496 * },
497 * ...
498 * ]
499 * }
500 */
501const JsonSection
502CaModule::genProbeResponseJson()
503{
504 JsonSection root;
505 // ca-prefix
506 Name caName = m_config.m_caName;
507 root.put("ca-prefix", caName.toUri());
508
509 // ca-info
510 const auto& pib = m_keyChain.getPib();
511 auto identity = pib.getIdentity(m_config.m_caName);
512 auto cert = identity.getDefaultKey().getDefaultCertificate();
513 std::string caInfo = "";
514 if (m_config.m_caInfo == "") {
515 caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
516 }
517 else {
518 caInfo = m_config.m_caInfo;
519 }
520 root.put("ca-info", caInfo);
521
522 // probe
523 root.put("probe", m_config.m_probe);
524
525 // certificate
526 std::stringstream ss;
527 io::save(cert, ss);
528 root.put("certificate", ss.str());
529
530 return root;
531}
532
533const JsonSection
534CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
535 const CertificateRequest& request,
536 const std::list<std::string>& challenges)
537{
538 JsonSection root;
539 JsonSection challengesSection;
540 root.put(JSON_CA_ECDH, ecdhKey);
541 root.put(JSON_CA_SALT, salt);
542 root.put(JSON_CA_EQUEST_ID, request.m_requestId);
543 root.put(JSON_CA_STATUS, std::to_string(request.m_status));
544
545 for (const auto& entry : challenges) {
546 JsonSection challenge;
547 challenge.put(JSON_CA_CHALLENGE_ID, entry);
548 challengesSection.push_back(std::make_pair("", challenge));
549 }
550 root.add_child(JSON_CA_CHALLENGES, challengesSection);
551 return root;
552}
553
554const JsonSection
555CaModule::genChallengeResponseJson(const CertificateRequest& request)
556{
557 JsonSection root;
558 JsonSection challengesSection;
559 root.put(JSON_CA_STATUS, request.m_status);
560 root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
561 root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
562 root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
563 return root;
564}
565
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800566void
567CaModule::onRegisterFailed(const std::string& reason)
568{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700569 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800570}
571
572Block
573CaModule::dataContentFromJson(const JsonSection& jsonSection)
574{
575 std::stringstream ss;
576 boost::property_tree::write_json(ss, jsonSection);
577 return makeStringBlock(ndn::tlv::Content, ss.str());
578}
579
580JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700581CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800582{
583 std::string jsonString;
584 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700585 jsonString = encoding::readString(block);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800586 }
587 catch (const std::exception& e) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700588 _LOG_ERROR("Cannot read JSON string from TLV Value" << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800589 return JsonSection();
590 }
591 std::istringstream ss(jsonString);
592 JsonSection json;
593 boost::property_tree::json_parser::read_json(ss, json);
594 return json;
595}
596
597} // namespace ndncert
598} // namespace ndn