blob: be44e11bcc5fca4102e4199a392394f04dc8ed6c [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"
Suyong Won57462ca2020-05-05 22:20:09 -070025#include "tlv.hpp"
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080026#include <ndn-cxx/util/io.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080027#include <ndn-cxx/security/verification-helpers.hpp>
28#include <ndn-cxx/security/signing-helpers.hpp>
Junxiao Shi7c068032017-05-28 13:40:47 +000029#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080030
31namespace ndn {
32namespace ndncert {
33
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070034static const int IS_SUBNAME_MIN_OFFSET = 5;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -070035static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -070036static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070037
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080038_LOG_INIT(ndncert.ca);
39
40CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
41 const std::string& configPath, const std::string& storageType)
42 : m_face(face)
43 , m_keyChain(keyChain)
44{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070045 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080046 m_config.load(configPath);
47 m_storage = CaStorage::createCaStorage(storageType);
48
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080049 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080050}
51
52CaModule::~CaModule()
53{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070054 for (auto handle : m_interestFilterHandles) {
55 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080056 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070057 for (auto handle : m_registeredPrefixHandles) {
58 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080059 }
60}
61
62void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080063CaModule::registerPrefix()
64{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065 // register localhop discovery prefix
swa77020643ac2020-03-26 02:24:45 -070066 Name localhopInfoPrefix("/localhop/CA/INFO");
67 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopInfoPrefix),
68 bind(&CaModule::onInfo, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080069 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070070 m_registeredPrefixHandles.push_back(prefixId);
swa77020643ac2020-03-26 02:24:45 -070071 _LOG_TRACE("Prefix " << localhopInfoPrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080072
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070073 // register prefixes
74 Name prefix = m_config.m_caName;
75 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080076
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070077 prefixId = m_face.registerPrefix(prefix,
78 [&] (const Name& name) {
swa77020643ac2020-03-26 02:24:45 -070079 // register INFO prefix
80 auto filterId = m_face.setInterestFilter(Name(name).append("INFO"),
81 bind(&CaModule::onInfo, this, _2));
82 m_interestFilterHandles.push_back(filterId);
83
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070084 // register PROBE prefix
swa77020643ac2020-03-26 02:24:45 -070085 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
86 bind(&CaModule::onProbe, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070087 m_interestFilterHandles.push_back(filterId);
88
89 // register NEW prefix
swa770de007bc2020-03-24 21:26:21 -070090 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070091 bind(&CaModule::onNew, this, _2));
92 m_interestFilterHandles.push_back(filterId);
93
94 // register SELECT prefix
swa770de007bc2020-03-24 21:26:21 -070095 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070096 bind(&CaModule::onChallenge, this, _2));
97 m_interestFilterHandles.push_back(filterId);
98
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070099 _LOG_TRACE("Prefix " << name << " got registered");
100 },
101 bind(&CaModule::onRegisterFailed, this, _2));
102 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800103}
104
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800105bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700106CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800107{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700108 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800109 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800110}
111
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800112bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700113CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800114{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800116 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800117}
118
119void
swa77020643ac2020-03-26 02:24:45 -0700120CaModule::onInfo(const Interest& request)
121{
122 _LOG_TRACE("Received INFO request");
Suyong Won57462ca2020-05-05 22:20:09 -0700123 Block contentTLV = genInfoResponseTLV();
swa77020643ac2020-03-26 02:24:45 -0700124 Data result;
125
126 result.setName(request.getName());
Suyong Won57462ca2020-05-05 22:20:09 -0700127 result.setContent(contentTLV);
swa77020643ac2020-03-26 02:24:45 -0700128 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
129
130 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
131 m_face.put(result);
132
133 _LOG_TRACE("Handle INFO: send out the INFO response");
134}
135
136void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700137CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800138{
swa77020643ac2020-03-26 02:24:45 -0700139 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
140 _LOG_TRACE("Received PROBE request");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700141 JsonSection contentJson;
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800142
swa77020643ac2020-03-26 02:24:45 -0700143 // process PROBE requests: find an available name
144 std::string availableId;
145 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
146 if (parameterJson.empty()) {
147 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
148 return;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700149 }
swa77020643ac2020-03-26 02:24:45 -0700150 //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
151 if (m_config.m_probeHandler) {
152 try {
153 availableId = m_config.m_probeHandler(parameterJson);
154 }
155 catch (const std::exception& e) {
156 _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700157 return;
158 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800159 }
swa77020643ac2020-03-26 02:24:45 -0700160 else {
161 // if there is no app-specified name lookup, use a random name id
162 availableId = std::to_string(random::generateSecureWord64());
163 }
164 Name newIdentityName = m_config.m_caName;
165 newIdentityName.append(availableId);
166 _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
167 contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800168
169 Data result;
170 result.setName(request.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700171 result.setContent(dataContentFromJson(contentJson));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700172 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700173 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800174 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700175 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800176}
177
178void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700179CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800180{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700181 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700182 // get ECDH pub key and cert request
183 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700184 if (parameterJson.empty()) {
185 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
186 return;
187 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700188 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700189 if (peerKeyBase64 == "") {
190 _LOG_ERROR("Empty JSON_CLIENT_ECDH obtained from the Interest parameter.");
191 return;
192 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700193
194 // get server's ECDH pub key
195 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700196 try {
197 m_ecdh.deriveSecret(peerKeyBase64);
198 }
199 catch (const std::exception& e) {
200 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
201 return;
202 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700203 // generate salt for HKDF
204 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700205 // hkdf
206 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
Zhiyi Zhang36706832019-07-04 21:33:03 -0700207 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700208
209 // parse certificate request
210 std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
211 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800212 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700213 std::stringstream ss(certRequestStr);
214 clientCert = io::load<security::v2::Certificate>(ss);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800215 }
216 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700217 _LOG_ERROR("Unrecognized certificate request: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800218 return;
219 }
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700220 // check the validity period
221 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
222 auto currentTime = time::system_clock::now();
223 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
224 _LOG_ERROR("Client requests a too old notBefore timepoint.");
225 return;
226 }
227 if (expectedPeriod.second > currentTime + m_config.m_validityPeriod ||
228 expectedPeriod.second <= expectedPeriod.first) {
229 _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
230 return;
231 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700232
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700233 // parse probe token if any
234 std::string probeTokenStr = parameterJson.get("probe-token", "");
235 shared_ptr<Data> probeToken = nullptr;
236 if (probeTokenStr != "") {
237 try {
238 std::stringstream ss(probeTokenStr);
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700239 probeToken = io::load<Data>(ss);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700240 }
241 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700242 _LOG_ERROR("Unrecognized probe token: " << e.what());
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700243 return;
244 }
245 }
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700246 if (probeToken == nullptr && m_config.m_probe != "") {
247 // the CA requires PROBE before NEW
248 _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
249 return;
250 }
251 else if (probeToken != nullptr) {
252 // check whether the carried probe token is a PROBE Data packet
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700253 Name prefix = m_config.m_caName;
swa770de007bc2020-03-24 21:26:21 -0700254 prefix.append("CA").append("PROBE");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700255 if (!prefix.isPrefixOf(probeToken->getName())) {
Zhiyi Zhanga1fc6232019-06-13 14:56:59 -0700256 _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700257 return;
258 }
259 }
260
261 // verify the self-signed certificate, the request, and the token
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700262 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
263 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
264 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
265 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
266 return;
267 }
268 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700269 _LOG_ERROR("Cert request with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700270 return;
271 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700272 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700273 _LOG_ERROR("Interest with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700274 return;
275 }
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700276 if (probeToken != nullptr) {
277 const auto& pib = m_keyChain.getPib();
278 const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
279 const auto& caCert = key.getDefaultCertificate();
280 if (!security::verifySignature(*probeToken, caCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700281 _LOG_ERROR("PROBE Token with bad signature.");
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700282 return;
283 }
284 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700285
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700286 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800287 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700288 CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700289 if (probeToken != nullptr) {
290 certRequest.setProbeToken(probeToken);
291 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800292 try {
293 m_storage->addRequest(certRequest);
294 }
295 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700296 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800297 return;
298 }
299
300 Data result;
301 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700302 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700303 result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
304 std::to_string(saltInt),
305 certRequest,
306 m_config.m_supportedChallenges)));
307 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800308 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700309
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700310 if (m_config.m_statusUpdateCallback) {
311 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800312 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800313}
314
315void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700316CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800317{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700318 // get certificate request state
319 CertificateRequest certRequest = getCertificateRequest(request);
320 if (certRequest.m_requestId == "") {
321 // cannot get the request state
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700322 _LOG_ERROR("Cannot find certificate request state from CA's storage.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800323 return;
324 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700325 // verify signature
326 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700327 _LOG_ERROR("Challenge Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800328 return;
329 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700330 // decrypt the parameters
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700331 Buffer paramJsonPayload;
332 try {
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700333 paramJsonPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), m_aesKey,
334 (uint8_t*)"test", strlen("test"));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700335 }
336 catch (const std::exception& e) {
337 _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
338 return;
339 }
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700340 if (paramJsonPayload.size() == 0) {
341 _LOG_ERROR("Got an empty buffer from content decryption.");
342 return;
343 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700344 std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
345 std::istringstream ss(paramJsonStr);
346 JsonSection paramJson;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700347 try {
348 boost::property_tree::json_parser::read_json(ss, paramJson);
349 }
350 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700351 _LOG_ERROR("Cannot read JSON from decrypted content: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700352 return;
353 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800354
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700355 // load the corresponding challenge module
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800356 std::string challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800357 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700358 JsonSection contentJson;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800359 if (challenge == nullptr) {
360 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700361 certRequest.m_status = STATUS_FAILURE;
362 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
363 contentJson = genChallengeResponseJson(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800364 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700365 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700366 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
367 // let challenge module handle the request
368 challenge->handleChallengeRequest(paramJson, certRequest);
369 if (certRequest.m_status == STATUS_FAILURE) {
370 // if challenge failed
371 m_storage->deleteRequest(certRequest.m_requestId);
372 contentJson = genChallengeResponseJson(certRequest);
373 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700374 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700375 else if (certRequest.m_status == STATUS_PENDING) {
376 // if challenge succeeded
377 auto issuedCert = issueCertificate(certRequest);
378 certRequest.m_cert = issuedCert;
379 certRequest.m_status = STATUS_SUCCESS;
380 try {
381 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
382 m_storage->deleteRequest(certRequest.m_requestId);
383 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
384 }
385 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700386 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700387 return;
388 }
389 if (m_config.m_statusUpdateCallback) {
390 m_config.m_statusUpdateCallback(certRequest);
391 }
392 contentJson = genChallengeResponseJson(certRequest);
393 contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
swa770cf1d8f72020-04-21 23:12:39 -0700394 contentJson.add(JSON_CHALLENGE_ISSUED_CERT_NAME, issuedCert.getName().toUri());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700395 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800396 }
397 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700398 try {
399 m_storage->updateRequest(certRequest);
400 }
401 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700402 _LOG_TRACE("Cannot update request instance: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700403 return;
404 }
405 contentJson = genChallengeResponseJson(certRequest);
406 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800407 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800408 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700409
410 Data result;
411 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700412 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700413
414 // encrypt the content
415 std::stringstream ss2;
416 boost::property_tree::write_json(ss2, contentJson);
417 auto payload = ss2.str();
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700418 auto contentBlock = encodeBlockWithAesGcm128(tlv::Content, m_aesKey, (const uint8_t*)payload.c_str(),
419 payload.size(), (uint8_t*)"test", strlen("test"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700420 result.setContent(contentBlock);
421 m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
422 m_face.put(result);
423
424 if (m_config.m_statusUpdateCallback) {
425 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800426 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700427}
428
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800429security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700430CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800431{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700432 auto expectedPeriod =
433 certRequest.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700434 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800435 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700436
437 Name certName = certRequest.m_cert.getKeyName();
438 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800439 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700440 newCert.setContent(certRequest.m_cert.getContent());
441 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800442 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800443 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700444 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700445 m_config.m_caName, signatureInfo);
446 newCert.setFreshnessPeriod(m_config.m_freshnessPeriod);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800447
448 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700449 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800450 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800451}
452
453CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700454CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800455{
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400456 std::string requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800457 CertificateRequest certRequest;
458 try {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700459 requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700460 }
461 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700462 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700463 }
464 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700465 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800466 certRequest = m_storage->getRequest(requestId);
467 }
468 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700469 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800470 }
471 return certRequest;
472}
473
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700474/**
475 * @brief Generate JSON file to response PROBE insterest
476 *
477 * PROBE response JSON format:
478 * {
Yufeng Zhang424d0362019-06-12 16:48:27 -0700479 * "name": "@p identifier"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700480 * }
481 */
swa77020643ac2020-03-26 02:24:45 -0700482JsonSection
Yufeng Zhang424d0362019-06-12 16:48:27 -0700483CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700484{
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700485 std::vector<std::string> fields;
486 std::string delimiter = ":";
487 size_t last = 0;
488 size_t next = 0;
489 while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
490 fields.push_back(m_probe.substr(last, next - last));
491 last = next + 1;
492 }
493 fields.push_back(m_probe.substr(last));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700494 JsonSection root;
Yufeng Zhang424d0362019-06-12 16:48:27 -0700495
496 for (size_t i = 0; i < fields.size(); ++i) {
Zhiyi Zhang781a5602019-06-26 19:05:04 -0700497 root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
Yufeng Zhang424d0362019-06-12 16:48:27 -0700498 }
499
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700500 root.put(JSON_CA_NAME, identifier.toUri());
501 return root;
502}
503
504/**
505 * @brief Generate JSON file to response NEW interest
506 *
507 * Target JSON format:
508 * {
509 * "ecdh-pub": "@p echdPub",
510 * "salt": "@p salt"
511 * "request-id": "@p requestId",
512 * "status": "@p status",
513 * "challenges": [
514 * {
515 * "challenge-id": ""
516 * },
517 * {
518 * "challenge-id": ""
519 * },
520 * ...
521 * ]
522 * }
523 */
swa77020643ac2020-03-26 02:24:45 -0700524JsonSection
525CaModule::genInfoResponseJson()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700526{
527 JsonSection root;
528 // ca-prefix
529 Name caName = m_config.m_caName;
530 root.put("ca-prefix", caName.toUri());
531
532 // ca-info
533 const auto& pib = m_keyChain.getPib();
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700534 const auto& identity = pib.getIdentity(m_config.m_caName);
535 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400536 std::string caInfo;
537 if (m_config.m_caInfo.empty()) {
538 caInfo = "Issued by " + cert.getSignatureInfo().getKeyLocator().getName().toUri();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700539 }
540 else {
541 caInfo = m_config.m_caInfo;
542 }
543 root.put("ca-info", caInfo);
544
545 // probe
546 root.put("probe", m_config.m_probe);
547
548 // certificate
549 std::stringstream ss;
550 io::save(cert, ss);
551 root.put("certificate", ss.str());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700552 return root;
553}
554
Suyong Won57462ca2020-05-05 22:20:09 -0700555Block
556CaModule::genInfoResponseTLV()
557{
558 Block response;
559 // ca-prefix
560 Name caName = m_config.m_caName;
561 // response = makeStringBlock(CAPrefix, caName.toUri());
562 response = makeNestedBlock(CAPrefix, caName);
563
564 // ca-info
565 const auto& pib = m_keyChain.getPib();
566 const auto& identity = pib.getIdentity(m_config.m_caName);
567 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
568 std::string caInfo = "";
569 if (m_config.m_caInfo == "") {
570 caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
571 }
572 else {
573 caInfo = m_config.m_caInfo;
574 }
575
576 response.push_back(makeStringBlock(CAInfo, caInfo));
577
578
579 // parameter-key (Not implemented yet)
580 for() {
581 response.push_back(makeStringBlock(ParameterKey, ""));
582 }
583
584 // TODO: need to convert from days to seconds
585 response.push_back(makeNonNegativeIntegerBlock(MaxValidityPeriod, m_validityPeriod));
586
587 // certificate
588 response.push_back(makeNestedBlock(CACertificate, cert));
589 response.parse();
590
591 return response;
592}
593
594
swa77020643ac2020-03-26 02:24:45 -0700595JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700596CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
597 const CertificateRequest& request,
598 const std::list<std::string>& challenges)
599{
600 JsonSection root;
601 JsonSection challengesSection;
602 root.put(JSON_CA_ECDH, ecdhKey);
603 root.put(JSON_CA_SALT, salt);
Zhiyi Zhangff4bcb62019-09-08 12:57:42 -0700604 root.put(JSON_CA_REQUEST_ID, request.m_requestId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700605 root.put(JSON_CA_STATUS, std::to_string(request.m_status));
606
607 for (const auto& entry : challenges) {
608 JsonSection challenge;
609 challenge.put(JSON_CA_CHALLENGE_ID, entry);
610 challengesSection.push_back(std::make_pair("", challenge));
611 }
612 root.add_child(JSON_CA_CHALLENGES, challengesSection);
613 return root;
614}
615
swa77020643ac2020-03-26 02:24:45 -0700616JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700617CaModule::genChallengeResponseJson(const CertificateRequest& request)
618{
619 JsonSection root;
620 JsonSection challengesSection;
621 root.put(JSON_CA_STATUS, request.m_status);
622 root.put(JSON_CHALLENGE_STATUS, request.m_challengeStatus);
623 root.put(JSON_CHALLENGE_REMAINING_TRIES, std::to_string(request.m_remainingTries));
624 root.put(JSON_CHALLENGE_REMAINING_TIME, std::to_string(request.m_remainingTime));
625 return root;
626}
627
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800628void
629CaModule::onRegisterFailed(const std::string& reason)
630{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700631 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800632}
633
634Block
635CaModule::dataContentFromJson(const JsonSection& jsonSection)
636{
637 std::stringstream ss;
638 boost::property_tree::write_json(ss, jsonSection);
639 return makeStringBlock(ndn::tlv::Content, ss.str());
640}
641
642JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700643CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800644{
645 std::string jsonString;
646 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700647 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700648 std::istringstream ss(jsonString);
649 JsonSection json;
650 boost::property_tree::json_parser::read_json(ss, json);
651 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800652 }
653 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700654 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800655 return JsonSection();
656 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800657}
658
659} // namespace ndncert
660} // namespace ndn