blob: 9ac7453d56939cb915036fbbebf93d457320b6bc [file] [log] [blame]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoe5b43692021-11-15 22:05:03 -05002/*
Davide Pesavento9510c912024-02-25 17:50:05 -05003 * Copyright (c) 2017-2024, 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 Zhangdc25ddf2020-10-20 14:28:55 -070022#include "detail/crypto-helpers.hpp"
Zhiyi Zhang84e11842020-11-19 20:03:23 -080023#include "challenge/challenge-module.hpp"
Zhiyi Zhangc2c4ed22020-10-14 17:16:28 -070024#include "name-assignment/assignment-func.hpp"
Zhiyi Zhang062be6d2020-10-14 17:13:43 -070025#include "detail/challenge-encoder.hpp"
26#include "detail/error-encoder.hpp"
27#include "detail/info-encoder.hpp"
Zhiyi Zhang7cca76a2021-02-17 14:57:42 -080028#include "detail/request-encoder.hpp"
Zhiyi Zhang062be6d2020-10-14 17:13:43 -070029#include "detail/probe-encoder.hpp"
Davide Pesaventoe5b43692021-11-15 22:05:03 -050030
Zhiyi Zhang01e91a32020-09-29 12:10:00 -070031#include <ndn-cxx/metadata-object.hpp>
32#include <ndn-cxx/security/signing-helpers.hpp>
33#include <ndn-cxx/security/verification-helpers.hpp>
34#include <ndn-cxx/util/io.hpp>
Davide Pesavento9510c912024-02-25 17:50:05 -050035#include <ndn-cxx/util/logger.hpp>
Zhiyi Zhang01e91a32020-09-29 12:10:00 -070036#include <ndn-cxx/util/random.hpp>
Zhiyi Zhang8683ec92020-10-07 18:18:35 -070037#include <ndn-cxx/util/string-helper.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080038
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040039namespace ndncert::ca {
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080040
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040041const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
42const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070043
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070044NDN_LOG_INIT(ndncert.ca);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080045
Davide Pesavento0dc02012021-11-23 22:55:03 -050046CaModule::CaModule(ndn::Face& face, ndn::KeyChain& keyChain,
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080047 const std::string& configPath, const std::string& storageType)
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080048 : m_face(face)
49 , m_keyChain(keyChain)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080050{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070051 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080052 m_config.load(configPath);
Zhiyi Zhang44c6a352020-12-14 10:57:17 -080053 m_storage = CaStorage::createCaStorage(storageType, m_config.caProfile.caPrefix, "");
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040054
55 ndn::random::generateSecureBytes(m_requestIdGenKey);
56
57 if (m_config.nameAssignmentFuncs.empty()) {
tylerliuf2e6bb52020-12-13 13:23:05 -080058 m_config.nameAssignmentFuncs.push_back(NameAssignmentFunc::createNameAssignmentFunc("random"));
Zhiyi Zhang8683ec92020-10-07 18:18:35 -070059 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040060
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080061 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080062}
63
64CaModule::~CaModule()
65{
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070066 for (auto& handle : m_interestFilterHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070067 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080068 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070069 for (auto& handle : m_registeredPrefixHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070070 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080071 }
72}
73
74void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080075CaModule::registerPrefix()
76{
Zhiyi Zhang44c6a352020-12-14 10:57:17 -080077 Name prefix = m_config.caProfile.caPrefix;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070078 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080079
Tianyuan Yua721a4d2024-12-20 20:05:21 -080080 ndn::security::pib::Identity identity;
81 try {
82 identity = m_keyChain.getPib().getDefaultIdentity();
83 }
84 catch (const ndn::security::Pib::Error&) {
85 identity = m_keyChain.getPib().getIdentity(m_config.caProfile.caPrefix);
86 }
87
88 auto prefixHandle = m_face.registerPrefix(prefix,
Davide Pesaventoe5b43692021-11-15 22:05:03 -050089 [&] (const Name& name) {
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080090 // register INFO RDR metadata prefix
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040091 const auto& metaDataComp = ndn::MetadataObject::getKeywordComponent();
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080092 auto filterId = m_face.setInterestFilter(Name(name).append("INFO").append(metaDataComp),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050093 [this] (auto&&, const auto& i) { onCaProfileDiscovery(i); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080094 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhang696cd042020-10-07 21:27:36 -070095
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080096 // register PROBE prefix
97 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050098 [this] (auto&&, const auto& i) { onProbe(i); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080099 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700100
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800101 // register NEW prefix
102 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500103 [this] (auto&&, const auto& i) { onNewRenewRevoke(i, RequestType::NEW); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800104 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700105
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800106 // register SELECT prefix
107 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500108 [this] (auto&&, const auto& i) { onChallenge(i); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800109 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700110
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800111 // register REVOKE prefix
112 filterId = m_face.setInterestFilter(Name(name).append("REVOKE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500113 [this] (auto&&, const auto& i) { onNewRenewRevoke(i, RequestType::REVOKE); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800114 m_interestFilterHandles.push_back(filterId);
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500115
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800116 NDN_LOG_TRACE("Prefix " << name << " got registered");
117 },
Tianyuan Yua721a4d2024-12-20 20:05:21 -0800118 [this] (auto&&, const auto& reason) { onRegisterFailed(reason); },
119 ndn::signingByIdentity(identity));
120 m_registeredPrefixHandles.push_back(prefixHandle);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800121}
122
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700123void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800125{
tylerliu1f480be2020-11-10 13:02:53 -0800126 m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800127}
128
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700129Data
130CaModule::getCaProfileData()
swa77020643ac2020-03-26 02:24:45 -0700131{
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700132 if (m_profileData == nullptr) {
Davide Pesavento6a625d82023-02-24 03:38:06 -0500133 auto key = m_keyChain.getPib().getIdentity(m_config.caProfile.caPrefix).getDefaultKey();
134 Block contentTLV = infotlv::encodeDataContent(m_config.caProfile, key.getDefaultCertificate());
swa77020643ac2020-03-26 02:24:45 -0700135
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800136 Name infoPacketName(m_config.caProfile.caPrefix);
Davide Pesavento0dc02012021-11-23 22:55:03 -0500137 auto segmentComp = ndn::name::Component::fromSegment(0);
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800138 infoPacketName.append("CA").append("INFO").appendVersion().append(segmentComp);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700139 m_profileData = std::make_unique<Data>(infoPacketName);
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800140 m_profileData->setFinalBlock(segmentComp);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700141 m_profileData->setContent(contentTLV);
142 m_profileData->setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800143 m_keyChain.sign(*m_profileData, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700144 }
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700145 return *m_profileData;
146}
147
148void
Davide Pesavento0dc02012021-11-23 22:55:03 -0500149CaModule::onCaProfileDiscovery(const Interest&)
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700150{
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700151 NDN_LOG_TRACE("Received CA Profile MetaData discovery Interest");
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700152 if (m_profileData == nullptr) {
153 m_profileData = std::make_unique<Data>(getCaProfileData());
154 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500155 ndn::MetadataObject metadata;
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700156 metadata.setVersionedName(m_profileData->getName().getPrefix(-1));
157 Name discoveryInterestName(m_profileData->getName().getPrefix(-2));
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400158 discoveryInterestName.append(ndn::MetadataObject::getKeywordComponent());
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800159 m_face.put(metadata.makeData(discoveryInterestName, m_keyChain, signingByIdentity(m_config.caProfile.caPrefix)));
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700160}
swa77020643ac2020-03-26 02:24:45 -0700161
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700162void
Davide Pesavento6a625d82023-02-24 03:38:06 -0500163CaModule::onProbe(const Interest& request)
164{
swa77020643ac2020-03-26 02:24:45 -0700165 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700166 NDN_LOG_TRACE("Received PROBE request");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800167
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700168 // process PROBE requests: collect probe parameters
Tianyuan Yu13aac732022-03-03 20:59:54 -0800169 std::vector<ndn::Name> redirectionNames;
Davide Pesavento0dc02012021-11-23 22:55:03 -0500170 std::vector<ndn::PartialName> availableComponents;
Tianyuan Yu13aac732022-03-03 20:59:54 -0800171 try {
172 auto parameters = probetlv::decodeApplicationParameters(request.getApplicationParameters());
173
Davide Pesavento6a625d82023-02-24 03:38:06 -0500174 // collect redirections
Tianyuan Yu13aac732022-03-03 20:59:54 -0800175 for (auto &item : m_config.redirection) {
176 if (item.second->isRedirecting(parameters)) {
177 redirectionNames.push_back(item.first->getFullName());
178 }
179 }
180
Davide Pesavento6a625d82023-02-24 03:38:06 -0500181 // collect name assignments
Tianyuan Yu13aac732022-03-03 20:59:54 -0800182 for (auto &item : m_config.nameAssignmentFuncs) {
183 auto names = item->assignName(parameters);
184 availableComponents.insert(availableComponents.end(), names.begin(), names.end());
185 }
Davide Pesavento6a625d82023-02-24 03:38:06 -0500186 }
187 catch (const std::exception& e) {
Tianyuan Yu13aac732022-03-03 20:59:54 -0800188 NDN_LOG_ERROR("[CaModule::onProbe]Error in decoding TLV: " << e.what());
189 return;
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700190 }
Tianyuan Yu13aac732022-03-03 20:59:54 -0800191
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400192 if (availableComponents.empty() && redirectionNames.empty()) {
Zhiyi Zhang8683ec92020-10-07 18:18:35 -0700193 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhangcb8acf22020-10-07 19:35:16 -0700194 "Cannot generate available names from parameters provided."));
Zhiyi Zhang8683ec92020-10-07 18:18:35 -0700195 return;
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700196 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400197
Tianyuan Yu13aac732022-03-03 20:59:54 -0800198 std::vector<Name> availableNames;
199 for (const auto &component : availableComponents) {
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800200 Name newIdentityName = m_config.caProfile.caPrefix;
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700201 newIdentityName.append(component);
202 availableNames.push_back(newIdentityName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700203 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700204
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700205 Data result;
206 result.setName(request.getName());
Davide Pesavento6a625d82023-02-24 03:38:06 -0500207 result.setContent(probetlv::encodeDataContent(availableNames, m_config.caProfile.maxSuffixLength,
208 redirectionNames));
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700209 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800210 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700211 m_face.put(result);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700212 NDN_LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800213}
214
215void
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700216CaModule::onNewRenewRevoke(const Interest& request, RequestType requestType)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800217{
Davide Pesavento6a625d82023-02-24 03:38:06 -0500218 // verify ca cert validity
219 auto caCert = m_keyChain.getPib()
220 .getIdentity(m_config.caProfile.caPrefix)
221 .getDefaultKey()
222 .getDefaultCertificate();
tylerliuf62cfeb2021-01-28 11:01:33 -0800223 if (!caCert.isValid()) {
224 NDN_LOG_ERROR("Server certificate invalid/expired");
225 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
226 "Server certificate invalid/expired"));
227 return;
228 }
229
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700230 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700231 // REVOKE Naming Convention: /<CA-prefix>/CA/REVOKE/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700232 // get ECDH pub key and cert request
Suyong Won19fba4d2020-05-09 13:39:46 -0700233 const auto& parameterTLV = request.getApplicationParameters();
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800234 std::vector <uint8_t> ecdhPub;
Davide Pesavento0dc02012021-11-23 22:55:03 -0500235 std::shared_ptr<Certificate> clientCert;
tylerliu0790bdb2020-10-02 00:50:51 -0700236 try {
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800237 requesttlv::decodeApplicationParameters(parameterTLV, requestType, ecdhPub, clientCert);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700238 }
239 catch (const std::exception& e) {
tylerliu0790bdb2020-10-02 00:50:51 -0700240 if (!parameterTLV.hasValue()) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700241 NDN_LOG_ERROR("Empty TLV obtained from the Interest parameter.");
tylerliu0790bdb2020-10-02 00:50:51 -0700242 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
243 "Empty TLV obtained from the Interest parameter."));
244 return;
245 }
Suyong Won7968f7a2020-05-12 01:01:25 -0700246
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700247 NDN_LOG_ERROR("Unrecognized self-signed certificate: " << e.what());
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700248 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
tylerliu0790bdb2020-10-02 00:50:51 -0700249 "Unrecognized self-signed certificate."));
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700250 return;
251 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700252
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700253 if (ecdhPub.empty()) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700254 NDN_LOG_ERROR("Empty ECDH PUB obtained from the Interest parameter.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700255 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
256 "Empty ECDH PUB obtained from the Interest parameter."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700257 return;
258 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700259
260 // get server's ECDH pub key
tylerliu8e170d62020-09-30 01:31:53 -0700261 ECDHState ecdh;
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800262 std::vector <uint8_t> sharedSecret;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700263 try {
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700264 sharedSecret = ecdh.deriveSecret(ecdhPub);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700265 }
266 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700267 NDN_LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700268 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
269 "Cannot derive a shared secret using the provided ECDH key."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700270 return;
271 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700272
tylerliud9239f52020-09-30 17:25:16 -0700273 // verify identity name
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800274 if (!m_config.caProfile.caPrefix.isPrefixOf(clientCert->getIdentity())
Davide Pesavento0dc02012021-11-23 22:55:03 -0500275 || !Certificate::isValidName(clientCert->getName())
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800276 || clientCert->getIdentity().size() <= m_config.caProfile.caPrefix.size()) {
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800277 NDN_LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
278 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
279 "An invalid certificate name is being requested."));
280 return;
tylerliud9239f52020-09-30 17:25:16 -0700281 }
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800282 if (m_config.caProfile.maxSuffixLength) {
283 if (clientCert->getIdentity().size() > m_config.caProfile.caPrefix.size() + *m_config.caProfile.maxSuffixLength) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700284 NDN_LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
tylerliud9239f52020-09-30 17:25:16 -0700285 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
286 "An invalid certificate name is being requested."));
287 return;
288 }
289 }
290
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700291 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700292 // check the validity period
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400293 auto [notBefore, notAfter] = clientCert->getValidityPeriod().getPeriod();
tylerliu4a00aad2020-09-26 02:03:17 -0700294 auto currentTime = time::system_clock::now();
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400295 if (notBefore < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD ||
296 notAfter > currentTime + m_config.caProfile.maxValidityPeriod ||
297 notAfter <= notBefore) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700298 NDN_LOG_ERROR("An invalid validity period is being requested.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700299 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
300 "An invalid validity period is being requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700301 return;
302 }
tylerliud9239f52020-09-30 17:25:16 -0700303
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700304 // verify signature
Davide Pesavento0dc02012021-11-23 22:55:03 -0500305 if (!ndn::security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700306 NDN_LOG_ERROR("Invalid signature in the self-signed certificate.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700307 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
308 "Invalid signature in the self-signed certificate."));
tylerliu4a00aad2020-09-26 02:03:17 -0700309 return;
310 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500311 if (!ndn::security::verifySignature(request, *clientCert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700312 NDN_LOG_ERROR("Invalid signature in the Interest packet.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700313 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
314 "Invalid signature in the Interest packet."));
tylerliu4a00aad2020-09-26 02:03:17 -0700315 return;
316 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700317 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700318 else if (requestType == RequestType::REVOKE) {
tylerliud9239f52020-09-30 17:25:16 -0700319 //verify cert is from this CA
Davide Pesavento0dc02012021-11-23 22:55:03 -0500320 if (!ndn::security::verifySignature(*clientCert, caCert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700321 NDN_LOG_ERROR("Invalid signature in the certificate to revoke.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700322 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
323 "Invalid signature in the certificate to revoke."));
tylerliu4a00aad2020-09-26 02:03:17 -0700324 return;
325 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700326 }
327
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700328 // create new request instance
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700329 uint8_t requestIdData[32];
330 Block certNameTlv = clientCert->getName().wireEncode();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700331 try {
Davide Pesaventof48e1632023-04-24 22:00:26 -0400332 hmacSha256(certNameTlv.data(), certNameTlv.size(), m_requestIdGenKey, 32, requestIdData);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700333 }
334 catch (const std::runtime_error& e) {
Davide Pesaventof48e1632023-04-24 22:00:26 -0400335 NDN_LOG_ERROR("Error computing the request ID: " << e.what());
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700336 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
337 "Error computing the request ID."));
338 return;
339 }
Zhiyi Zhangc9ada1b2020-10-29 19:13:15 -0700340 RequestId id;
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700341 std::memcpy(id.data(), requestIdData, id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800342 // initialize request state
343 RequestState requestState;
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800344 requestState.caPrefix = m_config.caProfile.caPrefix;
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800345 requestState.requestId = id;
346 requestState.requestType = requestType;
347 requestState.cert = *clientCert;
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800348 // generate salt for HKDF
349 std::array<uint8_t, 32> salt;
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400350 ndn::random::generateSecureBytes(salt);
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800351 // hkdf
352 std::array<uint8_t, 16> aesKey;
353 hkdf(sharedSecret.data(), sharedSecret.size(), salt.data(), salt.size(),
354 aesKey.data(), aesKey.size(), id.data(), id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800355 requestState.encryptionKey = aesKey;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700356 try {
357 m_storage->addRequest(requestState);
358 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500359 catch (const std::runtime_error&) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700360 NDN_LOG_ERROR("Duplicate Request ID: The same request has been seen before.");
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700361 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700362 "Duplicate Request ID: The same request has been seen before."));
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700363 return;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700364 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400365
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800366 Data result;
367 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700368 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800369 result.setContent(requesttlv::encodeDataContent(ecdh.getSelfPubKey(),
370 salt, requestState.requestId,
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800371 m_config.caProfile.supportedChallenges));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800372 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800373 m_face.put(result);
tylerliu1f480be2020-11-10 13:02:53 -0800374 if (m_statusUpdateCallback) {
375 m_statusUpdateCallback(requestState);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800376 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800377}
378
379void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700380CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800381{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700382 // get certificate request state
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700383 auto requestState = getCertificateRequest(request);
Zhiyi Zhang84207c52020-10-28 21:45:44 -0700384 if (requestState == nullptr) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700385 NDN_LOG_ERROR("No certificate request state can be found.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700386 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700387 "No certificate request state can be found."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800388 return;
389 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400390
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700391 // verify signature
Davide Pesavento0dc02012021-11-23 22:55:03 -0500392 if (!ndn::security::verifySignature(request, requestState->cert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700393 NDN_LOG_ERROR("Invalid Signature in the Interest packet.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700394 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
395 "Invalid Signature in the Interest packet."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800396 return;
397 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400398
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700399 // decrypt the parameters
Davide Pesavento0dc02012021-11-23 22:55:03 -0500400 ndn::Buffer paramTLVPayload;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700401 try {
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800402 paramTLVPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), requestState->encryptionKey.data(),
403 requestState->requestId.data(), requestState->requestId.size(),
404 requestState->decryptionIv, requestState->encryptionIv);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700405 }
406 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700407 NDN_LOG_ERROR("Interest paramaters decryption failed: " << e.what());
tylerliu7b9185c2020-11-24 12:15:18 -0800408 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700409 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700410 "Interest paramaters decryption failed."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700411 return;
412 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400413 if (paramTLVPayload.empty()) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700414 NDN_LOG_ERROR("No parameters are found after decryption.");
tylerliu7b9185c2020-11-24 12:15:18 -0800415 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700416 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700417 "No parameters are found after decryption."));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700418 return;
419 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400420
421 auto paramTLV = ndn::makeBinaryBlock(tlv::EncryptedPayload, paramTLVPayload);
Suyong Won44d0cce2020-05-10 04:07:43 -0700422 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800423
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700424 // load the corresponding challenge module
tylerliu50d679e2020-10-14 14:08:39 -0700425 std::string challengeType = readString(paramTLV.get(tlv::SelectedChallenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800426 auto challenge = ChallengeModule::createChallengeModule(challengeType);
427 if (challenge == nullptr) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700428 NDN_LOG_TRACE("Unrecognized challenge type: " << challengeType);
tylerliu7b9185c2020-11-24 12:15:18 -0800429 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800430 m_face.put(
431 generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER, "Unrecognized challenge type."));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700432 return;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800433 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700434
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700435 NDN_LOG_TRACE("CHALLENGE module to be load: " << challengeType);
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700436 auto errorInfo = challenge->handleChallengeRequest(paramTLV, *requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700437 if (std::get<0>(errorInfo) != ErrorCode::NO_ERROR) {
tylerliu7b9185c2020-11-24 12:15:18 -0800438 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700439 m_face.put(generateErrorDataPacket(request.getName(), std::get<0>(errorInfo), std::get<1>(errorInfo)));
440 return;
441 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700442
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700443 Block payload;
tylerliu7b9185c2020-11-24 12:15:18 -0800444 if (requestState->status == Status::PENDING) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700445 // if challenge succeeded
tylerliud82cc5c2020-12-21 23:58:39 -0800446 if (requestState->requestType == RequestType::NEW || requestState->requestType == RequestType::RENEW) {
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700447 auto issuedCert = issueCertificate(*requestState);
tylerliu7b9185c2020-11-24 12:15:18 -0800448 requestState->cert = issuedCert;
449 requestState->status = Status::SUCCESS;
450 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700451
Tianyuan Yu42bc63e2024-10-18 10:18:38 -0700452 payload = challengetlv::encodeDataContent(*requestState, issuedCert.getName(),
453 m_config.caProfile.forwardingHint);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700454 NDN_LOG_TRACE("Challenge succeeded. Certificate has been issued: " << issuedCert.getName());
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800455 }
tylerliu7b9185c2020-11-24 12:15:18 -0800456 else if (requestState->requestType == RequestType::REVOKE) {
457 requestState->status = Status::SUCCESS;
458 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangf9d94eb2020-12-21 15:39:38 -0800459 // TODO: where is the code to revoke?
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800460 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700461 NDN_LOG_TRACE("Challenge succeeded. Certificate has been revoked");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700462 }
463 }
464 else {
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800465 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800466 m_storage->updateRequest(*requestState);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700467 NDN_LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800468 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700469
470 Data result;
471 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700472 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang4c259db2020-10-30 09:36:01 -0700473 result.setContent(payload);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800474 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700475 m_face.put(result);
tylerliu1f480be2020-11-10 13:02:53 -0800476 if (m_statusUpdateCallback) {
477 m_statusUpdateCallback(*requestState);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800478 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700479}
480
Davide Pesavento0dc02012021-11-23 22:55:03 -0500481Certificate
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700482CaModule::issueCertificate(const RequestState& requestState)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800483{
Davide Pesavento6481e1e2024-12-22 17:44:15 -0500484 ndn::security::MakeCertificateOptions opts;
485 opts.issuerId = Name::Component("NDNCERT");
486 opts.validity = requestState.cert.getValidityPeriod();
487 auto newCert = m_keyChain.makeCertificate(requestState.cert,
488 signingByIdentity(m_config.caProfile.caPrefix), opts);
489 NDN_LOG_TRACE("Signed new certificate: " << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800490 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800491}
492
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800493std::unique_ptr <RequestState>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700494CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800495{
Zhiyi Zhangc9ada1b2020-10-29 19:13:15 -0700496 RequestId requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800497 try {
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800498 auto& component = request.getName().at(m_config.caProfile.caPrefix.size() + 2);
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700499 std::memcpy(requestId.data(), component.value(), component.value_size());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700500 }
501 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700502 NDN_LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700503 return nullptr;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700504 }
505 try {
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400506 NDN_LOG_TRACE("Request Id to query the database " << ndn::toHex(requestId));
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700507 return std::make_unique<RequestState>(m_storage->getRequest(requestId));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800508 }
509 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700510 NDN_LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700511 return nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800512 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800513}
514
515void
516CaModule::onRegisterFailed(const std::string& reason)
517{
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700518 NDN_LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800519}
520
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700521Data
522CaModule::generateErrorDataPacket(const Name& name, ErrorCode error, const std::string& errorInfo)
523{
524 Data result;
525 result.setName(name);
526 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800527 result.setContent(errortlv::encodeDataContent(error, errorInfo));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800528 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700529 return result;
530}
531
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400532} // namespace ndncert::ca