blob: 81b7138f9968251e3f10c0b5f92f79d2b3c45e23 [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"
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070022
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -070023#include <ndn-cxx/metadata-object.hpp>
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070024#include <ndn-cxx/security/signing-helpers.hpp>
25#include <ndn-cxx/security/verification-helpers.hpp>
26#include <ndn-cxx/util/io.hpp>
27#include <ndn-cxx/util/random.hpp>
28
29#include "challenge-module.hpp"
30#include "crypto-support/enc-tlv.hpp"
31#include "logging.hpp"
32#include "protocol-detail/challenge.hpp"
33#include "protocol-detail/error.hpp"
34#include "protocol-detail/info.hpp"
35#include "protocol-detail/new.hpp"
36#include "protocol-detail/probe.hpp"
37#include "protocol-detail/revoke.hpp"
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080038
39namespace ndn {
40namespace ndncert {
41
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -070042static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -070043static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070044
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080045_LOG_INIT(ndncert.ca);
46
47CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
48 const std::string& configPath, const std::string& storageType)
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070049 : m_face(face)
50 , m_keyChain(keyChain)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080051{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070052 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080053 m_config.load(configPath);
54 m_storage = CaStorage::createCaStorage(storageType);
55
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080056 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080057}
58
59CaModule::~CaModule()
60{
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070061 for (auto& handle : m_interestFilterHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080063 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070064 for (auto& handle : m_registeredPrefixHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080066 }
67}
68
69void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080070CaModule::registerPrefix()
71{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070072 // register localhop discovery prefix
swa77020643ac2020-03-26 02:24:45 -070073 Name localhopInfoPrefix("/localhop/CA/INFO");
74 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopInfoPrefix),
75 bind(&CaModule::onInfo, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080076 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070077 m_registeredPrefixHandles.push_back(prefixId);
swa77020643ac2020-03-26 02:24:45 -070078 _LOG_TRACE("Prefix " << localhopInfoPrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080079
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070080 // register prefixes
Suyong Won256c9062020-05-11 02:45:56 -070081 Name prefix = m_config.m_caPrefix;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070082 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080083
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070084 prefixId = m_face.registerPrefix(
85 prefix,
86 [&](const Name& name) {
87 // register INFO prefix
88 auto filterId = m_face.setInterestFilter(Name(name).append("INFO"),
89 bind(&CaModule::onInfo, this, _2));
90 m_interestFilterHandles.push_back(filterId);
swa77020643ac2020-03-26 02:24:45 -070091
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070092 // register PROBE prefix
93 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
94 bind(&CaModule::onProbe, this, _2));
95 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070096
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070097 // register NEW prefix
98 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070099 bind(&CaModule::onNewRenewRevoke, this, _1, _2, RequestType::NEW));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700100 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700101
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700102 // register SELECT prefix
103 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
104 bind(&CaModule::onChallenge, this, _2));
105 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700106
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700107 // register REVOKE prefix
108 filterId = m_face.setInterestFilter(Name(name).append("REVOKE"),
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700109 bind(&CaModule::onNewRenewRevoke, this, _1, _2, RequestType::REVOKE));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700110 m_interestFilterHandles.push_back(filterId);
111 _LOG_TRACE("Prefix " << name << " got registered");
112 },
113 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700114 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800115}
116
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700117void
118CaModule::setNameAssignmentFunction(const NameAssignmentFunc& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800119{
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700120 m_config.m_nameAssignmentFunc = handler;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800121}
122
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800123bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800125{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700126 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800127 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800128}
129
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700130shared_ptr<Data>
131CaModule::generateCaConfigMetaData()
swa77020643ac2020-03-26 02:24:45 -0700132{
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700133 // @TODO
134 // make metadata a class member variable m_infoMetadata
135 // check whether the m_infoMetadata has the latest versioned name, if not, then generate a new one
136 // otherwise, directly reply m_infoMetadata.makeData
137
138 auto infoPacket = generateCaConfigData();
139 MetadataObject metadata;
140 metadata.setVersionedName(infoPacket->getName().getPrefix(-1));
141 Name discoveryInterestName(infoPacket->getName().getPrefix(-2));
142 name::Component metadataComponent(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata"));
143 discoveryInterestName.append(metadataComponent);
144 auto metadataData = metadata.makeData(discoveryInterestName, m_keyChain, signingByIdentity(m_config.m_caPrefix));
145 return make_shared<Data>(metadataData);
146}
147
148shared_ptr<Data>
149CaModule::generateCaConfigData()
150{
151 // @TODO
152 // make CaInfo Data packet a class member variable m_infoData
153 // check whether the m_infoData is still valid, if not, then generate a new one
154 // otherwise, directly reply m_infoData
Suyong Won19fba4d2020-05-09 13:39:46 -0700155
156 const auto& pib = m_keyChain.getPib();
Suyong Won256c9062020-05-11 02:45:56 -0700157 const auto& identity = pib.getIdentity(m_config.m_caPrefix);
Suyong Won19fba4d2020-05-09 13:39:46 -0700158 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Zhiyi Zhang61c43a62020-09-28 12:56:01 -0700159 Block contentTLV = INFO::encodeDataContent(m_config, cert);
swa77020643ac2020-03-26 02:24:45 -0700160
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700161 Name infoPacketName(m_config.m_caPrefix);
162 infoPacketName.append("CA").append("INFO").appendVersion().appendSegment(0);
163 Data infoData(infoPacketName);
164 infoData.setContent(contentTLV);
165 infoData.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
166 m_keyChain.sign(infoData, signingByIdentity(m_config.m_caPrefix));
167 return make_shared<Data>(infoData);
168}
swa77020643ac2020-03-26 02:24:45 -0700169
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700170void
171CaModule::onInfo(const Interest& request)
172{
173 _LOG_TRACE("Received INFO request");
174
175 if (request.getName().get(-1).type() == 32) {
176 m_face.put(*generateCaConfigMetaData());
177 }
178 else {
179 m_face.put(*generateCaConfigData());
180 }
swa77020643ac2020-03-26 02:24:45 -0700181
182 _LOG_TRACE("Handle INFO: send out the INFO response");
183}
184
185void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700186CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800187{
swa77020643ac2020-03-26 02:24:45 -0700188 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
189 _LOG_TRACE("Received PROBE request");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800190
swa77020643ac2020-03-26 02:24:45 -0700191 // process PROBE requests: find an available name
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700192 std::string availableId = "";
Suyong Won19fba4d2020-05-09 13:39:46 -0700193 const auto& parameterTLV = request.getApplicationParameters();
Suyong Won44d0cce2020-05-10 04:07:43 -0700194 parameterTLV.parse();
Suyong Won19fba4d2020-05-09 13:39:46 -0700195 if (!parameterTLV.hasValue()) {
196 _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
swa77020643ac2020-03-26 02:24:45 -0700197 return;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700198 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700199
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700200 // if (m_config.m_nameAssignmentFunc) {
201 // try {
202 // availableId = m_config.m_nameAssignmentFunc(parameterTLV);
203 // }
204 // catch (const std::exception& e) {
205 // _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
206 // return;
207 // }
208 // }
209 // else {
210 // // if there is no app-specified name lookup, use a random name id
211 // availableId = std::to_string(random::generateSecureWord64());
212 // }
213 // Name newIdentityName = m_config.m_caPrefix;
214 // newIdentityName.append(availableId);
215 // _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800216
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700217 // Block contentTLV = PROBE::encodeDataContent(newIdentityName.toUri(), m_config.m_probe, parameterTLV);
218
219 // Data result;
220 // result.setName(request.getName());
221 // result.setContent(contentTLV);
222 // result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
223 // m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
224 // m_face.put(result);
225 // _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800226}
227
228void
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700229CaModule::onNewRenewRevoke(const InterestFilter& filter, const Interest& request, RequestType requestType)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800230{
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700231 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700232 // REVOKE Naming Convention: /<CA-prefix>/CA/REVOKE/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700233 // get ECDH pub key and cert request
Suyong Won19fba4d2020-05-09 13:39:46 -0700234 const auto& parameterTLV = request.getApplicationParameters();
Suyong Won7968f7a2020-05-12 01:01:25 -0700235 parameterTLV.parse();
236
Suyong Won19fba4d2020-05-09 13:39:46 -0700237 if (!parameterTLV.hasValue()) {
238 _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700239 return;
240 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700241
242 std::string peerKeyBase64 = readString(parameterTLV.get(tlv_ecdh_pub));
243
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700244 if (peerKeyBase64 == "") {
Suyong Won19fba4d2020-05-09 13:39:46 -0700245 _LOG_ERROR("Empty ECDH PUB obtained from the Interest parameter.");
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700246 return;
247 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700248
249 // get server's ECDH pub key
250 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700251 try {
252 m_ecdh.deriveSecret(peerKeyBase64);
253 }
254 catch (const std::exception& e) {
255 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
256 return;
257 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700258 // generate salt for HKDF
259 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700260 // hkdf
261 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
Zhiyi Zhang36706832019-07-04 21:33:03 -0700262 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700263
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700264 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Suyong Won7968f7a2020-05-12 01:01:25 -0700265
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700266 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700267 // parse certificate request
268 Block cert_req = parameterTLV.get(tlv_cert_request);
269 cert_req.parse();
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700270
tylerliu4a00aad2020-09-26 02:03:17 -0700271 try {
272 security::v2::Certificate cert = security::v2::Certificate(cert_req.get(tlv::Data));
273 clientCert = make_shared<security::v2::Certificate>(cert);
274 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700275 catch (const std::exception& e) {
tylerliu4a00aad2020-09-26 02:03:17 -0700276 _LOG_ERROR("Unrecognized certificate request: " << e.what());
277 return;
278 }
279 // check the validity period
280 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
281 auto currentTime = time::system_clock::now();
282 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
283 _LOG_ERROR("Client requests a too old notBefore timepoint.");
284 return;
285 }
286 if (expectedPeriod.second > currentTime + m_config.m_maxValidityPeriod ||
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700287 expectedPeriod.second <= expectedPeriod.first) {
tylerliu4a00aad2020-09-26 02:03:17 -0700288 _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
289 return;
290 }
291
292 // verify the self-signed certificate, the request, and the token
293 if (!m_config.m_caPrefix.isPrefixOf(clientCert->getName()) // under ca prefix
294 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
tylerliuc84c3ba2020-09-28 18:09:46 -0700295 || clientCert->getIdentity().size() <= m_config.m_caPrefix.size()
296 || clientCert->getIdentity().size() > m_config.m_caPrefix.size() + m_config.m_maxSuffixLength) {
tylerliu4a00aad2020-09-26 02:03:17 -0700297 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
298 return;
299 }
300 if (!security::verifySignature(*clientCert, *clientCert)) {
301 _LOG_ERROR("Cert request with bad signature.");
302 return;
303 }
304 if (!security::verifySignature(request, *clientCert)) {
305 _LOG_ERROR("Interest with bad signature.");
306 return;
307 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700308 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700309 else if (requestType == RequestType::REVOKE) {
tylerliu4a00aad2020-09-26 02:03:17 -0700310 // parse certificate request
311 Block cert_revoke = parameterTLV.get(tlv_cert_to_revoke);
312 cert_revoke.parse();
313
314 try {
315 security::v2::Certificate cert = security::v2::Certificate(cert_revoke.get(tlv::Data));
316 clientCert = make_shared<security::v2::Certificate>(cert);
317 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700318 catch (const std::exception& e) {
tylerliu4a00aad2020-09-26 02:03:17 -0700319 _LOG_ERROR("Unrecognized certificate: " << e.what());
320 return;
321 }
322
323 // verify the certificate
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700324 if (!m_config.m_caPrefix.isPrefixOf(clientCert->getName()) // under ca prefix
tylerliu4a00aad2020-09-26 02:03:17 -0700325 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
tylerliuc84c3ba2020-09-28 18:09:46 -0700326 || clientCert->getIdentity().size() <= m_config.m_caPrefix.size()
327 || clientCert->getIdentity().size() > m_config.m_caPrefix.size() + m_config.m_maxSuffixLength) {
tylerliu4a00aad2020-09-26 02:03:17 -0700328 _LOG_ERROR("Invalid certificate name " << clientCert->getName());
329 return;
330 }
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700331 const auto& cert = m_keyChain.getPib().getIdentity(m_config.m_caPrefix).getDefaultKey().getDefaultCertificate();
tylerliu4a00aad2020-09-26 02:03:17 -0700332 if (!security::verifySignature(*clientCert, cert)) {
333 _LOG_ERROR("Cert request with bad signature.");
334 return;
335 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700336 }
337
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700338 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800339 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700340 CertificateRequest certRequest(m_config.m_caPrefix, requestId, requestType, Status::BEFORE_CHALLENGE, *clientCert);
Suyong Won19fba4d2020-05-09 13:39:46 -0700341
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800342 try {
343 m_storage->addRequest(certRequest);
344 }
345 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700346 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800347 return;
348 }
349
350 Data result;
351 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700352 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700353 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700354 result.setContent(NEW::encodeDataContent(myEcdhPubKeyBase64,
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700355 std::to_string(saltInt),
356 certRequest,
357 m_config.m_supportedChallenges));
358 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700359 else if (requestType == RequestType::REVOKE) {
tylerliu4a00aad2020-09-26 02:03:17 -0700360 result.setContent(REVOKE::encodeDataContent(myEcdhPubKeyBase64,
361 std::to_string(saltInt),
362 certRequest,
363 m_config.m_supportedChallenges));
364 }
Suyong Won256c9062020-05-11 02:45:56 -0700365 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800366 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700367
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700368 if (m_config.m_statusUpdateCallback) {
369 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800370 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800371}
372
373void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700374CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800375{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700376 // get certificate request state
377 CertificateRequest certRequest = getCertificateRequest(request);
378 if (certRequest.m_requestId == "") {
379 // cannot get the request state
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700380 _LOG_ERROR("Cannot find certificate request state from CA's storage.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700381 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
382 "Cannot find certificate request state from CA's storage."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800383 return;
384 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700385 // verify signature
386 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700387 _LOG_ERROR("Challenge Interest with bad signature.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700388 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE, "Bad signature."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800389 return;
390 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700391 // decrypt the parameters
Suyong Won19fba4d2020-05-09 13:39:46 -0700392 Buffer paramTLVPayload;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700393 try {
Suyong Won19fba4d2020-05-09 13:39:46 -0700394 paramTLVPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), m_aesKey,
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700395 (uint8_t*)"test", strlen("test"));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700396 }
397 catch (const std::exception& e) {
398 _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700399 m_storage->deleteRequest(certRequest.m_requestId);
400 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
401 "Cannot successfully decrypt the Interest parameters."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700402 return;
403 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700404 if (paramTLVPayload.size() == 0) {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700405 _LOG_ERROR("Got an empty buffer from content decryption.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700406 m_storage->deleteRequest(certRequest.m_requestId);
407 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
408 "Empty buffer from content decryption."));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700409 return;
410 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700411 Block paramTLV = makeBinaryBlock(tlv_encrypted_payload, paramTLVPayload.data(), paramTLVPayload.size());
412 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800413
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700414 // load the corresponding challenge module
Suyong Won19fba4d2020-05-09 13:39:46 -0700415 std::string challengeType = readString(paramTLV.get(tlv_selected_challenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800416 auto challenge = ChallengeModule::createChallengeModule(challengeType);
417 if (challenge == nullptr) {
418 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700419 m_storage->deleteRequest(certRequest.m_requestId);
420 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER, "Unrecognized challenge type"));
421 return;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800422 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700423
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700424 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
425 auto errorInfo = challenge->handleChallengeRequest(paramTLV, certRequest);
426 if (std::get<0>(errorInfo) != ErrorCode::NO_ERROR) {
427 m_storage->deleteRequest(certRequest.m_requestId);
428 m_face.put(generateErrorDataPacket(request.getName(), std::get<0>(errorInfo), std::get<1>(errorInfo)));
429 return;
430 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700431
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700432 Block payload;
433 if (certRequest.m_status == Status::PENDING) {
434 // if challenge succeeded
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700435 if (certRequest.m_requestType == RequestType::NEW) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700436 auto issuedCert = issueCertificate(certRequest);
437 certRequest.m_cert = issuedCert;
438 certRequest.m_status = Status::SUCCESS;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700439 try {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700440 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
441 m_storage->deleteRequest(certRequest.m_requestId);
442 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700443 }
444 catch (const std::exception& e) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700445 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700446 return;
447 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700448
Suyong Won44d0cce2020-05-10 04:07:43 -0700449 payload = CHALLENGE::encodeDataPayload(certRequest);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700450 payload.parse();
451 payload.push_back(makeNestedBlock(tlv_issued_cert_name, issuedCert.getName()));
452 payload.encode();
453
454 //contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
455 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800456 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700457 else if (certRequest.m_requestType == RequestType::REVOKE) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700458 certRequest.m_status = Status::SUCCESS;
459 try {
460 m_storage->deleteRequest(certRequest.m_requestId);
461 _LOG_TRACE("Certificate Revoked");
462 }
463 catch (const std::exception& e) {
464 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
465 return;
466 }
467
468 payload = CHALLENGE::encodeDataPayload(certRequest);
469 payload.parse();
470 _LOG_TRACE("Challenge succeeded. Certificate has been revoked");
471 }
472 }
473 else {
474 m_storage->updateRequest(certRequest);
475 payload = CHALLENGE::encodeDataPayload(certRequest);
476 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800477 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700478
479 Data result;
480 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700481 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700482
483 // encrypt the content
Suyong Won7968f7a2020-05-12 01:01:25 -0700484 auto contentBlock = encodeBlockWithAesGcm128(tlv::Content, m_aesKey, payload.value(),
485 payload.value_size(), (uint8_t*)"test", strlen("test"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700486 result.setContent(contentBlock);
Suyong Won256c9062020-05-11 02:45:56 -0700487 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700488 m_face.put(result);
489
490 if (m_config.m_statusUpdateCallback) {
491 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800492 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700493}
494
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800495security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700496CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800497{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700498 auto expectedPeriod =
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700499 certRequest.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700500 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800501 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700502
503 Name certName = certRequest.m_cert.getKeyName();
504 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800505 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700506 newCert.setContent(certRequest.m_cert.getContent());
507 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800508 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800509 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700510 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Suyong Won256c9062020-05-11 02:45:56 -0700511 m_config.m_caPrefix, signatureInfo);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800512
513 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700514 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800515 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800516}
517
518CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700519CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800520{
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400521 std::string requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800522 CertificateRequest certRequest;
523 try {
Suyong Won256c9062020-05-11 02:45:56 -0700524 requestId = readString(request.getName().at(m_config.m_caPrefix.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700525 }
526 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700527 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700528 }
529 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700530 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800531 certRequest = m_storage->getRequest(requestId);
532 }
533 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700534 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800535 }
536 return certRequest;
537}
538
539void
540CaModule::onRegisterFailed(const std::string& reason)
541{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700542 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800543}
544
545Block
546CaModule::dataContentFromJson(const JsonSection& jsonSection)
547{
548 std::stringstream ss;
549 boost::property_tree::write_json(ss, jsonSection);
550 return makeStringBlock(ndn::tlv::Content, ss.str());
551}
552
553JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700554CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800555{
556 std::string jsonString;
557 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700558 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700559 std::istringstream ss(jsonString);
560 JsonSection json;
561 boost::property_tree::json_parser::read_json(ss, json);
562 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800563 }
564 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700565 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800566 return JsonSection();
567 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800568}
569
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700570Data
571CaModule::generateErrorDataPacket(const Name& name, ErrorCode error, const std::string& errorInfo)
572{
573 Data result;
574 result.setName(name);
575 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
576 result.setContent(ErrorTLV::encodeDataContent(error, errorInfo));
577 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
578 return result;
579}
580
581} // namespace ndncert
582} // namespace ndn