blob: 9606e24473fc9428e47e355a005dc8f13beb1d1a [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 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;
41constexpr time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_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();
tylerliu4a00aad2020-09-26 02:03:17 -0700269 auto currentTime = time::system_clock::now();
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400270 if (notBefore < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD ||
271 notAfter > currentTime + m_config.caProfile.maxValidityPeriod ||
272 notAfter <= notBefore) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500273 NDN_LOG_ERROR("Invalid validity period requested");
274 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
275 "Invalid validity period requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700276 return;
277 }
tylerliud9239f52020-09-30 17:25:16 -0700278
Davide Pesavento6866b902024-12-22 23:11:26 -0500279 // verify signatures
Davide Pesavento0dc02012021-11-23 22:55:03 -0500280 if (!ndn::security::verifySignature(*clientCert, *clientCert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500281 NDN_LOG_ERROR("Invalid signature in the self-signed certificate");
282 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
283 "Invalid signature in the self-signed certificate."));
tylerliu4a00aad2020-09-26 02:03:17 -0700284 return;
285 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500286 if (!ndn::security::verifySignature(request, *clientCert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500287 NDN_LOG_ERROR("Invalid signature in the Interest packet");
288 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
289 "Invalid signature in the Interest packet."));
tylerliu4a00aad2020-09-26 02:03:17 -0700290 return;
291 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700292 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700293 else if (requestType == RequestType::REVOKE) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500294 // verify cert is from this CA
Davide Pesavento0dc02012021-11-23 22:55:03 -0500295 if (!ndn::security::verifySignature(*clientCert, caCert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500296 NDN_LOG_ERROR("Invalid signature in the certificate to revoke");
297 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
298 "Invalid signature in the certificate to revoke."));
tylerliu4a00aad2020-09-26 02:03:17 -0700299 return;
300 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700301 }
302
Davide Pesavento6866b902024-12-22 23:11:26 -0500303 // derive server's ECDH pub key
304 ECDHState ecdh;
305 std::vector<uint8_t> sharedSecret;
306 try {
307 sharedSecret = ecdh.deriveSecret(ecdhPub);
308 }
309 catch (const std::exception& e) {
310 NDN_LOG_ERROR("Cannot derive a shared secret using the provided ECDH public key: " << e.what());
311 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
312 "Cannot derive a shared secret using the provided ECDH public key."));
313 return;
314 }
315
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700316 // create new request instance
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700317 uint8_t requestIdData[32];
318 Block certNameTlv = clientCert->getName().wireEncode();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700319 try {
Davide Pesaventof48e1632023-04-24 22:00:26 -0400320 hmacSha256(certNameTlv.data(), certNameTlv.size(), m_requestIdGenKey, 32, requestIdData);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700321 }
322 catch (const std::runtime_error& e) {
Davide Pesaventof48e1632023-04-24 22:00:26 -0400323 NDN_LOG_ERROR("Error computing the request ID: " << e.what());
Davide Pesavento6866b902024-12-22 23:11:26 -0500324 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
325 "Error computing the request ID."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700326 return;
327 }
Zhiyi Zhangc9ada1b2020-10-29 19:13:15 -0700328 RequestId id;
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700329 std::memcpy(id.data(), requestIdData, id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800330 // initialize request state
331 RequestState requestState;
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800332 requestState.caPrefix = m_config.caProfile.caPrefix;
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800333 requestState.requestId = id;
334 requestState.requestType = requestType;
335 requestState.cert = *clientCert;
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800336 // generate salt for HKDF
337 std::array<uint8_t, 32> salt;
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400338 ndn::random::generateSecureBytes(salt);
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800339 // hkdf
340 std::array<uint8_t, 16> aesKey;
341 hkdf(sharedSecret.data(), sharedSecret.size(), salt.data(), salt.size(),
342 aesKey.data(), aesKey.size(), id.data(), id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800343 requestState.encryptionKey = aesKey;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700344 try {
345 m_storage->addRequest(requestState);
346 }
Davide Pesavento0dc02012021-11-23 22:55:03 -0500347 catch (const std::runtime_error&) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500348 NDN_LOG_ERROR("Duplicate request ID " << ndn::toHex(id));
349 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER, "Duplicate request ID."));
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700350 return;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700351 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400352
Davide Pesavento6866b902024-12-22 23:11:26 -0500353 Data result(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700354 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800355 result.setContent(requesttlv::encodeDataContent(ecdh.getSelfPubKey(),
356 salt, requestState.requestId,
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800357 m_config.caProfile.supportedChallenges));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800358 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800359 m_face.put(result);
Davide Pesavento6866b902024-12-22 23:11:26 -0500360 NDN_LOG_TRACE("Sent " << requestType << " response");
361
tylerliu1f480be2020-11-10 13:02:53 -0800362 if (m_statusUpdateCallback) {
363 m_statusUpdateCallback(requestState);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800364 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800365}
366
367void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700368CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800369{
Davide Pesavento6866b902024-12-22 23:11:26 -0500370 NDN_LOG_TRACE("Received CHALLENGE request");
371
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700372 // get certificate request state
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700373 auto requestState = getCertificateRequest(request);
Zhiyi Zhang84207c52020-10-28 21:45:44 -0700374 if (requestState == nullptr) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500375 NDN_LOG_ERROR("No pending certificate request found");
376 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
377 "No pending certificate request found."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800378 return;
379 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400380
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700381 // verify signature
Davide Pesavento0dc02012021-11-23 22:55:03 -0500382 if (!ndn::security::verifySignature(request, requestState->cert)) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500383 NDN_LOG_ERROR("Invalid signature in the Interest packet");
384 m_face.put(makeErrorPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
385 "Invalid signature in the Interest packet."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800386 return;
387 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400388
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700389 // decrypt the parameters
Davide Pesavento6866b902024-12-22 23:11:26 -0500390 ndn::Buffer plaintext;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700391 try {
Davide Pesavento6866b902024-12-22 23:11:26 -0500392 plaintext = decodeBlockWithAesGcm128(request.getApplicationParameters(), requestState->encryptionKey.data(),
393 requestState->requestId.data(), requestState->requestId.size(),
394 requestState->decryptionIv, requestState->encryptionIv);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700395 }
396 catch (const std::exception& e) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500397 NDN_LOG_ERROR("Cannot decrypt challenge parameters: " << e.what());
tylerliu7b9185c2020-11-24 12:15:18 -0800398 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500399 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
400 "Malformed challenge parameters."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700401 return;
402 }
Davide Pesavento6866b902024-12-22 23:11:26 -0500403 if (plaintext.empty()) {
404 NDN_LOG_ERROR("Empty parameters after decryption");
tylerliu7b9185c2020-11-24 12:15:18 -0800405 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500406 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
407 "Malformed challenge parameters."));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700408 return;
409 }
Davide Pesavento6f1a2ab2022-03-17 03:57:21 -0400410
Davide Pesavento6866b902024-12-22 23:11:26 -0500411 auto paramTLV = ndn::makeBinaryBlock(tlv::EncryptedPayload, plaintext);
Suyong Won44d0cce2020-05-10 04:07:43 -0700412 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800413
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700414 // load the corresponding challenge module
tylerliu50d679e2020-10-14 14:08:39 -0700415 std::string challengeType = readString(paramTLV.get(tlv::SelectedChallenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800416 auto challenge = ChallengeModule::createChallengeModule(challengeType);
417 if (challenge == nullptr) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500418 NDN_LOG_TRACE("Unsupported challenge type: " << challengeType);
tylerliu7b9185c2020-11-24 12:15:18 -0800419 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500420 m_face.put(makeErrorPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
421 "Unsupported challenge type."));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700422 return;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800423 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700424
Davide Pesavento6866b902024-12-22 23:11:26 -0500425 NDN_LOG_TRACE("Using challenge: " << challengeType);
426 auto [errCode, errMsg] = challenge->handleChallengeRequest(paramTLV, *requestState);
427 if (errCode != ErrorCode::NO_ERROR) {
tylerliu7b9185c2020-11-24 12:15:18 -0800428 m_storage->deleteRequest(requestState->requestId);
Davide Pesavento6866b902024-12-22 23:11:26 -0500429 m_face.put(makeErrorPacket(request.getName(), errCode, errMsg));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700430 return;
431 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700432
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700433 Block payload;
tylerliu7b9185c2020-11-24 12:15:18 -0800434 if (requestState->status == Status::PENDING) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500435 NDN_LOG_TRACE("Challenge succeeded");
436 if (requestState->requestType == RequestType::NEW ||
437 requestState->requestType == RequestType::RENEW) {
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700438 auto issuedCert = issueCertificate(*requestState);
tylerliu7b9185c2020-11-24 12:15:18 -0800439 requestState->cert = issuedCert;
440 requestState->status = Status::SUCCESS;
441 m_storage->deleteRequest(requestState->requestId);
Tianyuan Yu42bc63e2024-10-18 10:18:38 -0700442 payload = challengetlv::encodeDataContent(*requestState, issuedCert.getName(),
443 m_config.caProfile.forwardingHint);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800444 }
tylerliu7b9185c2020-11-24 12:15:18 -0800445 else if (requestState->requestType == RequestType::REVOKE) {
Davide Pesavento6866b902024-12-22 23:11:26 -0500446 // TODO: where is the code to revoke?
tylerliu7b9185c2020-11-24 12:15:18 -0800447 requestState->status = Status::SUCCESS;
448 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800449 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700450 }
451 }
452 else {
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800453 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800454 m_storage->updateRequest(*requestState);
Davide Pesavento6866b902024-12-22 23:11:26 -0500455 NDN_LOG_TRACE("Challenge continues");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800456 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700457
Davide Pesavento6866b902024-12-22 23:11:26 -0500458 Data result(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700459 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang4c259db2020-10-30 09:36:01 -0700460 result.setContent(payload);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800461 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700462 m_face.put(result);
Davide Pesavento6866b902024-12-22 23:11:26 -0500463 NDN_LOG_TRACE("Sent CHALLENGE response");
464
tylerliu1f480be2020-11-10 13:02:53 -0800465 if (m_statusUpdateCallback) {
466 m_statusUpdateCallback(*requestState);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800467 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700468}
469
Davide Pesavento6866b902024-12-22 23:11:26 -0500470std::unique_ptr<RequestState>
471CaModule::getCertificateRequest(const Interest& request)
472{
473 Name::Component component;
474 try {
475 component = request.getName().at(m_config.caProfile.caPrefix.size() + 2);
476 }
477 catch (const std::exception& e) {
478 NDN_LOG_ERROR("Cannot extract request ID from Interest name: " << e.what());
479 return nullptr;
480 }
481
482 RequestId requestId;
483 if (component.value_size() != requestId.size()) {
484 NDN_LOG_ERROR("Invalid request ID of length " << component.value_size());
485 return nullptr;
486 }
487 std::memcpy(requestId.data(), component.value(), requestId.size());
488
489 try {
490 NDN_LOG_TRACE("Retrieving request with ID " << ndn::toHex(requestId));
491 return std::make_unique<RequestState>(m_storage->getRequest(requestId));
492 }
493 catch (const std::exception& e) {
494 NDN_LOG_ERROR("Cannot fetch certificate request record from storage: " << e.what());
495 return nullptr;
496 }
497}
498
Davide Pesavento0dc02012021-11-23 22:55:03 -0500499Certificate
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700500CaModule::issueCertificate(const RequestState& requestState)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800501{
Davide Pesavento6481e1e2024-12-22 17:44:15 -0500502 ndn::security::MakeCertificateOptions opts;
503 opts.issuerId = Name::Component("NDNCERT");
504 opts.validity = requestState.cert.getValidityPeriod();
505 auto newCert = m_keyChain.makeCertificate(requestState.cert,
506 signingByIdentity(m_config.caProfile.caPrefix), opts);
Davide Pesavento6866b902024-12-22 23:11:26 -0500507 NDN_LOG_TRACE("Certificate issued: " << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800508 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800509}
510
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700511Data
Davide Pesavento6866b902024-12-22 23:11:26 -0500512CaModule::makeErrorPacket(const Name& name, ErrorCode errorCode, std::string_view errorInfo)
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700513{
Davide Pesavento6866b902024-12-22 23:11:26 -0500514 Data result(name);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700515 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Davide Pesavento6866b902024-12-22 23:11:26 -0500516 result.setContent(errortlv::encodeDataContent(errorCode, errorInfo));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800517 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700518 return result;
519}
520
Davide Pesavento0d1d11c2022-04-11 22:11:34 -0400521} // namespace ndncert::ca