blob: 8b1c72a23807dda91801932c04e2132de3aec8d2 [file] [log] [blame]
Zhiyi Zhangf5246c42017-01-26 09:39:20 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
swa770de007bc2020-03-24 21:26:21 -07002/**
Davide Pesaventob48bbda2020-07-27 19:41:37 -04003 * Copyright (c) 2017-2020, Regents of the University of California.
Zhiyi Zhangf5246c42017-01-26 09:39:20 -08004 *
5 * This file is part of ndncert, a certificate management system based on NDN.
6 *
7 * ndncert is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License along with
16 * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ndncert authors and contributors.
19 */
20
21#include "ca-module.hpp"
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070022#include "challenge-module.hpp"
23#include "crypto-support/enc-tlv.hpp"
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070024#include "protocol-detail/challenge.hpp"
25#include "protocol-detail/error.hpp"
26#include "protocol-detail/info.hpp"
27#include "protocol-detail/new.hpp"
28#include "protocol-detail/probe.hpp"
29#include "protocol-detail/revoke.hpp"
Zhiyi Zhang01e91a32020-09-29 12:10:00 -070030#include <ndn-cxx/metadata-object.hpp>
31#include <ndn-cxx/security/signing-helpers.hpp>
32#include <ndn-cxx/security/verification-helpers.hpp>
33#include <ndn-cxx/util/io.hpp>
34#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080035
36namespace ndn {
37namespace ndncert {
38
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -070039static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -070040static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070041
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080042_LOG_INIT(ndncert.ca);
43
44CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
45 const std::string& configPath, const std::string& storageType)
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070046 : m_face(face)
47 , m_keyChain(keyChain)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080048{
Zhiyi Zhanga63b7372017-05-17 14:14:34 -070049 // load the config and create storage
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080050 m_config.load(configPath);
51 m_storage = CaStorage::createCaStorage(storageType);
52
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080053 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080054}
55
56CaModule::~CaModule()
57{
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070058 for (auto& handle : m_interestFilterHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070059 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080060 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -070061 for (auto& handle : m_registeredPrefixHandles) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080063 }
64}
65
66void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080067CaModule::registerPrefix()
68{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070069 // register localhop discovery prefix
swa77020643ac2020-03-26 02:24:45 -070070 Name localhopInfoPrefix("/localhop/CA/INFO");
71 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopInfoPrefix),
72 bind(&CaModule::onInfo, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080073 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070074 m_registeredPrefixHandles.push_back(prefixId);
swa77020643ac2020-03-26 02:24:45 -070075 _LOG_TRACE("Prefix " << localhopInfoPrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080076
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070077 // register prefixes
Zhiyi Zhang9829da92020-09-30 16:19:34 -070078 Name prefix = m_config.m_caItem.m_caPrefix;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070079 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080080
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070081 prefixId = m_face.registerPrefix(
82 prefix,
83 [&](const Name& name) {
84 // register INFO prefix
85 auto filterId = m_face.setInterestFilter(Name(name).append("INFO"),
86 bind(&CaModule::onInfo, this, _2));
87 m_interestFilterHandles.push_back(filterId);
swa77020643ac2020-03-26 02:24:45 -070088
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070089 // register PROBE prefix
90 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
91 bind(&CaModule::onProbe, this, _2));
92 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070093
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070094 // register NEW prefix
95 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Zhiyi Zhang01e91a32020-09-29 12:10:00 -070096 bind(&CaModule::onNewRenewRevoke, this, _2, RequestType::NEW));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070097 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070098
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070099 // register SELECT prefix
100 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
101 bind(&CaModule::onChallenge, this, _2));
102 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700103
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700104 // register REVOKE prefix
105 filterId = m_face.setInterestFilter(Name(name).append("REVOKE"),
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700106 bind(&CaModule::onNewRenewRevoke, this, _2, RequestType::REVOKE));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700107 m_interestFilterHandles.push_back(filterId);
108 _LOG_TRACE("Prefix " << name << " got registered");
109 },
110 bind(&CaModule::onRegisterFailed, this, _2));
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
115CaModule::setNameAssignmentFunction(const NameAssignmentFunc& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800116{
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700117 m_config.m_nameAssignmentFunc = handler;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800118}
119
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700120void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700121CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800122{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700123 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800124}
125
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700126shared_ptr<Data>
127CaModule::generateCaConfigMetaData()
swa77020643ac2020-03-26 02:24:45 -0700128{
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700129 // @TODO
130 // make metadata a class member variable m_infoMetadata
131 // check whether the m_infoMetadata has the latest versioned name, if not, then generate a new one
132 // otherwise, directly reply m_infoMetadata.makeData
133
134 auto infoPacket = generateCaConfigData();
135 MetadataObject metadata;
136 metadata.setVersionedName(infoPacket->getName().getPrefix(-1));
137 Name discoveryInterestName(infoPacket->getName().getPrefix(-2));
138 name::Component metadataComponent(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata"));
139 discoveryInterestName.append(metadataComponent);
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700140 auto metadataData = metadata.makeData(discoveryInterestName, m_keyChain, signingByIdentity(m_config.m_caItem.m_caPrefix));
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700141 return make_shared<Data>(metadataData);
142}
143
144shared_ptr<Data>
145CaModule::generateCaConfigData()
146{
147 // @TODO
148 // make CaInfo Data packet a class member variable m_infoData
149 // check whether the m_infoData is still valid, if not, then generate a new one
150 // otherwise, directly reply m_infoData
Suyong Won19fba4d2020-05-09 13:39:46 -0700151
152 const auto& pib = m_keyChain.getPib();
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700153 const auto& identity = pib.getIdentity(m_config.m_caItem.m_caPrefix);
Suyong Won19fba4d2020-05-09 13:39:46 -0700154 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700155 Block contentTLV = INFO::encodeDataContent(m_config.m_caItem, cert);
swa77020643ac2020-03-26 02:24:45 -0700156
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700157 Name infoPacketName(m_config.m_caItem.m_caPrefix);
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700158 infoPacketName.append("CA").append("INFO").appendVersion().appendSegment(0);
159 Data infoData(infoPacketName);
160 infoData.setContent(contentTLV);
161 infoData.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700162 m_keyChain.sign(infoData, signingByIdentity(m_config.m_caItem.m_caPrefix));
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700163 return make_shared<Data>(infoData);
164}
swa77020643ac2020-03-26 02:24:45 -0700165
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700166void
167CaModule::onInfo(const Interest& request)
168{
169 _LOG_TRACE("Received INFO request");
170
171 if (request.getName().get(-1).type() == 32) {
172 m_face.put(*generateCaConfigMetaData());
173 }
174 else {
175 m_face.put(*generateCaConfigData());
176 }
swa77020643ac2020-03-26 02:24:45 -0700177
178 _LOG_TRACE("Handle INFO: send out the INFO response");
179}
180
181void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700182CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800183{
swa77020643ac2020-03-26 02:24:45 -0700184 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
185 _LOG_TRACE("Received PROBE request");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800186
swa77020643ac2020-03-26 02:24:45 -0700187 // process PROBE requests: find an available name
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700188 std::string availableId = "";
Suyong Won19fba4d2020-05-09 13:39:46 -0700189 const auto& parameterTLV = request.getApplicationParameters();
Suyong Won44d0cce2020-05-10 04:07:43 -0700190 parameterTLV.parse();
Suyong Won19fba4d2020-05-09 13:39:46 -0700191 if (!parameterTLV.hasValue()) {
192 _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
swa77020643ac2020-03-26 02:24:45 -0700193 return;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700194 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700195
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700196 // if (m_config.m_caItem.m_nameAssignmentFunc) {
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700197 // try {
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700198 // availableId = m_config.m_caItem.m_nameAssignmentFunc(parameterTLV);
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700199 // }
200 // catch (const std::exception& e) {
201 // _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
202 // return;
203 // }
204 // }
205 // else {
206 // // if there is no app-specified name lookup, use a random name id
207 // availableId = std::to_string(random::generateSecureWord64());
208 // }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700209 // Name newIdentityName = m_config.m_caItem.m_caPrefix;
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700210 // newIdentityName.append(availableId);
211 // _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800212
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700213 // Block contentTLV = PROBE::encodeDataContent(newIdentityName.toUri(), m_config.m_caItem.m_probe, parameterTLV);
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700214
215 // Data result;
216 // result.setName(request.getName());
217 // result.setContent(contentTLV);
218 // result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700219 // m_keyChain.sign(result, signingByIdentity(m_config.m_caItem.m_caPrefix));
Zhiyi Zhangf6c5d272020-09-28 10:17:32 -0700220 // m_face.put(result);
221 // _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800222}
223
224void
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700225CaModule::onNewRenewRevoke(const Interest& request, RequestType requestType)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800226{
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700227 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700228 // REVOKE Naming Convention: /<CA-prefix>/CA/REVOKE/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700229 // get ECDH pub key and cert request
Suyong Won19fba4d2020-05-09 13:39:46 -0700230 const auto& parameterTLV = request.getApplicationParameters();
Suyong Won7968f7a2020-05-12 01:01:25 -0700231 parameterTLV.parse();
232
Suyong Won19fba4d2020-05-09 13:39:46 -0700233 if (!parameterTLV.hasValue()) {
234 _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700235 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
236 "Empty TLV obtained from the Interest parameter."));
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700237 return;
238 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700239
240 std::string peerKeyBase64 = readString(parameterTLV.get(tlv_ecdh_pub));
241
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700242 if (peerKeyBase64 == "") {
Suyong Won19fba4d2020-05-09 13:39:46 -0700243 _LOG_ERROR("Empty ECDH PUB obtained from the Interest parameter.");
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700244 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
245 "Empty ECDH PUB obtained from the Interest parameter."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700246 return;
247 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700248
249 // get server's ECDH pub key
tylerliu8e170d62020-09-30 01:31:53 -0700250 ECDHState ecdh;
251 auto myEcdhPubKeyBase64 = ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700252 try {
tylerliu8e170d62020-09-30 01:31:53 -0700253 ecdh.deriveSecret(peerKeyBase64);
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700254 }
255 catch (const std::exception& e) {
256 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700257 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
258 "Cannot derive a shared secret using the provided ECDH key."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700259 return;
260 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700261 // generate salt for HKDF
262 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700263 // hkdf
tylerliu8e170d62020-09-30 01:31:53 -0700264 uint8_t aesKey[AES_128_KEY_LEN];
265 hkdf(ecdh.context->sharedSecret, ecdh.context->sharedSecretLen,
266 (uint8_t*)&saltInt, sizeof(saltInt), aesKey, sizeof(aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700267
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700268 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700269 // parse certificate request
Zhiyi Zhangb940aa12020-09-30 16:38:57 -0700270 Block requestPayload;
271 if (requestType == RequestType::NEW) {
272 requestPayload = parameterTLV.get(tlv_cert_request);
273 }
274 else if (requestType == RequestType::REVOKE) {
275 requestPayload = parameterTLV.get(tlv_cert_to_revoke);
276 }
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700277 requestPayload.parse();
278 try {
279 security::v2::Certificate cert = security::v2::Certificate(requestPayload.get(tlv::Data));
280 clientCert = make_shared<security::v2::Certificate>(cert);
281 }
282 catch (const std::exception& e) {
283 _LOG_ERROR("Unrecognized self-signed certificate: " << e.what());
284 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
285 "Unrecognized self-signed certificate."));
286 return;
287 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700288 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700289 // check the validity period
290 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
291 auto currentTime = time::system_clock::now();
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700292 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD ||
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700293 expectedPeriod.second > currentTime + m_config.m_caItem.m_maxValidityPeriod ||
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700294 expectedPeriod.second <= expectedPeriod.first) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700295 _LOG_ERROR("An invalid validity period is being requested.");
296 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_VALIDITY_PERIOD,
297 "An invalid validity period is being requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700298 return;
299 }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700300 // verify identity name
301 if (!m_config.m_caItem.m_caPrefix.isPrefixOf(clientCert->getIdentity())
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700302 || !security::v2::Certificate::isValidName(clientCert->getName())
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700303 || clientCert->getIdentity().size() <= m_config.m_caItem.m_caPrefix.size()) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700304 _LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
305 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
306 "An invalid certificate name is being requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700307 return;
308 }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700309 if (m_config.m_caItem.m_maxSuffixLength) {
310 if (clientCert->getIdentity().size() > m_config.m_caItem.m_caPrefix.size() + *m_config.m_caItem.m_maxSuffixLength) {
311 _LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
312 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
313 "An invalid certificate name is being requested."));
314 return;
315 }
316 }
317 // verify signature
tylerliu4a00aad2020-09-26 02:03:17 -0700318 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700319 _LOG_ERROR("Invalid signature in the self-signed certificate.");
320 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
321 "Invalid signature in the self-signed certificate."));
tylerliu4a00aad2020-09-26 02:03:17 -0700322 return;
323 }
324 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700325 _LOG_ERROR("Invalid signature in the Interest packet.");
326 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
327 "Invalid signature in the Interest packet."));
tylerliu4a00aad2020-09-26 02:03:17 -0700328 return;
329 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700330 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700331 else if (requestType == RequestType::REVOKE) {
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700332 // verify identity name
333 if (!m_config.m_caItem.m_caPrefix.isPrefixOf(clientCert->getIdentity())
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700334 || !security::v2::Certificate::isValidName(clientCert->getName())
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700335 || clientCert->getIdentity().size() <= m_config.m_caItem.m_caPrefix.size()) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700336 _LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
337 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
338 "An invalid certificate name is being requested."));
tylerliu4a00aad2020-09-26 02:03:17 -0700339 return;
340 }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700341 if (m_config.m_caItem.m_maxSuffixLength) {
342 if (clientCert->getIdentity().size() > m_config.m_caItem.m_caPrefix.size() + *m_config.m_caItem.m_maxSuffixLength) {
343 _LOG_ERROR("An invalid certificate name is being requested " << clientCert->getName());
344 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::NAME_NOT_ALLOWED,
345 "An invalid certificate name is being requested."));
346 return;
347 }
348 }
349 const auto& cert = m_keyChain.getPib().getIdentity(m_config.m_caItem.m_caPrefix).getDefaultKey().getDefaultCertificate();
tylerliu4a00aad2020-09-26 02:03:17 -0700350 if (!security::verifySignature(*clientCert, cert)) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700351 _LOG_ERROR("Invalid signature in the certificate to revoke.");
352 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
353 "Invalid signature in the certificate to revoke."));
tylerliu4a00aad2020-09-26 02:03:17 -0700354 return;
355 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700356 }
357
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700358 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800359 std::string requestId = std::to_string(random::generateWord64());
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700360 RequestState requestState(m_config.m_caItem.m_caPrefix, requestId, requestType, Status::BEFORE_CHALLENGE, *clientCert,
tylerliu8e170d62020-09-30 01:31:53 -0700361 makeBinaryBlock(tlv::ContentType_Key, aesKey, sizeof(aesKey)));
tylerliu63900d52020-09-30 03:01:18 -0700362 m_storage->addRequest(requestState);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800363 Data result;
364 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700365 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700366 if (requestType == RequestType::NEW) {
tylerliu4a00aad2020-09-26 02:03:17 -0700367 result.setContent(NEW::encodeDataContent(myEcdhPubKeyBase64,
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700368 std::to_string(saltInt),
tylerliu63900d52020-09-30 03:01:18 -0700369 requestState,
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700370 m_config.m_caItem.m_supportedChallenges));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700371 }
Zhiyi Zhangc87d52b2020-09-28 22:07:18 -0700372 else if (requestType == RequestType::REVOKE) {
tylerliu4a00aad2020-09-26 02:03:17 -0700373 result.setContent(REVOKE::encodeDataContent(myEcdhPubKeyBase64,
374 std::to_string(saltInt),
tylerliu63900d52020-09-30 03:01:18 -0700375 requestState,
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700376 m_config.m_caItem.m_supportedChallenges));
tylerliu4a00aad2020-09-26 02:03:17 -0700377 }
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700378 m_keyChain.sign(result, signingByIdentity(m_config.m_caItem.m_caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800379 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700380 if (m_config.m_statusUpdateCallback) {
tylerliu63900d52020-09-30 03:01:18 -0700381 m_config.m_statusUpdateCallback(requestState);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800382 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800383}
384
385void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700386CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800387{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700388 // get certificate request state
tylerliu63900d52020-09-30 03:01:18 -0700389 RequestState requestState = getCertificateRequest(request);
390 if (requestState.m_requestId == "") {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700391 _LOG_ERROR("No certificate request state can be found.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700392 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700393 "No certificate request state can be found."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800394 return;
395 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700396 // verify signature
tylerliu63900d52020-09-30 03:01:18 -0700397 if (!security::verifySignature(request, requestState.m_cert)) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700398 _LOG_ERROR("Invalid Signature in the Interest packet.");
399 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::BAD_SIGNATURE,
400 "Invalid Signature in the Interest packet."));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800401 return;
402 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700403 // decrypt the parameters
Suyong Won19fba4d2020-05-09 13:39:46 -0700404 Buffer paramTLVPayload;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700405 try {
tylerliu63900d52020-09-30 03:01:18 -0700406 paramTLVPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), requestState.m_encryptionKey.value(),
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700407 (uint8_t*)"test", strlen("test"));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700408 }
409 catch (const std::exception& e) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700410 _LOG_ERROR("Interest paramaters decryption failed: " << e.what());
tylerliu63900d52020-09-30 03:01:18 -0700411 m_storage->deleteRequest(requestState.m_requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700412 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700413 "Interest paramaters decryption failed."));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700414 return;
415 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700416 if (paramTLVPayload.size() == 0) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700417 _LOG_ERROR("No parameters are found after decryption.");
tylerliu63900d52020-09-30 03:01:18 -0700418 m_storage->deleteRequest(requestState.m_requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700419 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700420 "No parameters are found after decryption."));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700421 return;
422 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700423 Block paramTLV = makeBinaryBlock(tlv_encrypted_payload, paramTLVPayload.data(), paramTLVPayload.size());
424 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800425
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700426 // load the corresponding challenge module
Suyong Won19fba4d2020-05-09 13:39:46 -0700427 std::string challengeType = readString(paramTLV.get(tlv_selected_challenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800428 auto challenge = ChallengeModule::createChallengeModule(challengeType);
429 if (challenge == nullptr) {
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700430 _LOG_TRACE("Unrecognized challenge type: " << challengeType);
tylerliu63900d52020-09-30 03:01:18 -0700431 m_storage->deleteRequest(requestState.m_requestId);
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700432 m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER, "Unrecognized challenge type."));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700433 return;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800434 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700435
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700436 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
tylerliu63900d52020-09-30 03:01:18 -0700437 auto errorInfo = challenge->handleChallengeRequest(paramTLV, requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700438 if (std::get<0>(errorInfo) != ErrorCode::NO_ERROR) {
tylerliu63900d52020-09-30 03:01:18 -0700439 m_storage->deleteRequest(requestState.m_requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700440 m_face.put(generateErrorDataPacket(request.getName(), std::get<0>(errorInfo), std::get<1>(errorInfo)));
441 return;
442 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700443
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700444 Block payload;
tylerliu63900d52020-09-30 03:01:18 -0700445 if (requestState.m_status == Status::PENDING) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700446 // if challenge succeeded
tylerliu63900d52020-09-30 03:01:18 -0700447 if (requestState.m_requestType == RequestType::NEW) {
448 auto issuedCert = issueCertificate(requestState);
449 requestState.m_cert = issuedCert;
450 requestState.m_status = Status::SUCCESS;
451 m_storage->addCertificate(requestState.m_requestId, issuedCert);
452 m_storage->deleteRequest(requestState.m_requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700453
tylerliu63900d52020-09-30 03:01:18 -0700454 payload = CHALLENGE::encodeDataPayload(requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700455 payload.parse();
456 payload.push_back(makeNestedBlock(tlv_issued_cert_name, issuedCert.getName()));
457 payload.encode();
Zhiyi Zhang01e91a32020-09-29 12:10:00 -0700458 _LOG_TRACE("Challenge succeeded. Certificate has been issued: " << issuedCert.getName());
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800459 }
tylerliu63900d52020-09-30 03:01:18 -0700460 else if (requestState.m_requestType == RequestType::REVOKE) {
461 requestState.m_status = Status::SUCCESS;
462 m_storage->deleteRequest(requestState.m_requestId);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700463
tylerliu63900d52020-09-30 03:01:18 -0700464 payload = CHALLENGE::encodeDataPayload(requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700465 _LOG_TRACE("Challenge succeeded. Certificate has been revoked");
466 }
467 }
468 else {
tylerliu63900d52020-09-30 03:01:18 -0700469 m_storage->updateRequest(requestState);
470 payload = CHALLENGE::encodeDataPayload(requestState);
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700471 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800472 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700473
474 Data result;
475 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700476 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
tylerliu63900d52020-09-30 03:01:18 -0700477 auto contentBlock = encodeBlockWithAesGcm128(tlv::Content, requestState.m_encryptionKey.value(), payload.value(),
Suyong Won7968f7a2020-05-12 01:01:25 -0700478 payload.value_size(), (uint8_t*)"test", strlen("test"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700479 result.setContent(contentBlock);
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700480 m_keyChain.sign(result, signingByIdentity(m_config.m_caItem.m_caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700481 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700482 if (m_config.m_statusUpdateCallback) {
tylerliu63900d52020-09-30 03:01:18 -0700483 m_config.m_statusUpdateCallback(requestState);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800484 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700485}
486
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800487security::v2::Certificate
tylerliu63900d52020-09-30 03:01:18 -0700488CaModule::issueCertificate(const RequestState& requestState)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800489{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700490 auto expectedPeriod =
tylerliu63900d52020-09-30 03:01:18 -0700491 requestState.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700492 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800493 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700494
tylerliu63900d52020-09-30 03:01:18 -0700495 Name certName = requestState.m_cert.getKeyName();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700496 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800497 newCert.setName(certName);
tylerliu63900d52020-09-30 03:01:18 -0700498 newCert.setContent(requestState.m_cert.getContent());
499 _LOG_TRACE("cert request content " << requestState.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800500 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800501 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700502 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700503 m_config.m_caItem.m_caPrefix, signatureInfo);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800504
505 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700506 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800507 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800508}
509
Zhiyi Zhange232a742020-09-29 17:34:17 -0700510RequestState
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700511CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800512{
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400513 std::string requestId;
tylerliu63900d52020-09-30 03:01:18 -0700514 RequestState requestState;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800515 try {
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700516 requestId = readString(request.getName().at(m_config.m_caItem.m_caPrefix.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700517 }
518 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700519 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700520 }
521 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700522 _LOG_TRACE("Request Id to query the database " << requestId);
tylerliu63900d52020-09-30 03:01:18 -0700523 requestState = m_storage->getRequest(requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800524 }
525 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700526 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800527 }
tylerliu63900d52020-09-30 03:01:18 -0700528 return requestState;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800529}
530
531void
532CaModule::onRegisterFailed(const std::string& reason)
533{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700534 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800535}
536
537Block
538CaModule::dataContentFromJson(const JsonSection& jsonSection)
539{
540 std::stringstream ss;
541 boost::property_tree::write_json(ss, jsonSection);
542 return makeStringBlock(ndn::tlv::Content, ss.str());
543}
544
545JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700546CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800547{
548 std::string jsonString;
549 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700550 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700551 std::istringstream ss(jsonString);
552 JsonSection json;
553 boost::property_tree::json_parser::read_json(ss, json);
554 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800555 }
556 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700557 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800558 return JsonSection();
559 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800560}
561
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700562Data
563CaModule::generateErrorDataPacket(const Name& name, ErrorCode error, const std::string& errorInfo)
564{
565 Data result;
566 result.setName(name);
567 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
568 result.setContent(ErrorTLV::encodeDataContent(error, errorInfo));
Zhiyi Zhang9829da92020-09-30 16:19:34 -0700569 m_keyChain.sign(result, signingByIdentity(m_config.m_caItem.m_caPrefix));
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700570 return result;
571}
572
573} // namespace ndncert
574} // namespace ndn