blob: cd28b94b4463e294981582c551c8dfd74c9ede1e [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/*
3 * Copyright (c) 2017-2021, 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>
35#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
38namespace ndn {
39namespace ndncert {
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -070040namespace ca {
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080041
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -070042static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -070043static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070044
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070045NDN_LOG_INIT(ndncert.ca);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080046
tylerliua7bea662020-10-08 18:51:02 -070047CaModule::CaModule(Face& face, security::KeyChain& keyChain,
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080048 const std::string& configPath, const std::string& storageType)
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080049 : m_face(face)
50 , m_keyChain(keyChain)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080051{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070052 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080053 m_config.load(configPath);
Zhiyi Zhang44c6a352020-12-14 10:57:17 -080054 m_storage = CaStorage::createCaStorage(storageType, m_config.caProfile.caPrefix, "");
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -070055 random::generateSecureBytes(m_requestIdGenKey, 32);
tylerliuf2e6bb52020-12-13 13:23:05 -080056 if (m_config.nameAssignmentFuncs.size() == 0) {
57 m_config.nameAssignmentFuncs.push_back(NameAssignmentFunc::createNameAssignmentFunc("random"));
Zhiyi Zhang8683ec92020-10-07 18:18:35 -070058 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080059 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080060}
61
62CaModule::~CaModule()
63{
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070064 for (auto& handle : m_interestFilterHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080066 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070067 for (auto& handle : m_registeredPrefixHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070068 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080069 }
70}
71
72void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080073CaModule::registerPrefix()
74{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 // register prefixes
Zhiyi Zhang44c6a352020-12-14 10:57:17 -080076 Name prefix = m_config.caProfile.caPrefix;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070077 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080078
Zhiyi Zhangfbcab842020-10-07 15:17:13 -070079 auto prefixId = m_face.registerPrefix(
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080080 prefix,
Davide Pesaventoe5b43692021-11-15 22:05:03 -050081 [&] (const Name& name) {
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080082 // register INFO RDR metadata prefix
83 name::Component metaDataComp(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata"));
84 auto filterId = m_face.setInterestFilter(Name(name).append("INFO").append(metaDataComp),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050085 [this] (auto&&, const auto& i) { onCaProfileDiscovery(i); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080086 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhang696cd042020-10-07 21:27:36 -070087
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080088 // register PROBE prefix
89 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050090 [this] (auto&&, const auto& i) { onProbe(i); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080091 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070092
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080093 // register NEW prefix
94 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -050095 [this] (auto&&, const auto& i) { onNewRenewRevoke(i, RequestType::NEW); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080096 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070097
Zhiyi Zhang1b101b32021-02-17 14:36:03 -080098 // register SELECT prefix
99 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500100 [this] (auto&&, const auto& i) { onChallenge(i); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800101 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700102
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800103 // register REVOKE prefix
104 filterId = m_face.setInterestFilter(Name(name).append("REVOKE"),
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500105 [this] (auto&&, const auto& i) { onNewRenewRevoke(i, RequestType::REVOKE); });
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800106 m_interestFilterHandles.push_back(filterId);
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500107
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800108 NDN_LOG_TRACE("Prefix " << name << " got registered");
109 },
Davide Pesaventoe5b43692021-11-15 22:05:03 -0500110 [this] (auto&&, const auto& reason) { onRegisterFailed(reason); });
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800112}
113
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700114void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800116{
tylerliu1f480be2020-11-10 13:02:53 -0800117 m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800118}
119
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700120Data
121CaModule::getCaProfileData()
swa77020643ac2020-03-26 02:24:45 -0700122{
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700123 if (m_profileData == nullptr) {
124 const auto& pib = m_keyChain.getPib();
Zhiyi Zhangcac27f12021-02-01 13:48:14 -0800125 const auto& identity = pib.getIdentity(m_config.caProfile.caPrefix);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700126 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
tylerliuf2e6bb52020-12-13 13:23:05 -0800127 Block contentTLV = infotlv::encodeDataContent(m_config.caProfile, cert);
swa77020643ac2020-03-26 02:24:45 -0700128
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800129 // set naming convention to be typed
130 auto convention = name::getConventionEncoding();
131 name::setConventionEncoding(name::Convention::TYPED);
132
133 Name infoPacketName(m_config.caProfile.caPrefix);
134 auto segmentComp = name::Component::fromSegment(0);
135 infoPacketName.append("CA").append("INFO").appendVersion().append(segmentComp);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700136 m_profileData = std::make_unique<Data>(infoPacketName);
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800137 m_profileData->setFinalBlock(segmentComp);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700138 m_profileData->setContent(contentTLV);
139 m_profileData->setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800140 m_keyChain.sign(*m_profileData, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhang615b6b72021-01-11 14:25:32 -0800141
142 // set back the convention
143 name::setConventionEncoding(convention);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700144 }
Zhiyi Zhang696cd042020-10-07 21:27:36 -0700145 return *m_profileData;
146}
147
148void
149CaModule::onCaProfileDiscovery(const Interest& request)
150{
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 }
155 MetadataObject metadata;
156 metadata.setVersionedName(m_profileData->getName().getPrefix(-1));
157 Name discoveryInterestName(m_profileData->getName().getPrefix(-2));
158 name::Component metadataComponent(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata"));
159 discoveryInterestName.append(metadataComponent);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800160 m_face.put(metadata.makeData(discoveryInterestName, m_keyChain, signingByIdentity(m_config.caProfile.caPrefix)));
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700161}
swa77020643ac2020-03-26 02:24:45 -0700162
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700163void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700164CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800165{
swa77020643ac2020-03-26 02:24:45 -0700166 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700167 NDN_LOG_TRACE("Received PROBE request");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800168
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700169 // process PROBE requests: collect probe parameters
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800170 auto parameters = probetlv::decodeApplicationParameters(request.getApplicationParameters());
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800171 std::vector <PartialName> availableComponents;
tylerliuf2e6bb52020-12-13 13:23:05 -0800172 for (auto& item : m_config.nameAssignmentFuncs) {
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700173 auto names = item->assignName(parameters);
174 availableComponents.insert(availableComponents.end(), names.begin(), names.end());
175 }
Zhiyi Zhangcb8acf22020-10-07 19:35:16 -0700176 if (availableComponents.size() == 0) {
Zhiyi Zhang8683ec92020-10-07 18:18:35 -0700177 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhangcb8acf22020-10-07 19:35:16 -0700178 "Cannot generate available names from parameters provided."));
Zhiyi Zhang8683ec92020-10-07 18:18:35 -0700179 return;
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700180 }
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800181 std::vector <Name> availableNames;
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700182 for (const auto& component : availableComponents) {
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800183 Name newIdentityName = m_config.caProfile.caPrefix;
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700184 newIdentityName.append(component);
185 availableNames.push_back(newIdentityName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700186 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700187
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700188 Data result;
189 result.setName(request.getName());
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800190 result.setContent(
191 probetlv::encodeDataContent(availableNames, m_config.caProfile.maxSuffixLength, m_config.redirection));
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700192 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800193 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhang3e8ca252020-09-30 17:18:38 -0700194 m_face.put(result);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700195 NDN_LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800196}
197
198void
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700199CaModule::onNewRenewRevoke(const Interest& request, RequestType requestType)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800200{
tylerliuf62cfeb2021-01-28 11:01:33 -0800201
202 //verify ca cert validity
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800203 const auto& caCert = m_keyChain.getPib()
204 .getIdentity(m_config.caProfile.caPrefix)
205 .getDefaultKey()
206 .getDefaultCertificate();
tylerliuf62cfeb2021-01-28 11:01:33 -0800207 if (!caCert.isValid()) {
208 NDN_LOG_ERROR("Server certificate invalid/expired");
209 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
210 "Server certificate invalid/expired"));
211 return;
212 }
213
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700214 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700215 // REVOKE Naming Convention: /<CA-prefix>/CA/REVOKE/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700216 // get ECDH pub key and cert request
Suyong Won19fba4d2020-05-09 13:39:46 -0700217 const auto& parameterTLV = request.getApplicationParameters();
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800218 std::vector <uint8_t> ecdhPub;
219 shared_ptr <security::Certificate> clientCert;
tylerliu0790bdb2020-10-02 00:50:51 -0700220 try {
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800221 requesttlv::decodeApplicationParameters(parameterTLV, requestType, ecdhPub, clientCert);
Zhiyi Zhangcd57da82020-10-08 20:35:40 -0700222 }
223 catch (const std::exception& e) {
tylerliu0790bdb2020-10-02 00:50:51 -0700224 if (!parameterTLV.hasValue()) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700225 NDN_LOG_ERROR("Empty TLV obtained from the Interest parameter.");
tylerliu0790bdb2020-10-02 00:50:51 -0700226 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
227 "Empty TLV obtained from the Interest parameter."));
228 return;
229 }
Suyong Won7968f7a2020-05-12 01:01:25 -0700230
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700231 NDN_LOG_ERROR("Unrecognized self-signed certificate: " << e.what());
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700232 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
tylerliu0790bdb2020-10-02 00:50:51 -0700233 "Unrecognized self-signed certificate."));
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700234 return;
235 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700236
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700237 if (ecdhPub.empty()) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700238 NDN_LOG_ERROR("Empty ECDH PUB obtained from the Interest parameter.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700239 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
240 "Empty ECDH PUB obtained from the Interest parameter."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700241 return;
242 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700243
244 // get server's ECDH pub key
tylerliu8e170d62020-09-30 01:31:53 -0700245 ECDHState ecdh;
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800246 std::vector <uint8_t> sharedSecret;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700247 try {
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700248 sharedSecret = ecdh.deriveSecret(ecdhPub);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700249 }
250 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700251 NDN_LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700252 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
253 "Cannot derive a shared secret using the provided ECDH key."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700254 return;
255 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700256
tylerliud9239f52020-09-30 17:25:16 -0700257 // verify identity name
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800258 if (!m_config.caProfile.caPrefix.isPrefixOf(clientCert->getIdentity())
tylerliua7bea662020-10-08 18:51:02 -0700259 || !security::Certificate::isValidName(clientCert->getName())
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800260 || clientCert->getIdentity().size() <= m_config.caProfile.caPrefix.size()) {
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800261 NDN_LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
262 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
263 "An invalid certificate name is being requested."));
264 return;
tylerliud9239f52020-09-30 17:25:16 -0700265 }
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800266 if (m_config.caProfile.maxSuffixLength) {
267 if (clientCert->getIdentity().size() > m_config.caProfile.caPrefix.size() + *m_config.caProfile.maxSuffixLength) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700268 NDN_LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
tylerliud9239f52020-09-30 17:25:16 -0700269 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
270 "An invalid certificate name is being requested."));
271 return;
272 }
273 }
274
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700275 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700276 // check the validity period
277 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
278 auto currentTime = time::system_clock::now();
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700279 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD ||
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800280 expectedPeriod.second > currentTime + m_config.caProfile.maxValidityPeriod ||
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700281 expectedPeriod.second <= expectedPeriod.first) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700282 NDN_LOG_ERROR("An invalid validity period is being requested.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700283 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
284 "An invalid validity period is being requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700285 return;
286 }
tylerliud9239f52020-09-30 17:25:16 -0700287
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700288 // verify signature
tylerliu4a00aad2020-09-26 02:03:17 -0700289 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700290 NDN_LOG_ERROR("Invalid signature in the self-signed certificate.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700291 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
292 "Invalid signature in the self-signed certificate."));
tylerliu4a00aad2020-09-26 02:03:17 -0700293 return;
294 }
295 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700296 NDN_LOG_ERROR("Invalid signature in the Interest packet.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700297 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
298 "Invalid signature in the Interest packet."));
tylerliu4a00aad2020-09-26 02:03:17 -0700299 return;
300 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700301 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700302 else if (requestType == RequestType::REVOKE) {
tylerliud9239f52020-09-30 17:25:16 -0700303 //verify cert is from this CA
tylerliuf62cfeb2021-01-28 11:01:33 -0800304 if (!security::verifySignature(*clientCert, caCert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700305 NDN_LOG_ERROR("Invalid signature in the certificate to revoke.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700306 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
307 "Invalid signature in the certificate to revoke."));
tylerliu4a00aad2020-09-26 02:03:17 -0700308 return;
309 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700310 }
311
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700312 // create new request instance
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700313 uint8_t requestIdData[32];
314 Block certNameTlv = clientCert->getName().wireEncode();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700315 try {
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700316 hmacSha256(certNameTlv.wire(), certNameTlv.size(), m_requestIdGenKey, 32, requestIdData);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700317 }
318 catch (const std::runtime_error& e) {
319 NDN_LOG_ERROR("Error computing the request ID: " << std::string(e.what()));
320 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
321 "Error computing the request ID."));
322 return;
323 }
Zhiyi Zhangc9ada1b2020-10-29 19:13:15 -0700324 RequestId id;
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700325 std::memcpy(id.data(), requestIdData, id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800326 // initialize request state
327 RequestState requestState;
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800328 requestState.caPrefix = m_config.caProfile.caPrefix;
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800329 requestState.requestId = id;
330 requestState.requestType = requestType;
331 requestState.cert = *clientCert;
Zhiyi Zhang6499edd2021-02-17 22:37:21 -0800332 // generate salt for HKDF
333 std::array<uint8_t, 32> salt;
334 random::generateSecureBytes(salt.data(), salt.size());
335 // hkdf
336 std::array<uint8_t, 16> aesKey;
337 hkdf(sharedSecret.data(), sharedSecret.size(), salt.data(), salt.size(),
338 aesKey.data(), aesKey.size(), id.data(), id.size());
Zhiyi Zhang1f5e86e2020-12-04 15:07:57 -0800339 requestState.encryptionKey = aesKey;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700340 try {
341 m_storage->addRequest(requestState);
342 }
343 catch (const std::runtime_error& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700344 NDN_LOG_ERROR("Duplicate Request ID: The same request has been seen before.");
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700345 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700346 "Duplicate Request ID: The same request has been seen before."));
Zhiyi Zhanga2c39f72020-10-06 16:45:55 -0700347 return;
Zhiyi Zhang21d52b52020-10-05 18:29:15 -0700348 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800349 Data result;
350 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700351 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800352 result.setContent(requesttlv::encodeDataContent(ecdh.getSelfPubKey(),
353 salt, requestState.requestId,
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800354 m_config.caProfile.supportedChallenges));
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800355 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800356 m_face.put(result);
tylerliu1f480be2020-11-10 13:02:53 -0800357 if (m_statusUpdateCallback) {
358 m_statusUpdateCallback(requestState);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800359 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800360}
361
362void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700363CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800364{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700365 // get certificate request state
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700366 auto requestState = getCertificateRequest(request);
Zhiyi Zhang84207c52020-10-28 21:45:44 -0700367 if (requestState == nullptr) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700368 NDN_LOG_ERROR("No certificate request state can be found.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700369 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700370 "No certificate request state can be found."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800371 return;
372 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700373 // verify signature
tylerliu7b9185c2020-11-24 12:15:18 -0800374 if (!security::verifySignature(request, requestState->cert)) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700375 NDN_LOG_ERROR("Invalid Signature in the Interest packet.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700376 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
377 "Invalid Signature in the Interest packet."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800378 return;
379 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700380 // decrypt the parameters
Suyong Won19fba4d2020-05-09 13:39:46 -0700381 Buffer paramTLVPayload;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700382 try {
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800383 paramTLVPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), requestState->encryptionKey.data(),
384 requestState->requestId.data(), requestState->requestId.size(),
385 requestState->decryptionIv, requestState->encryptionIv);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700386 }
387 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700388 NDN_LOG_ERROR("Interest paramaters decryption failed: " << e.what());
tylerliu7b9185c2020-11-24 12:15:18 -0800389 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700390 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700391 "Interest paramaters decryption failed."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700392 return;
393 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700394 if (paramTLVPayload.size() == 0) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700395 NDN_LOG_ERROR("No parameters are found after decryption.");
tylerliu7b9185c2020-11-24 12:15:18 -0800396 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700397 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700398 "No parameters are found after decryption."));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700399 return;
400 }
tylerliu50d679e2020-10-14 14:08:39 -0700401 Block paramTLV = makeBinaryBlock(tlv::EncryptedPayload, paramTLVPayload.data(), paramTLVPayload.size());
Suyong Won44d0cce2020-05-10 04:07:43 -0700402 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800403
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700404 // load the corresponding challenge module
tylerliu50d679e2020-10-14 14:08:39 -0700405 std::string challengeType = readString(paramTLV.get(tlv::SelectedChallenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800406 auto challenge = ChallengeModule::createChallengeModule(challengeType);
407 if (challenge == nullptr) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700408 NDN_LOG_TRACE("Unrecognized challenge type: " << challengeType);
tylerliu7b9185c2020-11-24 12:15:18 -0800409 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800410 m_face.put(
411 generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER, "Unrecognized challenge type."));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700412 return;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800413 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700414
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700415 NDN_LOG_TRACE("CHALLENGE module to be load: " << challengeType);
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700416 auto errorInfo = challenge->handleChallengeRequest(paramTLV, *requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700417 if (std::get<0>(errorInfo) != ErrorCode::NO_ERROR) {
tylerliu7b9185c2020-11-24 12:15:18 -0800418 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700419 m_face.put(generateErrorDataPacket(request.getName(), std::get<0>(errorInfo), std::get<1>(errorInfo)));
420 return;
421 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700422
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700423 Block payload;
tylerliu7b9185c2020-11-24 12:15:18 -0800424 if (requestState->status == Status::PENDING) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700425 // if challenge succeeded
tylerliud82cc5c2020-12-21 23:58:39 -0800426 if (requestState->requestType == RequestType::NEW || requestState->requestType == RequestType::RENEW) {
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700427 auto issuedCert = issueCertificate(*requestState);
tylerliu7b9185c2020-11-24 12:15:18 -0800428 requestState->cert = issuedCert;
429 requestState->status = Status::SUCCESS;
430 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700431
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800432 payload = challengetlv::encodeDataContent(*requestState, issuedCert.getName());
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700433 NDN_LOG_TRACE("Challenge succeeded. Certificate has been issued: " << issuedCert.getName());
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800434 }
tylerliu7b9185c2020-11-24 12:15:18 -0800435 else if (requestState->requestType == RequestType::REVOKE) {
436 requestState->status = Status::SUCCESS;
437 m_storage->deleteRequest(requestState->requestId);
Zhiyi Zhangf9d94eb2020-12-21 15:39:38 -0800438 // TODO: where is the code to revoke?
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800439 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700440 NDN_LOG_TRACE("Challenge succeeded. Certificate has been revoked");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700441 }
442 }
443 else {
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800444 payload = challengetlv::encodeDataContent(*requestState);
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800445 m_storage->updateRequest(*requestState);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700446 NDN_LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800447 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700448
449 Data result;
450 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700451 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang4c259db2020-10-30 09:36:01 -0700452 result.setContent(payload);
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800453 m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700454 m_face.put(result);
tylerliu1f480be2020-11-10 13:02:53 -0800455 if (m_statusUpdateCallback) {
456 m_statusUpdateCallback(*requestState);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800457 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700458}
459
tylerliua7bea662020-10-08 18:51:02 -0700460security::Certificate
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700461CaModule::issueCertificate(const RequestState& requestState)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800462{
tylerliu7b9185c2020-11-24 12:15:18 -0800463 auto expectedPeriod = requestState.cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700464 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
tylerliua7bea662020-10-08 18:51:02 -0700465 security::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700466
tylerliu7b9185c2020-11-24 12:15:18 -0800467 Name certName = requestState.cert.getKeyName();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700468 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800469 newCert.setName(certName);
tylerliu7b9185c2020-11-24 12:15:18 -0800470 newCert.setContent(requestState.cert.getContent());
471 NDN_LOG_TRACE("cert request content " << requestState.cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800472 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800473 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700474 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800475 m_config.caProfile.caPrefix, signatureInfo);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800476
477 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700478 NDN_LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800479 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800480}
481
Zhiyi Zhang1b101b32021-02-17 14:36:03 -0800482std::unique_ptr <RequestState>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700483CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800484{
Zhiyi Zhangc9ada1b2020-10-29 19:13:15 -0700485 RequestId requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800486 try {
Zhiyi Zhang44c6a352020-12-14 10:57:17 -0800487 auto& component = request.getName().at(m_config.caProfile.caPrefix.size() + 2);
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700488 std::memcpy(requestId.data(), component.value(), component.value_size());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700489 }
490 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700491 NDN_LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700492 return nullptr;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700493 }
494 try {
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700495 NDN_LOG_TRACE("Request Id to query the database " << toHex(requestId.data(), requestId.size()));
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700496 return std::make_unique<RequestState>(m_storage->getRequest(requestId));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800497 }
498 catch (const std::exception& e) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700499 NDN_LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -0700500 return nullptr;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800501 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800502}
503
504void
505CaModule::onRegisterFailed(const std::string& reason)
506{
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700507 NDN_LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800508}
509
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700510Data
511CaModule::generateErrorDataPacket(const Name& name, ErrorCode error, const std::string& errorInfo)
512{
513 Data result;
514 result.setName(name);
515 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangf22ae242020-11-17 10:51:15 -0800516 result.setContent(errortlv::encodeDataContent(error, 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
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700521} // namespace ca
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700522} // namespace ndncert
523} // namespace ndn