blob: 715fe1da95c8e9c5d622e9d4545406cadb4d0f74 [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;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -070034static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070035
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080036_LOG_INIT(ndncert.ca);
37
38CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
39 const std::string& configPath, const std::string& storageType)
40 : m_face(face)
41 , m_keyChain(keyChain)
42{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070043 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080044 m_config.load(configPath);
45 m_storage = CaStorage::createCaStorage(storageType);
46
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080047 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080048}
49
50CaModule::~CaModule()
51{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070052 for (auto handle : m_interestFilterHandles) {
53 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080054 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070055 for (auto handle : m_registeredPrefixHandles) {
56 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080057 }
58}
59
60void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080061CaModule::registerPrefix()
62{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070063 // register localhop discovery prefix
64 Name localhopProbePrefix("/localhop/CA/PROBE/INFO");
65 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopProbePrefix),
66 bind(&CaModule::onProbe, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080067 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070068 m_registeredPrefixHandles.push_back(prefixId);
69 _LOG_TRACE("Prefix " << localhopProbePrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080070
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070071 // register prefixes
72 Name prefix = m_config.m_caName;
73 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080074
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 prefixId = m_face.registerPrefix(prefix,
76 [&] (const Name& name) {
77 // register PROBE prefix
78 auto filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
79 bind(&CaModule::onProbe, this, _2));
80 m_interestFilterHandles.push_back(filterId);
81
82 // register NEW prefix
83 filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
84 bind(&CaModule::onNew, this, _2));
85 m_interestFilterHandles.push_back(filterId);
86
87 // register SELECT prefix
88 filterId = m_face.setInterestFilter(Name(name).append("_CHALLENGE"),
89 bind(&CaModule::onChallenge, this, _2));
90 m_interestFilterHandles.push_back(filterId);
91
92 // register DOWNLOAD prefix
93 filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
94 bind(&CaModule::onDownload, this, _2));
95 m_interestFilterHandles.push_back(filterId);
96 _LOG_TRACE("Prefix " << name << " got registered");
97 },
98 bind(&CaModule::onRegisterFailed, this, _2));
99 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800100}
101
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800102bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700103CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800104{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700105 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800106 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800107}
108
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800109bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700110CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800111{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700112 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800113 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800114}
115
116void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700117CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800118{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700119 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent|INFO]
120 _LOG_TRACE("Receive PROBE request");
121 JsonSection contentJson;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800122
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700123 // process PROBE INFO requests
124 if (readString(request.getName().at(-1)) == "INFO") {
125 contentJson = genProbeResponseJson();
126 }
127 else {
128 // if not a PROBE INFO, find an available name
129 std::string availableId = "";
130 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700131 if (parameterJson.empty()) {
132 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
133 return;
134 }
Yufeng Zhang424d0362019-06-12 16:48:27 -0700135 //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));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700158 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700159 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800160 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700161 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800162}
163
164void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700165CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800166{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700167 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700168 // get ECDH pub key and cert request
169 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700170 if (parameterJson.empty()) {
171 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
172 return;
173 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700174 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
175
176 // get server's ECDH pub key
177 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
178 m_ecdh.deriveSecret(peerKeyBase64);
179 // generate salt for HKDF
180 auto saltInt = random::generateSecureWord64();
181 uint8_t salt[sizeof(saltInt)];
182 std::memcpy(salt, &saltInt, sizeof(saltInt));
183 // hkdf
184 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
185 salt, sizeof(saltInt), m_aesKey, 32);
186
187 // parse certificate request
188 std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
189 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800190 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700191 std::stringstream ss(certRequestStr);
192 clientCert = io::load<security::v2::Certificate>(ss);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800193 }
194 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700195 _LOG_ERROR("Unrecognized certificate request " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800196 return;
197 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700198
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700199 // parse probe token if any
200 std::string probeTokenStr = parameterJson.get("probe-token", "");
201 shared_ptr<Data> probeToken = nullptr;
202 if (probeTokenStr != "") {
203 try {
204 std::stringstream ss(probeTokenStr);
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700205 probeToken = io::load<Data>(ss);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700206 }
207 catch (const std::exception& e) {
208 _LOG_ERROR("Unrecognized probe token " << e.what());
209 return;
210 }
211 }
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700212 if (probeToken == nullptr && m_config.m_probe != "") {
213 // the CA requires PROBE before NEW
214 _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
215 return;
216 }
217 else if (probeToken != nullptr) {
218 // check whether the carried probe token is a PROBE Data packet
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700219 Name prefix = m_config.m_caName;
220 prefix.append("CA").append("_PROBE");
221 if (!prefix.isPrefixOf(probeToken->getName())) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700222 _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700223 return;
224 }
225 }
226
227 // verify the self-signed certificate, the request, and the token
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700228 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
229 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
230 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
231 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
232 return;
233 }
234 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700235 _LOG_TRACE("Cert request with bad signature.");
236 return;
237 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700238 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700239 _LOG_TRACE("Interest with bad signature.");
240 return;
241 }
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700242 if (probeToken != nullptr) {
243 const auto& pib = m_keyChain.getPib();
244 const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
245 const auto& caCert = key.getDefaultCertificate();
246 if (!security::verifySignature(*probeToken, caCert)) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700247 _LOG_TRACE("PROBE Token with bad signature.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700248 return;
249 }
250 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700251
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700252 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800253 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700254 CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700255 if (probeToken != nullptr) {
256 certRequest.setProbeToken(probeToken);
257 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800258 try {
259 m_storage->addRequest(certRequest);
260 }
261 catch (const std::exception& e) {
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700262 _LOG_TRACE("Cannot add new request instance into the storage " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800263 return;
264 }
265
266 Data result;
267 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700268 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700269 result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
270 std::to_string(saltInt),
271 certRequest,
272 m_config.m_supportedChallenges)));
273 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800274 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700275
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700276 if (m_config.m_statusUpdateCallback) {
277 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800278 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800279}
280
281void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700282CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800283{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700284 // get certificate request state
285 CertificateRequest certRequest = getCertificateRequest(request);
286 if (certRequest.m_requestId == "") {
287 // cannot get the request state
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800288 return;
289 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700290 // verify signature
291 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700292 _LOG_TRACE("Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800293 return;
294 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700295 // decrypt the parameters
296 auto paramJsonPayload = parseEncBlock(m_ecdh.context->sharedSecret,
297 m_ecdh.context->sharedSecretLen,
298 request.getApplicationParameters());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700299 if (paramJsonPayload.size() == 0) {
300 _LOG_ERROR("Got an empty buffer from content decryption.");
301 return;
302 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700303 std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
304 std::istringstream ss(paramJsonStr);
305 JsonSection paramJson;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700306 try {
307 boost::property_tree::json_parser::read_json(ss, paramJson);
308 }
309 catch (const std::exception& e) {
310 _LOG_ERROR("Cannot read JSON from decrypted content " << e.what());
311 return;
312 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800313
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700314 // load the corresponding challenge module
315 std::string challengeType = paramJson.get<std::string>(JSON_CLIENT_SELECTED_CHALLENGE);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800316 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700317 JsonSection contentJson;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800318 if (challenge == nullptr) {
319 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700320 certRequest.m_status = STATUS_FAILURE;
321 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
322 contentJson = genChallengeResponseJson(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800323 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700324 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700325 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
326 // let challenge module handle the request
327 challenge->handleChallengeRequest(paramJson, certRequest);
328 if (certRequest.m_status == STATUS_FAILURE) {
329 // if challenge failed
330 m_storage->deleteRequest(certRequest.m_requestId);
331 contentJson = genChallengeResponseJson(certRequest);
332 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700333 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700334 else if (certRequest.m_status == STATUS_PENDING) {
335 // if challenge succeeded
336 auto issuedCert = issueCertificate(certRequest);
337 certRequest.m_cert = issuedCert;
338 certRequest.m_status = STATUS_SUCCESS;
339 try {
340 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
341 m_storage->deleteRequest(certRequest.m_requestId);
342 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
343 }
344 catch (const std::exception& e) {
345 _LOG_ERROR("Cannot add issued cert and remove the request " << e.what());
346 return;
347 }
348 if (m_config.m_statusUpdateCallback) {
349 m_config.m_statusUpdateCallback(certRequest);
350 }
351 contentJson = genChallengeResponseJson(certRequest);
352 contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
353 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800354 }
355 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700356 try {
357 m_storage->updateRequest(certRequest);
358 }
359 catch (const std::exception& e) {
360 _LOG_TRACE("Cannot update request instance " << e.what());
361 return;
362 }
363 contentJson = genChallengeResponseJson(certRequest);
364 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800365 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800366 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700367
368 Data result;
369 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700370 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700371
372 // encrypt the content
373 std::stringstream ss2;
374 boost::property_tree::write_json(ss2, contentJson);
375 auto payload = ss2.str();
376 auto contentBlock = genEncBlock(tlv::Content, m_ecdh.context->sharedSecret,
377 m_ecdh.context->sharedSecretLen,
378 (const uint8_t*)payload.c_str(), payload.size());
379 result.setContent(contentBlock);
380 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
381 m_face.put(result);
382
383 if (m_config.m_statusUpdateCallback) {
384 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800385 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700386}
387
388void
389CaModule::onDownload(const Interest& request)
390{
391 auto requestId = readString(request.getName().at(-1));
392 security::v2::Certificate signedCert;
393 try {
394 signedCert = m_storage->getCertificate(requestId);
395 }
396 catch (const std::exception& e) {
397 _LOG_ERROR("Cannot read signed cert " << requestId << " from ca database " << e.what());
398 return;
399 }
400 Data result;
401 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700402 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700403 result.setContent(signedCert.wireEncode());
404 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800405 m_face.put(result);
406}
407
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800408security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700409CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800410{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700411 auto expectedPeriod =
412 certRequest.m_cert.getValidityPeriod().getPeriod();
413
414 time::system_clock::TimePoint startingTime, endingTime;
415 if (expectedPeriod.first > time::system_clock::now()
416 && expectedPeriod.first < time::system_clock::now()
417 + m_config.m_validityPeriod)
418 {
419 startingTime = expectedPeriod.first;
420 }
421 else {
422 startingTime = time::system_clock::now();
423 }
424 if (expectedPeriod.second < time::system_clock::now() + m_config.m_validityPeriod) {
425 endingTime = expectedPeriod.second;
426 }
427 else {
428 endingTime = time::system_clock::now() + m_config.m_validityPeriod;
429 }
430 security::ValidityPeriod period(startingTime, endingTime);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800431 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700432
433 Name certName = certRequest.m_cert.getKeyName();
434 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800435 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700436 newCert.setContent(certRequest.m_cert.getContent());
437 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800438 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800439 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700440 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700441 m_config.m_caName, signatureInfo);
442 newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800443
444 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700445 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800446 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800447}
448
449CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700450CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800451{
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700452 std::string requestId = "";
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800453 CertificateRequest certRequest;
454 try {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700455 requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
456 _LOG_TRACE("Request Id to query the database " << requestId);
457 }
458 catch (const std::exception& e) {
459 _LOG_ERROR(e.what());
460 }
461 try {
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800462 certRequest = m_storage->getRequest(requestId);
463 }
464 catch (const std::exception& e) {
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700465 _LOG_ERROR(e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800466 }
467 return certRequest;
468}
469
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700470/**
471 * @brief Generate JSON file to response PROBE insterest
472 *
473 * PROBE response JSON format:
474 * {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700475 * "name": "@p identifier"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700476 * }
477 */
478const JsonSection
Yufeng Zhang424d0362019-06-12 16:48:27 -0700479CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700480{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700481 std::vector<std::string> fields;
482 std::string delimiter = ":";
483 size_t last = 0;
484 size_t next = 0;
485 while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
486 fields.push_back(m_probe.substr(last, next - last));
487 last = next + 1;
488 }
489 fields.push_back(m_probe.substr(last));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700490 JsonSection root;
Yufeng Zhang424d0362019-06-12 16:48:27 -0700491
492 for (size_t i = 0; i < fields.size(); ++i) {
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700493 root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
Yufeng Zhang424d0362019-06-12 16:48:27 -0700494 }
495
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700496 root.put(JSON_CA_NAME, identifier.toUri());
497 return root;
498}
499
500/**
501 * @brief Generate JSON file to response NEW interest
502 *
503 * Target JSON format:
504 * {
505 * "ecdh-pub": "@p echdPub",
506 * "salt": "@p salt"
507 * "request-id": "@p requestId",
508 * "status": "@p status",
509 * "challenges": [
510 * {
511 * "challenge-id": ""
512 * },
513 * {
514 * "challenge-id": ""
515 * },
516 * ...
517 * ]
518 * }
519 */
520const JsonSection
521CaModule::genProbeResponseJson()
522{
523 JsonSection root;
524 // ca-prefix
525 Name caName = m_config.m_caName;
526 root.put("ca-prefix", caName.toUri());
527
528 // ca-info
529 const auto& pib = m_keyChain.getPib();
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700530 const auto& identity = pib.getIdentity(m_config.m_caName);
531 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700532 std::string caInfo = "";
533 if (m_config.m_caInfo == "") {
534 caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
535 }
536 else {
537 caInfo = m_config.m_caInfo;
538 }
539 root.put("ca-info", caInfo);
540
541 // probe
542 root.put("probe", m_config.m_probe);
543
544 // certificate
545 std::stringstream ss;
546 io::save(cert, ss);
547 root.put("certificate", ss.str());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700548 return root;
549}
550
551const JsonSection
552CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
553 const CertificateRequest& request,
554 const std::list<std::string>& challenges)
555{
556 JsonSection root;
557 JsonSection challengesSection;
558 root.put(JSON_CA_ECDH, ecdhKey);
559 root.put(JSON_CA_SALT, salt);
560 root.put(JSON_CA_EQUEST_ID, request.m_requestId);
561 root.put(JSON_CA_STATUS, std::to_string(request.m_status));
562
563 for (const auto& entry : challenges) {
564 JsonSection challenge;
565 challenge.put(JSON_CA_CHALLENGE_ID, entry);
566 challengesSection.push_back(std::make_pair("", challenge));
567 }
568 root.add_child(JSON_CA_CHALLENGES, challengesSection);
569 return root;
570}
571
572const JsonSection
573CaModule::genChallengeResponseJson(const CertificateRequest& request)
574{
575 JsonSection root;
576 JsonSection challengesSection;
577 root.put(JSON_CA_STATUS, request.m_status);
578 root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
579 root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
580 root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
581 return root;
582}
583
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800584void
585CaModule::onRegisterFailed(const std::string& reason)
586{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700587 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800588}
589
590Block
591CaModule::dataContentFromJson(const JsonSection& jsonSection)
592{
593 std::stringstream ss;
594 boost::property_tree::write_json(ss, jsonSection);
595 return makeStringBlock(ndn::tlv::Content, ss.str());
596}
597
598JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700599CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800600{
601 std::string jsonString;
602 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700603 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700604 std::istringstream ss(jsonString);
605 JsonSection json;
606 boost::property_tree::json_parser::read_json(ss, json);
607 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800608 }
609 catch (const std::exception& e) {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700610 _LOG_ERROR("Cannot read JSON string from TLV Value " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800611 return JsonSection();
612 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800613}
614
615} // namespace ndncert
616} // namespace ndn