blob: 3eb349e026cbe21ce22aea885469e0ff2224a349 [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/*
Tianyuan Yu050b3ea2025-03-13 15:20:34 -07003 * Copyright (c) 2017-2025, 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 Zhang84e11842020-11-19 20:03:23 -080022#include "challenge/challenge-module.hpp"
Zhiyi Zhangc2c4ed22020-10-14 17:16:28 -070023#include "name-assignment/assignment-func.hpp"
Zhiyi Zhang062be6d2020-10-14 17:13:43 -070024#include "detail/challenge-encoder.hpp"
Davide Pesavento6866b902024-12-22 23:11:26 -050025#include "detail/crypto-helpers.hpp"
Zhiyi Zhang062be6d2020-10-14 17:13:43 -070026#include "detail/error-encoder.hpp"
27#include "detail/info-encoder.hpp"
Zhiyi Zhang062be6d2020-10-14 17:13:43 -070028#include "detail/probe-encoder.hpp"
Davide Pesavento6866b902024-12-22 23:11:26 -050029#include "detail/request-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>
Davide Pesavento9510c912024-02-25 17:50:05 -050034#include <ndn-cxx/util/logger.hpp>
Zhiyi Zhang01e91a32020-09-29 12:10:00 -070035#include <ndn-cxx/util/random.hpp>
Zhiyi Zhang8683ec92020-10-07 18:18:35 -070036#include <ndn-cxx/util/string-helper.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080037
Davide Pesavento0d1d11c2022-04-11 22:11:34 -040038namespace ndncert::ca {
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080039
Davide Pesavento6866b902024-12-22 23:11:26 -050040constexpr time::milliseconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Tianyuan Yu050b3ea2025-03-13 15:20:34 -070041constexpr time::seconds REQUEST_VALIDITY_PERIOD_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070042
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070043NDN_LOG_INIT(ndncert.ca);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080044
Davide Pesavento0dc02012021-11-23 22:55:03 -050045CaModule::CaModule(ndn::Face& face, ndn::KeyChain& keyChain,
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080046 const std::string& configPath, const std::string& storageType)
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080047 : m_face(face)
48 , m_keyChain(keyChain)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080049{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070050 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080051 m_config.load(configPath);
Zhiyi Zhang44c6a352020-12-14 10:57:17 -080052 m_storage = CaStorage::createCaStorage(storageType, m_config.caProfile.caPrefix, "");
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040053
54 ndn::random::generateSecureBytes(m_requestIdGenKey);
55
56 if (m_config.nameAssignmentFuncs.empty()) {
tylerliuf2e6bb52020-12-13 13:23:05 -080057 m_config.nameAssignmentFuncs.push_back(NameAssignmentFunc::createNameAssignmentFunc("random"));
Zhiyi Zhang8683ec92020-10-07 18:18:35 -070058 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040059
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080060 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080061}
62
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080063void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080064CaModule::registerPrefix()
65{
Zhiyi Zhang44c6a352020-12-14 10:57:17 -080066 Name prefix = m_config.caProfile.caPrefix;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070067 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080068
Tianyuan Yua721a4d2024-12-20 20:05:21 -080069 ndn::security::pib::Identity identity;
70 try {
71 identity = m_keyChain.getPib().getDefaultIdentity();
72 }
73 catch (const ndn::security::Pib::Error&) {
74 identity = m_keyChain.getPib().getIdentity(m_config.caProfile.caPrefix);
75 }
76
Davide Pesavento6866b902024-12-22 23:11:26 -050077 m_registeredPrefixes.emplace_back(m_face.registerPrefix(prefix,
Davide Pesaventoe5b43692021-11-15 22:05:03 -050078 [&] (const Name& name) {
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080079 // register INFO RDR metadata prefix
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -040080 const auto& metaDataComp = ndn::MetadataObject::getKeywordComponent();
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080081 auto filterId = m_face.setInterestFilter(Name(name).append("INFO").append(metaDataComp),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050082 [this] (auto&&, const auto& i) { onCaProfileDiscovery(i); });
Davide Pesavento6866b902024-12-22 23:11:26 -050083 m_interestFilters.emplace_back(std::move(filterId));
Zhiyi Zhang696cd042020-10-07 21:27:36 -070084
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080085 // register PROBE prefix
86 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050087 [this] (auto&&, const auto& i) { onProbe(i); });
Davide Pesavento6866b902024-12-22 23:11:26 -050088 m_interestFilters.emplace_back(std::move(filterId));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070089
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080090 // register NEW prefix
91 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050092 [this] (auto&&, const auto& i) { onNewRenewRevoke(i, RequestType::NEW); });
Davide Pesavento6866b902024-12-22 23:11:26 -050093 m_interestFilters.emplace_back(std::move(filterId));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070094
Davide Pesavento6866b902024-12-22 23:11:26 -050095 // register CHALLENGE prefix
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080096 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050097 [this] (auto&&, const auto& i) { onChallenge(i); });
Davide Pesavento6866b902024-12-22 23:11:26 -050098 m_interestFilters.emplace_back(std::move(filterId));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070099
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800100 // register REVOKE prefix
101 filterId = m_face.setInterestFilter(Name(name).append("REVOKE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500102 [this] (auto&&, const auto& i) { onNewRenewRevoke(i, RequestType::REVOKE); });
Davide Pesavento6866b902024-12-22 23:11:26 -0500103 m_interestFilters.emplace_back(std::move(filterId));
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500104
Davide Pesavento6866b902024-12-22 23:11:26 -0500105 NDN_LOG_TRACE("Prefix " << name << " registered successfully");
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800106 },
Davide Pesavento6866b902024-12-22 23:11:26 -0500107 [] (auto&&, const auto& reason) {
108 NDN_THROW(std::runtime_error("Failed to register prefix: " + reason));
109 },
110 ndn::signingByIdentity(identity)));
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800111}
112
Davide Pesavento6866b902024-12-22 23:11:26 -0500113const Data&
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700114CaModule::getCaProfileData()
swa77020643ac2020-03-26 02:24:45 -0700115{
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700116 if (m_profileData == nullptr) {
Davide Pesavento6a625d82023-02-24 03:38:06 -0500117 auto key = m_keyChain.getPib().getIdentity(m_config.caProfile.caPrefix).getDefaultKey();
Davide Pesavento6866b902024-12-22 23:11:26 -0500118 Block content = infotlv::encodeDataContent(m_config.caProfile, key.getDefaultCertificate());
swa77020643ac2020-03-26 02:24:45 -0700119
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800120 Name infoPacketName(m_config.caProfile.caPrefix);
Davide Pesavento6866b902024-12-22 23:11:26 -0500121 auto segmentComp = Name::Component::fromSegment(0);
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800122 infoPacketName.append("CA").append("INFO").appendVersion().append(segmentComp);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700123 m_profileData = std::make_unique<Data>(infoPacketName);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700124 m_profileData->setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Davide Pesavento6866b902024-12-22 23:11:26 -0500125 m_profileData->setFinalBlock(segmentComp);
126 m_profileData->setContent(content);
127
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800128 m_keyChain.sign(*m_profileData, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700129 }
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700130 return *m_profileData;
131}
132
133void
Davide Pesavento0dc02012021-11-23 22:55:03 -0500134CaModule::onCaProfileDiscovery(const Interest&)
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700135{
Davide Pesavento6866b902024-12-22 23:11:26 -0500136 NDN_LOG_TRACE("Received CA profile metadata discovery Interest");
137
138 const auto& profileDataName = getCaProfileData().getName();
Davide Pesavento0dc02012021-11-23 22:55:03 -0500139 ndn::MetadataObject metadata;
Davide Pesavento6866b902024-12-22 23:11:26 -0500140 metadata.setVersionedName(profileDataName.getPrefix(-1));
141 Name discoveryInterestName(profileDataName.getPrefix(-2));
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400142 discoveryInterestName.append(ndn::MetadataObject::getKeywordComponent());
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800143 m_face.put(metadata.makeData(discoveryInterestName, m_keyChain, signingByIdentity(m_config.caProfile.caPrefix)));
Davide Pesavento6866b902024-12-22 23:11:26 -0500144
145 NDN_LOG_TRACE("Sent CA profile metadata");
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700146}
swa77020643ac2020-03-26 02:24:45 -0700147
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700148void
Davide Pesavento6a625d82023-02-24 03:38:06 -0500149CaModule::onProbe(const Interest& request)
150{
Davide Pesavento6866b902024-12-22 23:11:26 -0500151 // PROBE naming convention: /<CA-Prefix>/CA/PROBE/<ParametersSha256Digest>
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700152 NDN_LOG_TRACE("Received PROBE request");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800153
Davide Pesavento6866b902024-12-22 23:11:26 -0500154 // process PROBE request: collect probe parameters
155 std::multimap<std::string, std::string> parameters;
Tianyuan Yu13aac732022-03-03 20:59:54 -0800156 try {
Davide Pesavento6866b902024-12-22 23:11:26 -0500157 parameters = probetlv::decodeApplicationParameters(request.getApplicationParameters());
Davide Pesavento6a625d82023-02-24 03:38:06 -0500158 }
159 catch (const std::exception& e) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500160 NDN_LOG_ERROR("Cannot decode PROBE parameters: " << e.what());
Tianyuan Yu13aac732022-03-03 20:59:54 -0800161 return;
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700162 }
Tianyuan Yu13aac732022-03-03 20:59:54 -0800163
Davide Pesavento6866b902024-12-22 23:11:26 -0500164 // collect redirections
165 std::vector<ndn::Name> redirectionNames;
166 for (const auto& item : m_config.redirection) {
167 if (item.second->isRedirecting(parameters)) {
168 redirectionNames.push_back(item.first->getFullName());
169 }
170 }
171
172 // collect name assignments
173 std::vector<ndn::PartialName> availableComponents;
174 for (const auto& item : m_config.nameAssignmentFuncs) {
175 auto names = item->assignName(parameters);
176 availableComponents.insert(availableComponents.end(), names.begin(), names.end());
177 }
178
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400179 if (availableComponents.empty() && redirectionNames.empty()) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500180 NDN_LOG_TRACE("Cannot generate available names");
181 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
182 "Cannot generate available names from parameters provided."));
Zhiyi Zhang8683ec92020-10-07 18:18:35 -0700183 return;
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700184 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400185
Tianyuan Yu13aac732022-03-03 20:59:54 -0800186 std::vector<Name> availableNames;
Davide Pesavento6866b902024-12-22 23:11:26 -0500187 availableNames.reserve(availableComponents.size());
188 for (const auto& component : availableComponents) {
189 availableNames.push_back(Name(m_config.caProfile.caPrefix).append(component));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700191
Davide Pesavento6866b902024-12-22 23:11:26 -0500192 Data result(request.getName());
193 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Davide Pesavento6a625d82023-02-24 03:38:06 -0500194 result.setContent(probetlv::encodeDataContent(availableNames, m_config.caProfile.maxSuffixLength,
195 redirectionNames));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800196 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700197 m_face.put(result);
Davide Pesavento6866b902024-12-22 23:11:26 -0500198 NDN_LOG_TRACE("Sent PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800199}
200
201void
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700202CaModule::onNewRenewRevoke(const Interest& request, RequestType requestType)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800203{
Davide Pesavento6866b902024-12-22 23:11:26 -0500204 // NEW naming convention: /<CA-Prefix>/CA/NEW/<ParametersSha256Digest>
205 // REVOKE naming convention: /<CA-Prefix>/CA/REVOKE/<ParametersSha256Digest>
206 NDN_LOG_TRACE("Received " << requestType << " request");
207
Davide Pesavento6a625d82023-02-24 03:38:06 -0500208 // verify ca cert validity
209 auto caCert = m_keyChain.getPib()
210 .getIdentity(m_config.caProfile.caPrefix)
211 .getDefaultKey()
212 .getDefaultCertificate();
tylerliuf62cfeb2021-01-28 11:01:33 -0800213 if (!caCert.isValid()) {
214 NDN_LOG_ERROR("Server certificate invalid/expired");
Davide Pesavento6866b902024-12-22 23:11:26 -0500215 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
216 "Server certificate invalid/expired"));
tylerliuf62cfeb2021-01-28 11:01:33 -0800217 return;
218 }
219
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700220 // get ECDH pub key and cert request
Davide Pesavento6866b902024-12-22 23:11:26 -0500221 const auto& paramsBlock = request.getApplicationParameters();
222 std::vector<uint8_t> ecdhPub;
Davide Pesavento0dc02012021-11-23 22:55:03 -0500223 std::shared_ptr<Certificate> clientCert;
tylerliu0790bdb2020-10-02 00:50:51 -0700224 try {
Davide Pesavento6866b902024-12-22 23:11:26 -0500225 requesttlv::decodeApplicationParameters(paramsBlock, requestType, ecdhPub, clientCert);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700226 }
227 catch (const std::exception& e) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500228 if (!paramsBlock.hasValue()) {
229 NDN_LOG_ERROR("Empty parameters in request");
230 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
231 "Malformed request parameters."));
tylerliu0790bdb2020-10-02 00:50:51 -0700232 }
Davide Pesavento6866b902024-12-22 23:11:26 -0500233 else {
234 NDN_LOG_ERROR("Cannot decode request parameters: " << e.what());
235 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
236 "Malformed request parameters."));
237 }
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700238 return;
239 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700240
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700241 if (ecdhPub.empty()) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500242 NDN_LOG_ERROR("Empty ECDH public key in request parameters");
243 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
244 "Malformed request parameters."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700245 return;
246 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700247
tylerliud9239f52020-09-30 17:25:16 -0700248 // verify identity name
Davide Pesavento6866b902024-12-22 23:11:26 -0500249 if (!Certificate::isValidName(clientCert->getName()) ||
250 !m_config.caProfile.caPrefix.isPrefixOf(clientCert->getIdentity()) ||
251 clientCert->getIdentity().size() <= m_config.caProfile.caPrefix.size()) {
252 NDN_LOG_ERROR("Invalid certificate name requested: " << clientCert->getName());
253 m_face.put(makeErrorPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
254 "Invalid certificate name requested."));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800255 return;
tylerliud9239f52020-09-30 17:25:16 -0700256 }
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800257 if (m_config.caProfile.maxSuffixLength) {
258 if (clientCert->getIdentity().size() > m_config.caProfile.caPrefix.size() + *m_config.caProfile.maxSuffixLength) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500259 NDN_LOG_ERROR("Invalid certificate name requested: " << clientCert->getName());
260 m_face.put(makeErrorPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
261 "Invalid certificate name requested."));
tylerliud9239f52020-09-30 17:25:16 -0700262 return;
263 }
264 }
265
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700266 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700267 // check the validity period
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400268 auto [notBefore, notAfter] = clientCert->getValidityPeriod().getPeriod();
Tianyuan Yu050b3ea2025-03-13 15:20:34 -0700269 auto validFor = notAfter - notBefore;
270 auto now = time::system_clock::now();
271 if (notBefore < now - REQUEST_VALIDITY_PERIOD_GRACE_PERIOD ||
272 notAfter > now + m_config.caProfile.maxValidityPeriod + REQUEST_VALIDITY_PERIOD_GRACE_PERIOD ||
273 validFor > m_config.caProfile.maxValidityPeriod ||
274 validFor < 0_s) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500275 NDN_LOG_ERROR("Invalid validity period requested");
276 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
277 "Invalid validity period requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700278 return;
279 }
tylerliud9239f52020-09-30 17:25:16 -0700280
Davide Pesavento6866b902024-12-22 23:11:26 -0500281 // verify signatures
Davide Pesavento0dc02012021-11-23 22:55:03 -0500282 if (!ndn::security::verifySignature(*clientCert, *clientCert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500283 NDN_LOG_ERROR("Invalid signature in the self-signed certificate");
284 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
285 "Invalid signature in the self-signed certificate."));
tylerliu4a00aad2020-09-26 02:03:17 -0700286 return;
287 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500288 if (!ndn::security::verifySignature(request, *clientCert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500289 NDN_LOG_ERROR("Invalid signature in the Interest packet");
290 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
291 "Invalid signature in the Interest packet."));
tylerliu4a00aad2020-09-26 02:03:17 -0700292 return;
293 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700294 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700295 else if (requestType == RequestType::REVOKE) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500296 // verify cert is from this CA
Davide Pesavento0dc02012021-11-23 22:55:03 -0500297 if (!ndn::security::verifySignature(*clientCert, caCert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500298 NDN_LOG_ERROR("Invalid signature in the certificate to revoke");
299 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
300 "Invalid signature in the certificate to revoke."));
tylerliu4a00aad2020-09-26 02:03:17 -0700301 return;
302 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700303 }
304
Davide Pesavento6866b902024-12-22 23:11:26 -0500305 // derive server's ECDH pub key
306 ECDHState ecdh;
307 std::vector<uint8_t> sharedSecret;
308 try {
309 sharedSecret = ecdh.deriveSecret(ecdhPub);
310 }
311 catch (const std::exception& e) {
312 NDN_LOG_ERROR("Cannot derive a shared secret using the provided ECDH public key: " << e.what());
313 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
314 "Cannot derive a shared secret using the provided ECDH public key."));
315 return;
316 }
317
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700318 // create new request instance
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700319 uint8_t requestIdData[32];
320 Block certNameTlv = clientCert->getName().wireEncode();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700321 try {
Davide Pesaventof48e1632023-04-24 22:00:26 -0400322 hmacSha256(certNameTlv.data(), certNameTlv.size(), m_requestIdGenKey, 32, requestIdData);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700323 }
324 catch (const std::runtime_error& e) {
Davide Pesaventof48e1632023-04-24 22:00:26 -0400325 NDN_LOG_ERROR("Error computing the request ID: " << e.what());
Davide Pesavento6866b902024-12-22 23:11:26 -0500326 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
327 "Error computing the request ID."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700328 return;
329 }
Zhiyi Zhangc9ada1b2020-10-29 19:13:15 -0700330 RequestId id;
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700331 std::memcpy(id.data(), requestIdData, id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800332 // initialize request state
333 RequestState requestState;
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800334 requestState.caPrefix = m_config.caProfile.caPrefix;
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800335 requestState.requestId = id;
336 requestState.requestType = requestType;
337 requestState.cert = *clientCert;
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800338 // generate salt for HKDF
339 std::array<uint8_t, 32> salt;
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400340 ndn::random::generateSecureBytes(salt);
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800341 // hkdf
342 std::array<uint8_t, 16> aesKey;
343 hkdf(sharedSecret.data(), sharedSecret.size(), salt.data(), salt.size(),
344 aesKey.data(), aesKey.size(), id.data(), id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800345 requestState.encryptionKey = aesKey;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700346 try {
347 m_storage->addRequest(requestState);
348 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500349 catch (const std::runtime_error&) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500350 NDN_LOG_ERROR("Duplicate request ID " << ndn::toHex(id));
351 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER, "Duplicate request ID."));
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700352 return;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700353 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400354
Davide Pesavento6866b902024-12-22 23:11:26 -0500355 Data result(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700356 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800357 result.setContent(requesttlv::encodeDataContent(ecdh.getSelfPubKey(),
358 salt, requestState.requestId,
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800359 m_config.caProfile.supportedChallenges));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800360 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800361 m_face.put(result);
Davide Pesavento6866b902024-12-22 23:11:26 -0500362 NDN_LOG_TRACE("Sent " << requestType << " response");
363
tylerliu1f480be2020-11-10 13:02:53 -0800364 if (m_statusUpdateCallback) {
365 m_statusUpdateCallback(requestState);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800366 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800367}
368
369void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700370CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800371{
Davide Pesavento6866b902024-12-22 23:11:26 -0500372 NDN_LOG_TRACE("Received CHALLENGE request");
373
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700374 // get certificate request state
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700375 auto requestState = getCertificateRequest(request);
Zhiyi Zhang84207c52020-10-28 21:45:44 -0700376 if (requestState == nullptr) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500377 NDN_LOG_ERROR("No pending certificate request found");
378 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
379 "No pending certificate request found."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800380 return;
381 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400382
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700383 // verify signature
Davide Pesavento0dc02012021-11-23 22:55:03 -0500384 if (!ndn::security::verifySignature(request, requestState->cert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500385 NDN_LOG_ERROR("Invalid signature in the Interest packet");
386 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
387 "Invalid signature in the Interest packet."));
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 // decrypt the parameters
Davide Pesavento6866b902024-12-22 23:11:26 -0500392 ndn::Buffer plaintext;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700393 try {
Davide Pesavento6866b902024-12-22 23:11:26 -0500394 plaintext = decodeBlockWithAesGcm128(request.getApplicationParameters(), requestState->encryptionKey.data(),
395 requestState->requestId.data(), requestState->requestId.size(),
396 requestState->decryptionIv, requestState->encryptionIv);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700397 }
398 catch (const std::exception& e) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500399 NDN_LOG_ERROR("Cannot decrypt challenge parameters: " << e.what());
tylerliu7b9185c2020-11-24 12:15:18 -0800400 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500401 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
402 "Malformed challenge parameters."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700403 return;
404 }
Davide Pesavento6866b902024-12-22 23:11:26 -0500405 if (plaintext.empty()) {
406 NDN_LOG_ERROR("Empty parameters after decryption");
tylerliu7b9185c2020-11-24 12:15:18 -0800407 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500408 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
409 "Malformed challenge parameters."));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700410 return;
411 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400412
Davide Pesavento6866b902024-12-22 23:11:26 -0500413 auto paramTLV = ndn::makeBinaryBlock(tlv::EncryptedPayload, plaintext);
Suyong Won44d0cce2020-05-10 04:07:43 -0700414 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800415
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700416 // load the corresponding challenge module
tylerliu50d679e2020-10-14 14:08:39 -0700417 std::string challengeType = readString(paramTLV.get(tlv::SelectedChallenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800418 auto challenge = ChallengeModule::createChallengeModule(challengeType);
419 if (challenge == nullptr) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500420 NDN_LOG_TRACE("Unsupported challenge type: " << challengeType);
tylerliu7b9185c2020-11-24 12:15:18 -0800421 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500422 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
423 "Unsupported challenge type."));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700424 return;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800425 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700426
Davide Pesavento6866b902024-12-22 23:11:26 -0500427 NDN_LOG_TRACE("Using challenge: " << challengeType);
428 auto [errCode, errMsg] = challenge->handleChallengeRequest(paramTLV, *requestState);
429 if (errCode != ErrorCode::NO_ERROR) {
tylerliu7b9185c2020-11-24 12:15:18 -0800430 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500431 m_face.put(makeErrorPacket(request.getName(), errCode, errMsg));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700432 return;
433 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700434
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700435 Block payload;
tylerliu7b9185c2020-11-24 12:15:18 -0800436 if (requestState->status == Status::PENDING) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500437 NDN_LOG_TRACE("Challenge succeeded");
438 if (requestState->requestType == RequestType::NEW ||
439 requestState->requestType == RequestType::RENEW) {
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700440 auto issuedCert = issueCertificate(*requestState);
tylerliu7b9185c2020-11-24 12:15:18 -0800441 requestState->cert = issuedCert;
442 requestState->status = Status::SUCCESS;
443 m_storage->deleteRequest(requestState->requestId);
Tianyuan Yu42bc63e2024-10-18 10:18:38 -0700444 payload = challengetlv::encodeDataContent(*requestState, issuedCert.getName(),
445 m_config.caProfile.forwardingHint);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800446 }
tylerliu7b9185c2020-11-24 12:15:18 -0800447 else if (requestState->requestType == RequestType::REVOKE) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500448 // TODO: where is the code to revoke?
tylerliu7b9185c2020-11-24 12:15:18 -0800449 requestState->status = Status::SUCCESS;
450 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800451 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700452 }
453 }
454 else {
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800455 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800456 m_storage->updateRequest(*requestState);
Davide Pesavento6866b902024-12-22 23:11:26 -0500457 NDN_LOG_TRACE("Challenge continues");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800458 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700459
Davide Pesavento6866b902024-12-22 23:11:26 -0500460 Data result(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700461 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang4c259db2020-10-30 09:36:01 -0700462 result.setContent(payload);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800463 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700464 m_face.put(result);
Davide Pesavento6866b902024-12-22 23:11:26 -0500465 NDN_LOG_TRACE("Sent CHALLENGE response");
466
tylerliu1f480be2020-11-10 13:02:53 -0800467 if (m_statusUpdateCallback) {
468 m_statusUpdateCallback(*requestState);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800469 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700470}
471
Davide Pesavento6866b902024-12-22 23:11:26 -0500472std::unique_ptr<RequestState>
473CaModule::getCertificateRequest(const Interest& request)
474{
475 Name::Component component;
476 try {
477 component = request.getName().at(m_config.caProfile.caPrefix.size() + 2);
478 }
479 catch (const std::exception& e) {
480 NDN_LOG_ERROR("Cannot extract request ID from Interest name: " << e.what());
481 return nullptr;
482 }
483
484 RequestId requestId;
485 if (component.value_size() != requestId.size()) {
486 NDN_LOG_ERROR("Invalid request ID of length " << component.value_size());
487 return nullptr;
488 }
489 std::memcpy(requestId.data(), component.value(), requestId.size());
490
491 try {
492 NDN_LOG_TRACE("Retrieving request with ID " << ndn::toHex(requestId));
493 return std::make_unique<RequestState>(m_storage->getRequest(requestId));
494 }
495 catch (const std::exception& e) {
496 NDN_LOG_ERROR("Cannot fetch certificate request record from storage: " << e.what());
497 return nullptr;
498 }
499}
500
Davide Pesavento0dc02012021-11-23 22:55:03 -0500501Certificate
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700502CaModule::issueCertificate(const RequestState& requestState)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800503{
Davide Pesavento6481e1e2024-12-22 17:44:15 -0500504 ndn::security::MakeCertificateOptions opts;
505 opts.issuerId = Name::Component("NDNCERT");
506 opts.validity = requestState.cert.getValidityPeriod();
507 auto newCert = m_keyChain.makeCertificate(requestState.cert,
508 signingByIdentity(m_config.caProfile.caPrefix), opts);
Davide Pesavento6866b902024-12-22 23:11:26 -0500509 NDN_LOG_TRACE("Certificate issued: " << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800510 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800511}
512
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700513Data
Davide Pesavento6866b902024-12-22 23:11:26 -0500514CaModule::makeErrorPacket(const Name& name, ErrorCode errorCode, std::string_view errorInfo)
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700515{
Davide Pesavento6866b902024-12-22 23:11:26 -0500516 Data result(name);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700517 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Davide Pesavento6866b902024-12-22 23:11:26 -0500518 result.setContent(errortlv::encodeDataContent(errorCode, errorInfo));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800519 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700520 return result;
521}
522
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400523} // namespace ndncert::ca