blob: d8e44deaa094f0841864f023d889ce87b13ce986 [file] [log] [blame]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
swa770de007bc2020-03-24 21:26:21 -07002/**
Davide Pesaventob48bbda2020-07-27 19:41:37 -04003 * Copyright (c) 2017-2020, 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 Zhang1a735bc2019-07-04 21:36:49 -070035static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070036
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080037_LOG_INIT(ndncert.ca);
38
39CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
40 const std::string& configPath, const std::string& storageType)
41 : m_face(face)
42 , m_keyChain(keyChain)
43{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070044 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080045 m_config.load(configPath);
46 m_storage = CaStorage::createCaStorage(storageType);
47
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080048 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080049}
50
51CaModule::~CaModule()
52{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070053 for (auto handle : m_interestFilterHandles) {
54 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080055 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070056 for (auto handle : m_registeredPrefixHandles) {
57 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080058 }
59}
60
61void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080062CaModule::registerPrefix()
63{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070064 // register localhop discovery prefix
swa77020643ac2020-03-26 02:24:45 -070065 Name localhopInfoPrefix("/localhop/CA/INFO");
66 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopInfoPrefix),
67 bind(&CaModule::onInfo, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080068 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070069 m_registeredPrefixHandles.push_back(prefixId);
swa77020643ac2020-03-26 02:24:45 -070070 _LOG_TRACE("Prefix " << localhopInfoPrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080071
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070072 // register prefixes
73 Name prefix = m_config.m_caName;
74 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080075
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070076 prefixId = m_face.registerPrefix(prefix,
77 [&] (const Name& name) {
swa77020643ac2020-03-26 02:24:45 -070078 // register INFO prefix
79 auto filterId = m_face.setInterestFilter(Name(name).append("INFO"),
80 bind(&CaModule::onInfo, this, _2));
81 m_interestFilterHandles.push_back(filterId);
82
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070083 // register PROBE prefix
swa77020643ac2020-03-26 02:24:45 -070084 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
85 bind(&CaModule::onProbe, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070086 m_interestFilterHandles.push_back(filterId);
87
88 // register NEW prefix
swa770de007bc2020-03-24 21:26:21 -070089 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070090 bind(&CaModule::onNew, this, _2));
91 m_interestFilterHandles.push_back(filterId);
92
93 // register SELECT prefix
swa770de007bc2020-03-24 21:26:21 -070094 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070095 bind(&CaModule::onChallenge, this, _2));
96 m_interestFilterHandles.push_back(filterId);
97
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070098 _LOG_TRACE("Prefix " << name << " got registered");
99 },
100 bind(&CaModule::onRegisterFailed, this, _2));
101 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800102}
103
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800104bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700105CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800106{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700107 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800108 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800109}
110
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800111bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700112CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800113{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700114 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800115 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800116}
117
118void
swa77020643ac2020-03-26 02:24:45 -0700119CaModule::onInfo(const Interest& request)
120{
121 _LOG_TRACE("Received INFO request");
122 JsonSection contentJson = genInfoResponseJson();
123 Data result;
124
125 result.setName(request.getName());
126 result.setContent(dataContentFromJson(contentJson));
127 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
128
129 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
130 m_face.put(result);
131
132 _LOG_TRACE("Handle INFO: send out the INFO response");
133}
134
135void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700136CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800137{
swa77020643ac2020-03-26 02:24:45 -0700138 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
139 _LOG_TRACE("Received PROBE request");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700140 JsonSection contentJson;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800141
swa77020643ac2020-03-26 02:24:45 -0700142 // process PROBE requests: find an available name
143 std::string availableId;
144 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
145 if (parameterJson.empty()) {
146 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
147 return;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700148 }
swa77020643ac2020-03-26 02:24:45 -0700149 //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
150 if (m_config.m_probeHandler) {
151 try {
152 availableId = m_config.m_probeHandler(parameterJson);
153 }
154 catch (const std::exception& e) {
155 _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700156 return;
157 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800158 }
swa77020643ac2020-03-26 02:24:45 -0700159 else {
160 // if there is no app-specified name lookup, use a random name id
161 availableId = std::to_string(random::generateSecureWord64());
162 }
163 Name newIdentityName = m_config.m_caName;
164 newIdentityName.append(availableId);
165 _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
166 contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800167
168 Data result;
169 result.setName(request.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700170 result.setContent(dataContentFromJson(contentJson));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700171 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700172 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800173 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700174 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800175}
176
177void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700178CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800179{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700180 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700181 // get ECDH pub key and cert request
182 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700183 if (parameterJson.empty()) {
184 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
185 return;
186 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700187 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700188 if (peerKeyBase64 == "") {
189 _LOG_ERROR("Empty JSON_CLIENT_ECDH obtained from the Interest parameter.");
190 return;
191 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700192
193 // get server's ECDH pub key
194 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700195 try {
196 m_ecdh.deriveSecret(peerKeyBase64);
197 }
198 catch (const std::exception& e) {
199 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
200 return;
201 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700202 // generate salt for HKDF
203 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700204 // hkdf
205 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
Zhiyi Zhang36706832019-07-04 21:33:03 -0700206 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700207
208 // parse certificate request
209 std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
210 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800211 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700212 std::stringstream ss(certRequestStr);
213 clientCert = io::load<security::v2::Certificate>(ss);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800214 }
215 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700216 _LOG_ERROR("Unrecognized certificate request: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800217 return;
218 }
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700219 // check the validity period
220 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
221 auto currentTime = time::system_clock::now();
222 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
223 _LOG_ERROR("Client requests a too old notBefore timepoint.");
224 return;
225 }
226 if (expectedPeriod.second > currentTime + m_config.m_validityPeriod ||
227 expectedPeriod.second <= expectedPeriod.first) {
228 _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
229 return;
230 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700231
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700232 // parse probe token if any
233 std::string probeTokenStr = parameterJson.get("probe-token", "");
234 shared_ptr<Data> probeToken = nullptr;
235 if (probeTokenStr != "") {
236 try {
237 std::stringstream ss(probeTokenStr);
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700238 probeToken = io::load<Data>(ss);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700239 }
240 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700241 _LOG_ERROR("Unrecognized probe token: " << e.what());
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700242 return;
243 }
244 }
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700245 if (probeToken == nullptr && m_config.m_probe != "") {
246 // the CA requires PROBE before NEW
247 _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
248 return;
249 }
250 else if (probeToken != nullptr) {
251 // check whether the carried probe token is a PROBE Data packet
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700252 Name prefix = m_config.m_caName;
swa770de007bc2020-03-24 21:26:21 -0700253 prefix.append("CA").append("PROBE");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700254 if (!prefix.isPrefixOf(probeToken->getName())) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700255 _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700256 return;
257 }
258 }
259
260 // verify the self-signed certificate, the request, and the token
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700261 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
262 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
263 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
264 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
265 return;
266 }
267 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700268 _LOG_ERROR("Cert request with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700269 return;
270 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700271 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700272 _LOG_ERROR("Interest with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700273 return;
274 }
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700275 if (probeToken != nullptr) {
276 const auto& pib = m_keyChain.getPib();
277 const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
278 const auto& caCert = key.getDefaultCertificate();
279 if (!security::verifySignature(*probeToken, caCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700280 _LOG_ERROR("PROBE Token with bad signature.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700281 return;
282 }
283 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700284
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700285 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800286 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700287 CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700288 if (probeToken != nullptr) {
289 certRequest.setProbeToken(probeToken);
290 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800291 try {
292 m_storage->addRequest(certRequest);
293 }
294 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700295 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800296 return;
297 }
298
299 Data result;
300 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700301 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700302 result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
303 std::to_string(saltInt),
304 certRequest,
305 m_config.m_supportedChallenges)));
306 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800307 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700308
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700309 if (m_config.m_statusUpdateCallback) {
310 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800311 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800312}
313
314void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700315CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800316{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700317 // get certificate request state
318 CertificateRequest certRequest = getCertificateRequest(request);
319 if (certRequest.m_requestId == "") {
320 // cannot get the request state
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700321 _LOG_ERROR("Cannot find certificate request state from CA's storage.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800322 return;
323 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700324 // verify signature
325 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700326 _LOG_ERROR("Challenge Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800327 return;
328 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700329 // decrypt the parameters
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700330 Buffer paramJsonPayload;
331 try {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700332 paramJsonPayload = parseEncBlock(m_aesKey, sizeof(m_aesKey),
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700333 request.getApplicationParameters());
334 }
335 catch (const std::exception& e) {
336 _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
337 return;
338 }
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700339 if (paramJsonPayload.size() == 0) {
340 _LOG_ERROR("Got an empty buffer from content decryption.");
341 return;
342 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700343 std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
344 std::istringstream ss(paramJsonStr);
345 JsonSection paramJson;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700346 try {
347 boost::property_tree::json_parser::read_json(ss, paramJson);
348 }
349 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700350 _LOG_ERROR("Cannot read JSON from decrypted content: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700351 return;
352 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800353
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700354 // load the corresponding challenge module
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800355 std::string challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800356 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700357 JsonSection contentJson;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800358 if (challenge == nullptr) {
359 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700360 certRequest.m_status = STATUS_FAILURE;
361 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
362 contentJson = genChallengeResponseJson(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800363 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700364 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700365 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
366 // let challenge module handle the request
367 challenge->handleChallengeRequest(paramJson, certRequest);
368 if (certRequest.m_status == STATUS_FAILURE) {
369 // if challenge failed
370 m_storage->deleteRequest(certRequest.m_requestId);
371 contentJson = genChallengeResponseJson(certRequest);
372 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700373 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700374 else if (certRequest.m_status == STATUS_PENDING) {
375 // if challenge succeeded
376 auto issuedCert = issueCertificate(certRequest);
377 certRequest.m_cert = issuedCert;
378 certRequest.m_status = STATUS_SUCCESS;
379 try {
380 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
381 m_storage->deleteRequest(certRequest.m_requestId);
382 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
383 }
384 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700385 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700386 return;
387 }
388 if (m_config.m_statusUpdateCallback) {
389 m_config.m_statusUpdateCallback(certRequest);
390 }
391 contentJson = genChallengeResponseJson(certRequest);
392 contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
swa770cf1d8f72020-04-21 23:12:39 -0700393 contentJson.add(JSON_CHALLENGE_ISSUED_CERT_NAME, issuedCert.getName().toUri());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700394 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800395 }
396 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700397 try {
398 m_storage->updateRequest(certRequest);
399 }
400 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700401 _LOG_TRACE("Cannot update request instance: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700402 return;
403 }
404 contentJson = genChallengeResponseJson(certRequest);
405 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800406 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800407 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700408
409 Data result;
410 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700411 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700412
413 // encrypt the content
414 std::stringstream ss2;
415 boost::property_tree::write_json(ss2, contentJson);
416 auto payload = ss2.str();
Zhiyi Zhang36706832019-07-04 21:33:03 -0700417 auto contentBlock = genEncBlock(tlv::Content, m_aesKey, sizeof(m_aesKey),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700418 (const uint8_t*)payload.c_str(), payload.size());
419 result.setContent(contentBlock);
420 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
421 m_face.put(result);
422
423 if (m_config.m_statusUpdateCallback) {
424 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800425 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700426}
427
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800428security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700429CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800430{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700431 auto expectedPeriod =
432 certRequest.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700433 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800434 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700435
436 Name certName = certRequest.m_cert.getKeyName();
437 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800438 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700439 newCert.setContent(certRequest.m_cert.getContent());
440 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800441 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800442 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700443 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700444 m_config.m_caName, signatureInfo);
445 newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800446
447 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700448 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800449 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800450}
451
452CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700453CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800454{
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400455 std::string requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800456 CertificateRequest certRequest;
457 try {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700458 requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700459 }
460 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700461 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700462 }
463 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700464 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800465 certRequest = m_storage->getRequest(requestId);
466 }
467 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700468 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800469 }
470 return certRequest;
471}
472
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700473/**
474 * @brief Generate JSON file to response PROBE insterest
475 *
476 * PROBE response JSON format:
477 * {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700478 * "name": "@p identifier"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700479 * }
480 */
swa77020643ac2020-03-26 02:24:45 -0700481JsonSection
Yufeng Zhang424d0362019-06-12 16:48:27 -0700482CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700483{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700484 std::vector<std::string> fields;
485 std::string delimiter = ":";
486 size_t last = 0;
487 size_t next = 0;
488 while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
489 fields.push_back(m_probe.substr(last, next - last));
490 last = next + 1;
491 }
492 fields.push_back(m_probe.substr(last));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700493 JsonSection root;
Yufeng Zhang424d0362019-06-12 16:48:27 -0700494
495 for (size_t i = 0; i < fields.size(); ++i) {
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700496 root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
Yufeng Zhang424d0362019-06-12 16:48:27 -0700497 }
498
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700499 root.put(JSON_CA_NAME, identifier.toUri());
500 return root;
501}
502
503/**
504 * @brief Generate JSON file to response NEW interest
505 *
506 * Target JSON format:
507 * {
508 * "ecdh-pub": "@p echdPub",
509 * "salt": "@p salt"
510 * "request-id": "@p requestId",
511 * "status": "@p status",
512 * "challenges": [
513 * {
514 * "challenge-id": ""
515 * },
516 * {
517 * "challenge-id": ""
518 * },
519 * ...
520 * ]
521 * }
522 */
swa77020643ac2020-03-26 02:24:45 -0700523JsonSection
524CaModule::genInfoResponseJson()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700525{
526 JsonSection root;
527 // ca-prefix
528 Name caName = m_config.m_caName;
529 root.put("ca-prefix", caName.toUri());
530
531 // ca-info
532 const auto& pib = m_keyChain.getPib();
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700533 const auto& identity = pib.getIdentity(m_config.m_caName);
534 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400535 std::string caInfo;
536 if (m_config.m_caInfo.empty()) {
537 caInfo = "Issued by " + cert.getSignatureInfo().getKeyLocator().getName().toUri();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700538 }
539 else {
540 caInfo = m_config.m_caInfo;
541 }
542 root.put("ca-info", caInfo);
543
544 // probe
545 root.put("probe", m_config.m_probe);
546
547 // certificate
548 std::stringstream ss;
549 io::save(cert, ss);
550 root.put("certificate", ss.str());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700551 return root;
552}
553
swa77020643ac2020-03-26 02:24:45 -0700554JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700555CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
556 const CertificateRequest& request,
557 const std::list<std::string>& challenges)
558{
559 JsonSection root;
560 JsonSection challengesSection;
561 root.put(JSON_CA_ECDH, ecdhKey);
562 root.put(JSON_CA_SALT, salt);
Zhiyi Zhangff4bcb62019-09-08 12:57:42 -0700563 root.put(JSON_CA_REQUEST_ID, request.m_requestId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700564 root.put(JSON_CA_STATUS, std::to_string(request.m_status));
565
566 for (const auto& entry : challenges) {
567 JsonSection challenge;
568 challenge.put(JSON_CA_CHALLENGE_ID, entry);
569 challengesSection.push_back(std::make_pair("", challenge));
570 }
571 root.add_child(JSON_CA_CHALLENGES, challengesSection);
572 return root;
573}
574
swa77020643ac2020-03-26 02:24:45 -0700575JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700576CaModule::genChallengeResponseJson(const CertificateRequest& request)
577{
578 JsonSection root;
579 JsonSection challengesSection;
580 root.put(JSON_CA_STATUS, request.m_status);
581 root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
582 root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
583 root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
584 return root;
585}
586
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800587void
588CaModule::onRegisterFailed(const std::string& reason)
589{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700590 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800591}
592
593Block
594CaModule::dataContentFromJson(const JsonSection& jsonSection)
595{
596 std::stringstream ss;
597 boost::property_tree::write_json(ss, jsonSection);
598 return makeStringBlock(ndn::tlv::Content, ss.str());
599}
600
601JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700602CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800603{
604 std::string jsonString;
605 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700606 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700607 std::istringstream ss(jsonString);
608 JsonSection json;
609 boost::property_tree::json_parser::read_json(ss, json);
610 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800611 }
612 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700613 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800614 return JsonSection();
615 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800616}
617
618} // namespace ndncert
619} // namespace ndn