blob: f982dc7e90fe9fde895c2098ce46e9ab8fc4782a [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
98 // register DOWNLOAD prefix
swa770de007bc2020-03-24 21:26:21 -070099 filterId = m_face.setInterestFilter(Name(name).append("DOWNLOAD"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700100 bind(&CaModule::onDownload, this, _2));
101 m_interestFilterHandles.push_back(filterId);
102 _LOG_TRACE("Prefix " << name << " got registered");
103 },
104 bind(&CaModule::onRegisterFailed, this, _2));
105 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800106}
107
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800108bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700109CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800110{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800112 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800113}
114
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800115bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700116CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800117{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700118 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800119 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800120}
121
122void
swa77020643ac2020-03-26 02:24:45 -0700123CaModule::onInfo(const Interest& request)
124{
125 _LOG_TRACE("Received INFO request");
126 JsonSection contentJson = genInfoResponseJson();
127 Data result;
128
129 result.setName(request.getName());
130 result.setContent(dataContentFromJson(contentJson));
131 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
132
133 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
134 m_face.put(result);
135
136 _LOG_TRACE("Handle INFO: send out the INFO response");
137}
138
139void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700140CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800141{
swa77020643ac2020-03-26 02:24:45 -0700142 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
143 _LOG_TRACE("Received PROBE request");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700144 JsonSection contentJson;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800145
swa77020643ac2020-03-26 02:24:45 -0700146 // process PROBE requests: find an available name
147 std::string availableId;
148 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
149 if (parameterJson.empty()) {
150 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
151 return;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700152 }
swa77020643ac2020-03-26 02:24:45 -0700153 //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
154 if (m_config.m_probeHandler) {
155 try {
156 availableId = m_config.m_probeHandler(parameterJson);
157 }
158 catch (const std::exception& e) {
159 _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700160 return;
161 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800162 }
swa77020643ac2020-03-26 02:24:45 -0700163 else {
164 // if there is no app-specified name lookup, use a random name id
165 availableId = std::to_string(random::generateSecureWord64());
166 }
167 Name newIdentityName = m_config.m_caName;
168 newIdentityName.append(availableId);
169 _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
170 contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800171
172 Data result;
173 result.setName(request.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700174 result.setContent(dataContentFromJson(contentJson));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700175 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700176 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800177 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700178 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800179}
180
181void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700182CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800183{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700184 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700185 // get ECDH pub key and cert request
186 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700187 if (parameterJson.empty()) {
188 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
189 return;
190 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700191 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700192 if (peerKeyBase64 == "") {
193 _LOG_ERROR("Empty JSON_CLIENT_ECDH obtained from the Interest parameter.");
194 return;
195 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700196
197 // get server's ECDH pub key
198 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700199 try {
200 m_ecdh.deriveSecret(peerKeyBase64);
201 }
202 catch (const std::exception& e) {
203 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
204 return;
205 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700206 // generate salt for HKDF
207 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700208 // hkdf
209 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
Zhiyi Zhang36706832019-07-04 21:33:03 -0700210 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700211
212 // parse certificate request
213 std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
214 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800215 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700216 std::stringstream ss(certRequestStr);
217 clientCert = io::load<security::v2::Certificate>(ss);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800218 }
219 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700220 _LOG_ERROR("Unrecognized certificate request: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800221 return;
222 }
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700223 // check the validity period
224 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
225 auto currentTime = time::system_clock::now();
226 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
227 _LOG_ERROR("Client requests a too old notBefore timepoint.");
228 return;
229 }
230 if (expectedPeriod.second > currentTime + m_config.m_validityPeriod ||
231 expectedPeriod.second <= expectedPeriod.first) {
232 _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
233 return;
234 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700235
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700236 // parse probe token if any
237 std::string probeTokenStr = parameterJson.get("probe-token", "");
238 shared_ptr<Data> probeToken = nullptr;
239 if (probeTokenStr != "") {
240 try {
241 std::stringstream ss(probeTokenStr);
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700242 probeToken = io::load<Data>(ss);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700243 }
244 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700245 _LOG_ERROR("Unrecognized probe token: " << e.what());
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700246 return;
247 }
248 }
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700249 if (probeToken == nullptr && m_config.m_probe != "") {
250 // the CA requires PROBE before NEW
251 _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
252 return;
253 }
254 else if (probeToken != nullptr) {
255 // check whether the carried probe token is a PROBE Data packet
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700256 Name prefix = m_config.m_caName;
swa770de007bc2020-03-24 21:26:21 -0700257 prefix.append("CA").append("PROBE");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700258 if (!prefix.isPrefixOf(probeToken->getName())) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700259 _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700260 return;
261 }
262 }
263
264 // verify the self-signed certificate, the request, and the token
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700265 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
266 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
267 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
268 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
269 return;
270 }
271 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700272 _LOG_ERROR("Cert request with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700273 return;
274 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700275 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700276 _LOG_ERROR("Interest with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700277 return;
278 }
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700279 if (probeToken != nullptr) {
280 const auto& pib = m_keyChain.getPib();
281 const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
282 const auto& caCert = key.getDefaultCertificate();
283 if (!security::verifySignature(*probeToken, caCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700284 _LOG_ERROR("PROBE Token with bad signature.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700285 return;
286 }
287 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700288
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700289 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800290 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700291 CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700292 if (probeToken != nullptr) {
293 certRequest.setProbeToken(probeToken);
294 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800295 try {
296 m_storage->addRequest(certRequest);
297 }
298 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700299 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800300 return;
301 }
302
303 Data result;
304 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700305 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700306 result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
307 std::to_string(saltInt),
308 certRequest,
309 m_config.m_supportedChallenges)));
310 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800311 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700312
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700313 if (m_config.m_statusUpdateCallback) {
314 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800315 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800316}
317
318void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700319CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800320{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700321 // get certificate request state
322 CertificateRequest certRequest = getCertificateRequest(request);
323 if (certRequest.m_requestId == "") {
324 // cannot get the request state
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700325 _LOG_ERROR("Cannot find certificate request state from CA's storage.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800326 return;
327 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700328 // verify signature
329 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700330 _LOG_ERROR("Challenge Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800331 return;
332 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700333 // decrypt the parameters
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700334 Buffer paramJsonPayload;
335 try {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700336 paramJsonPayload = parseEncBlock(m_aesKey, sizeof(m_aesKey),
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700337 request.getApplicationParameters());
338 }
339 catch (const std::exception& e) {
340 _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
341 return;
342 }
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700343 if (paramJsonPayload.size() == 0) {
344 _LOG_ERROR("Got an empty buffer from content decryption.");
345 return;
346 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700347 std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
348 std::istringstream ss(paramJsonStr);
349 JsonSection paramJson;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700350 try {
351 boost::property_tree::json_parser::read_json(ss, paramJson);
352 }
353 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700354 _LOG_ERROR("Cannot read JSON from decrypted content: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700355 return;
356 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800357
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700358 // load the corresponding challenge module
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800359 std::string challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800360 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700361 JsonSection contentJson;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800362 if (challenge == nullptr) {
363 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700364 certRequest.m_status = STATUS_FAILURE;
365 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
366 contentJson = genChallengeResponseJson(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800367 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700368 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700369 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
370 // let challenge module handle the request
371 challenge->handleChallengeRequest(paramJson, certRequest);
372 if (certRequest.m_status == STATUS_FAILURE) {
373 // if challenge failed
374 m_storage->deleteRequest(certRequest.m_requestId);
375 contentJson = genChallengeResponseJson(certRequest);
376 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700377 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700378 else if (certRequest.m_status == STATUS_PENDING) {
379 // if challenge succeeded
380 auto issuedCert = issueCertificate(certRequest);
381 certRequest.m_cert = issuedCert;
382 certRequest.m_status = STATUS_SUCCESS;
383 try {
384 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
385 m_storage->deleteRequest(certRequest.m_requestId);
386 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
387 }
388 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700389 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700390 return;
391 }
392 if (m_config.m_statusUpdateCallback) {
393 m_config.m_statusUpdateCallback(certRequest);
394 }
395 contentJson = genChallengeResponseJson(certRequest);
396 contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
397 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800398 }
399 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700400 try {
401 m_storage->updateRequest(certRequest);
402 }
403 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700404 _LOG_TRACE("Cannot update request instance: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700405 return;
406 }
407 contentJson = genChallengeResponseJson(certRequest);
408 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800409 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800410 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700411
412 Data result;
413 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700414 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700415
416 // encrypt the content
417 std::stringstream ss2;
418 boost::property_tree::write_json(ss2, contentJson);
419 auto payload = ss2.str();
Zhiyi Zhang36706832019-07-04 21:33:03 -0700420 auto contentBlock = genEncBlock(tlv::Content, m_aesKey, sizeof(m_aesKey),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700421 (const uint8_t*)payload.c_str(), payload.size());
422 result.setContent(contentBlock);
423 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
424 m_face.put(result);
425
426 if (m_config.m_statusUpdateCallback) {
427 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800428 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700429}
430
431void
432CaModule::onDownload(const Interest& request)
433{
434 auto requestId = readString(request.getName().at(-1));
435 security::v2::Certificate signedCert;
436 try {
437 signedCert = m_storage->getCertificate(requestId);
438 }
439 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700440 _LOG_ERROR("Cannot read signed cert " << requestId << " from CA's storage: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700441 return;
442 }
443 Data result;
444 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700445 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700446 result.setContent(signedCert.wireEncode());
447 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800448 m_face.put(result);
449}
450
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800451security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700452CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800453{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700454 auto expectedPeriod =
455 certRequest.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700456 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800457 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700458
459 Name certName = certRequest.m_cert.getKeyName();
460 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800461 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700462 newCert.setContent(certRequest.m_cert.getContent());
463 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800464 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800465 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700466 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700467 m_config.m_caName, signatureInfo);
468 newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800469
470 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700471 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800472 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800473}
474
475CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700476CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800477{
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400478 std::string requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800479 CertificateRequest certRequest;
480 try {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700481 requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700482 }
483 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700484 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700485 }
486 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700487 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800488 certRequest = m_storage->getRequest(requestId);
489 }
490 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700491 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800492 }
493 return certRequest;
494}
495
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700496/**
497 * @brief Generate JSON file to response PROBE insterest
498 *
499 * PROBE response JSON format:
500 * {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700501 * "name": "@p identifier"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700502 * }
503 */
swa77020643ac2020-03-26 02:24:45 -0700504JsonSection
Yufeng Zhang424d0362019-06-12 16:48:27 -0700505CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700506{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700507 std::vector<std::string> fields;
508 std::string delimiter = ":";
509 size_t last = 0;
510 size_t next = 0;
511 while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
512 fields.push_back(m_probe.substr(last, next - last));
513 last = next + 1;
514 }
515 fields.push_back(m_probe.substr(last));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700516 JsonSection root;
Yufeng Zhang424d0362019-06-12 16:48:27 -0700517
518 for (size_t i = 0; i < fields.size(); ++i) {
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700519 root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
Yufeng Zhang424d0362019-06-12 16:48:27 -0700520 }
521
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700522 root.put(JSON_CA_NAME, identifier.toUri());
523 return root;
524}
525
526/**
527 * @brief Generate JSON file to response NEW interest
528 *
529 * Target JSON format:
530 * {
531 * "ecdh-pub": "@p echdPub",
532 * "salt": "@p salt"
533 * "request-id": "@p requestId",
534 * "status": "@p status",
535 * "challenges": [
536 * {
537 * "challenge-id": ""
538 * },
539 * {
540 * "challenge-id": ""
541 * },
542 * ...
543 * ]
544 * }
545 */
swa77020643ac2020-03-26 02:24:45 -0700546JsonSection
547CaModule::genInfoResponseJson()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700548{
549 JsonSection root;
550 // ca-prefix
551 Name caName = m_config.m_caName;
552 root.put("ca-prefix", caName.toUri());
553
554 // ca-info
555 const auto& pib = m_keyChain.getPib();
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700556 const auto& identity = pib.getIdentity(m_config.m_caName);
557 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400558 std::string caInfo;
559 if (m_config.m_caInfo.empty()) {
560 caInfo = "Issued by " + cert.getSignatureInfo().getKeyLocator().getName().toUri();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700561 }
562 else {
563 caInfo = m_config.m_caInfo;
564 }
565 root.put("ca-info", caInfo);
566
567 // probe
568 root.put("probe", m_config.m_probe);
569
570 // certificate
571 std::stringstream ss;
572 io::save(cert, ss);
573 root.put("certificate", ss.str());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700574 return root;
575}
576
swa77020643ac2020-03-26 02:24:45 -0700577JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700578CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
579 const CertificateRequest& request,
580 const std::list<std::string>& challenges)
581{
582 JsonSection root;
583 JsonSection challengesSection;
584 root.put(JSON_CA_ECDH, ecdhKey);
585 root.put(JSON_CA_SALT, salt);
Zhiyi Zhangff4bcb62019-09-08 12:57:42 -0700586 root.put(JSON_CA_REQUEST_ID, request.m_requestId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700587 root.put(JSON_CA_STATUS, std::to_string(request.m_status));
588
589 for (const auto& entry : challenges) {
590 JsonSection challenge;
591 challenge.put(JSON_CA_CHALLENGE_ID, entry);
592 challengesSection.push_back(std::make_pair("", challenge));
593 }
594 root.add_child(JSON_CA_CHALLENGES, challengesSection);
595 return root;
596}
597
swa77020643ac2020-03-26 02:24:45 -0700598JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700599CaModule::genChallengeResponseJson(const CertificateRequest& request)
600{
601 JsonSection root;
602 JsonSection challengesSection;
603 root.put(JSON_CA_STATUS, request.m_status);
604 root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
605 root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
606 root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
607 return root;
608}
609
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800610void
611CaModule::onRegisterFailed(const std::string& reason)
612{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700613 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800614}
615
616Block
617CaModule::dataContentFromJson(const JsonSection& jsonSection)
618{
619 std::stringstream ss;
620 boost::property_tree::write_json(ss, jsonSection);
621 return makeStringBlock(ndn::tlv::Content, ss.str());
622}
623
624JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700625CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800626{
627 std::string jsonString;
628 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700629 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700630 std::istringstream ss(jsonString);
631 JsonSection json;
632 boost::property_tree::json_parser::read_json(ss, json);
633 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800634 }
635 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700636 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800637 return JsonSection();
638 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800639}
640
641} // namespace ndncert
642} // namespace ndn