blob: 7dbb053739259935cdb7bdd58e176c52e535fac3 [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 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
65 Name localhopProbePrefix("/localhop/CA/PROBE/INFO");
66 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopProbePrefix),
67 bind(&CaModule::onProbe, 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);
70 _LOG_TRACE("Prefix " << localhopProbePrefix << " 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) {
78 // register PROBE prefix
79 auto filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
80 bind(&CaModule::onProbe, this, _2));
81 m_interestFilterHandles.push_back(filterId);
82
83 // register NEW prefix
84 filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
85 bind(&CaModule::onNew, this, _2));
86 m_interestFilterHandles.push_back(filterId);
87
88 // register SELECT prefix
89 filterId = m_face.setInterestFilter(Name(name).append("_CHALLENGE"),
90 bind(&CaModule::onChallenge, this, _2));
91 m_interestFilterHandles.push_back(filterId);
92
93 // register DOWNLOAD prefix
94 filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
95 bind(&CaModule::onDownload, this, _2));
96 m_interestFilterHandles.push_back(filterId);
97 _LOG_TRACE("Prefix " << name << " got registered");
98 },
99 bind(&CaModule::onRegisterFailed, this, _2));
100 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800101}
102
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800103bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700104CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800105{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700106 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800107 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800108}
109
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800110bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800112{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700113 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800114 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800115}
116
117void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700118CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800119{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700120 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent|INFO]
121 _LOG_TRACE("Receive PROBE request");
122 JsonSection contentJson;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800123
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 // process PROBE INFO requests
125 if (readString(request.getName().at(-1)) == "INFO") {
126 contentJson = genProbeResponseJson();
127 }
128 else {
129 // if not a PROBE INFO, find an available name
130 std::string availableId = "";
131 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700132 if (parameterJson.empty()) {
133 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
134 return;
135 }
Yufeng Zhang424d0362019-06-12 16:48:27 -0700136 //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700137 if (m_config.m_probeHandler) {
138 try {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700139 availableId = m_config.m_probeHandler(parameterJson);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700140 }
141 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700142 _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700143 return;
144 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800145 }
146 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700147 // if there is no app-specified name lookup, use a random name id
148 availableId = std::to_string(random::generateSecureWord64());
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800149 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700150 Name newIdentityName = m_config.m_caName;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700151 newIdentityName.append(availableId);
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700152 _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
Yufeng Zhang424d0362019-06-12 16:48:27 -0700153 contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800154 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800155
156 Data result;
157 result.setName(request.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700158 result.setContent(dataContentFromJson(contentJson));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700159 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700160 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800161 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700162 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800163}
164
165void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700166CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800167{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700168 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700169 // get ECDH pub key and cert request
170 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700171 if (parameterJson.empty()) {
172 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
173 return;
174 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700175 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700176 if (peerKeyBase64 == "") {
177 _LOG_ERROR("Empty JSON_CLIENT_ECDH obtained from the Interest parameter.");
178 return;
179 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700180
181 // get server's ECDH pub key
182 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700183 try {
184 m_ecdh.deriveSecret(peerKeyBase64);
185 }
186 catch (const std::exception& e) {
187 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
188 return;
189 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190 // generate salt for HKDF
191 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700192 // hkdf
193 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
Zhiyi Zhang36706832019-07-04 21:33:03 -0700194 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700195
196 // parse certificate request
197 std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
198 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800199 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700200 std::stringstream ss(certRequestStr);
201 clientCert = io::load<security::v2::Certificate>(ss);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800202 }
203 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700204 _LOG_ERROR("Unrecognized certificate request: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800205 return;
206 }
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700207 // check the validity period
208 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
209 auto currentTime = time::system_clock::now();
210 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
211 _LOG_ERROR("Client requests a too old notBefore timepoint.");
212 return;
213 }
214 if (expectedPeriod.second > currentTime + m_config.m_validityPeriod ||
215 expectedPeriod.second <= expectedPeriod.first) {
216 _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
217 return;
218 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700219
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700220 // parse probe token if any
221 std::string probeTokenStr = parameterJson.get("probe-token", "");
222 shared_ptr<Data> probeToken = nullptr;
223 if (probeTokenStr != "") {
224 try {
225 std::stringstream ss(probeTokenStr);
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700226 probeToken = io::load<Data>(ss);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700227 }
228 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700229 _LOG_ERROR("Unrecognized probe token: " << e.what());
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700230 return;
231 }
232 }
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700233 if (probeToken == nullptr && m_config.m_probe != "") {
234 // the CA requires PROBE before NEW
235 _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
236 return;
237 }
238 else if (probeToken != nullptr) {
239 // check whether the carried probe token is a PROBE Data packet
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700240 Name prefix = m_config.m_caName;
241 prefix.append("CA").append("_PROBE");
242 if (!prefix.isPrefixOf(probeToken->getName())) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700243 _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700244 return;
245 }
246 }
247
248 // verify the self-signed certificate, the request, and the token
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700249 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
250 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
251 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
252 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
253 return;
254 }
255 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700256 _LOG_ERROR("Cert request with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700257 return;
258 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700259 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700260 _LOG_ERROR("Interest with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700261 return;
262 }
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700263 if (probeToken != nullptr) {
264 const auto& pib = m_keyChain.getPib();
265 const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
266 const auto& caCert = key.getDefaultCertificate();
267 if (!security::verifySignature(*probeToken, caCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700268 _LOG_ERROR("PROBE Token with bad signature.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700269 return;
270 }
271 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700272
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700273 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800274 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700275 CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700276 if (probeToken != nullptr) {
277 certRequest.setProbeToken(probeToken);
278 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800279 try {
280 m_storage->addRequest(certRequest);
281 }
282 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700283 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800284 return;
285 }
286
287 Data result;
288 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700289 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700290 result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
291 std::to_string(saltInt),
292 certRequest,
293 m_config.m_supportedChallenges)));
294 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800295 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700296
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700297 if (m_config.m_statusUpdateCallback) {
298 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800299 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800300}
301
302void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700303CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800304{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700305 // get certificate request state
306 CertificateRequest certRequest = getCertificateRequest(request);
307 if (certRequest.m_requestId == "") {
308 // cannot get the request state
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700309 _LOG_ERROR("Cannot find certificate request state from CA's storage.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800310 return;
311 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700312 // verify signature
313 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700314 _LOG_ERROR("Challenge Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800315 return;
316 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700317 // decrypt the parameters
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700318 Buffer paramJsonPayload;
319 try {
Zhiyi Zhang36706832019-07-04 21:33:03 -0700320 paramJsonPayload = parseEncBlock(m_aesKey, sizeof(m_aesKey),
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700321 request.getApplicationParameters());
322 }
323 catch (const std::exception& e) {
324 _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
325 return;
326 }
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700327 if (paramJsonPayload.size() == 0) {
328 _LOG_ERROR("Got an empty buffer from content decryption.");
329 return;
330 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700331 std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
332 std::istringstream ss(paramJsonStr);
333 JsonSection paramJson;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700334 try {
335 boost::property_tree::json_parser::read_json(ss, paramJson);
336 }
337 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700338 _LOG_ERROR("Cannot read JSON from decrypted content: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700339 return;
340 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800341
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700342 // load the corresponding challenge module
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800343 std::string challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800344 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700345 JsonSection contentJson;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800346 if (challenge == nullptr) {
347 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700348 certRequest.m_status = STATUS_FAILURE;
349 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
350 contentJson = genChallengeResponseJson(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800351 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700352 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700353 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
354 // let challenge module handle the request
355 challenge->handleChallengeRequest(paramJson, certRequest);
356 if (certRequest.m_status == STATUS_FAILURE) {
357 // if challenge failed
358 m_storage->deleteRequest(certRequest.m_requestId);
359 contentJson = genChallengeResponseJson(certRequest);
360 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700361 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700362 else if (certRequest.m_status == STATUS_PENDING) {
363 // if challenge succeeded
364 auto issuedCert = issueCertificate(certRequest);
365 certRequest.m_cert = issuedCert;
366 certRequest.m_status = STATUS_SUCCESS;
367 try {
368 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
369 m_storage->deleteRequest(certRequest.m_requestId);
370 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
371 }
372 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700373 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700374 return;
375 }
376 if (m_config.m_statusUpdateCallback) {
377 m_config.m_statusUpdateCallback(certRequest);
378 }
379 contentJson = genChallengeResponseJson(certRequest);
380 contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
381 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800382 }
383 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700384 try {
385 m_storage->updateRequest(certRequest);
386 }
387 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700388 _LOG_TRACE("Cannot update request instance: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700389 return;
390 }
391 contentJson = genChallengeResponseJson(certRequest);
392 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800393 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800394 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700395
396 Data result;
397 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700398 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700399
400 // encrypt the content
401 std::stringstream ss2;
402 boost::property_tree::write_json(ss2, contentJson);
403 auto payload = ss2.str();
Zhiyi Zhang36706832019-07-04 21:33:03 -0700404 auto contentBlock = genEncBlock(tlv::Content, m_aesKey, sizeof(m_aesKey),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700405 (const uint8_t*)payload.c_str(), payload.size());
406 result.setContent(contentBlock);
407 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
408 m_face.put(result);
409
410 if (m_config.m_statusUpdateCallback) {
411 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800412 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700413}
414
415void
416CaModule::onDownload(const Interest& request)
417{
418 auto requestId = readString(request.getName().at(-1));
419 security::v2::Certificate signedCert;
420 try {
421 signedCert = m_storage->getCertificate(requestId);
422 }
423 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700424 _LOG_ERROR("Cannot read signed cert " << requestId << " from CA's storage: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700425 return;
426 }
427 Data result;
428 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700429 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700430 result.setContent(signedCert.wireEncode());
431 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800432 m_face.put(result);
433}
434
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800435security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700436CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800437{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700438 auto expectedPeriod =
439 certRequest.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700440 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800441 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700442
443 Name certName = certRequest.m_cert.getKeyName();
444 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800445 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700446 newCert.setContent(certRequest.m_cert.getContent());
447 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800448 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800449 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700450 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700451 m_config.m_caName, signatureInfo);
452 newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800453
454 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700455 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800456 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800457}
458
459CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700460CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800461{
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700462 std::string requestId = "";
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800463 CertificateRequest certRequest;
464 try {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700465 requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700466 }
467 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700468 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700469 }
470 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700471 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800472 certRequest = m_storage->getRequest(requestId);
473 }
474 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700475 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800476 }
477 return certRequest;
478}
479
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700480/**
481 * @brief Generate JSON file to response PROBE insterest
482 *
483 * PROBE response JSON format:
484 * {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700485 * "name": "@p identifier"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700486 * }
487 */
488const JsonSection
Yufeng Zhang424d0362019-06-12 16:48:27 -0700489CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700490{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700491 std::vector<std::string> fields;
492 std::string delimiter = ":";
493 size_t last = 0;
494 size_t next = 0;
495 while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
496 fields.push_back(m_probe.substr(last, next - last));
497 last = next + 1;
498 }
499 fields.push_back(m_probe.substr(last));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700500 JsonSection root;
Yufeng Zhang424d0362019-06-12 16:48:27 -0700501
502 for (size_t i = 0; i < fields.size(); ++i) {
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700503 root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
Yufeng Zhang424d0362019-06-12 16:48:27 -0700504 }
505
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700506 root.put(JSON_CA_NAME, identifier.toUri());
507 return root;
508}
509
510/**
511 * @brief Generate JSON file to response NEW interest
512 *
513 * Target JSON format:
514 * {
515 * "ecdh-pub": "@p echdPub",
516 * "salt": "@p salt"
517 * "request-id": "@p requestId",
518 * "status": "@p status",
519 * "challenges": [
520 * {
521 * "challenge-id": ""
522 * },
523 * {
524 * "challenge-id": ""
525 * },
526 * ...
527 * ]
528 * }
529 */
530const JsonSection
531CaModule::genProbeResponseJson()
532{
533 JsonSection root;
534 // ca-prefix
535 Name caName = m_config.m_caName;
536 root.put("ca-prefix", caName.toUri());
537
538 // ca-info
539 const auto& pib = m_keyChain.getPib();
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700540 const auto& identity = pib.getIdentity(m_config.m_caName);
541 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700542 std::string caInfo = "";
543 if (m_config.m_caInfo == "") {
544 caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
545 }
546 else {
547 caInfo = m_config.m_caInfo;
548 }
549 root.put("ca-info", caInfo);
550
551 // probe
552 root.put("probe", m_config.m_probe);
553
554 // certificate
555 std::stringstream ss;
556 io::save(cert, ss);
557 root.put("certificate", ss.str());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700558 return root;
559}
560
561const JsonSection
562CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
563 const CertificateRequest& request,
564 const std::list<std::string>& challenges)
565{
566 JsonSection root;
567 JsonSection challengesSection;
568 root.put(JSON_CA_ECDH, ecdhKey);
569 root.put(JSON_CA_SALT, salt);
Zhiyi Zhangff4bcb62019-09-08 12:57:42 -0700570 root.put(JSON_CA_REQUEST_ID, request.m_requestId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700571 root.put(JSON_CA_STATUS, std::to_string(request.m_status));
572
573 for (const auto& entry : challenges) {
574 JsonSection challenge;
575 challenge.put(JSON_CA_CHALLENGE_ID, entry);
576 challengesSection.push_back(std::make_pair("", challenge));
577 }
578 root.add_child(JSON_CA_CHALLENGES, challengesSection);
579 return root;
580}
581
582const JsonSection
583CaModule::genChallengeResponseJson(const CertificateRequest& request)
584{
585 JsonSection root;
586 JsonSection challengesSection;
587 root.put(JSON_CA_STATUS, request.m_status);
588 root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
589 root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
590 root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
591 return root;
592}
593
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800594void
595CaModule::onRegisterFailed(const std::string& reason)
596{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700597 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800598}
599
600Block
601CaModule::dataContentFromJson(const JsonSection& jsonSection)
602{
603 std::stringstream ss;
604 boost::property_tree::write_json(ss, jsonSection);
605 return makeStringBlock(ndn::tlv::Content, ss.str());
606}
607
608JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700609CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800610{
611 std::string jsonString;
612 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700613 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700614 std::istringstream ss(jsonString);
615 JsonSection json;
616 boost::property_tree::json_parser::read_json(ss, json);
617 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800618 }
619 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700620 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800621 return JsonSection();
622 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800623}
624
625} // namespace ndncert
626} // namespace ndn