blob: 5201e3111acb3b6221fb31cb8531baef045ba95f [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"
22#include "challenge-module.hpp"
23#include "logging.hpp"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070024#include "crypto-support/enc-tlv.hpp"
Suyong Won19fba4d2020-05-09 13:39:46 -070025#include "protocol-detail/info.hpp"
26#include "protocol-detail/probe.hpp"
27#include "protocol-detail/new.hpp"
28#include "protocol-detail/challenge.hpp"
tylerliu182bc532020-09-25 01:54:45 -070029#include "protocol-detail/revoke.hpp"
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080030#include <ndn-cxx/util/io.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080031#include <ndn-cxx/security/verification-helpers.hpp>
32#include <ndn-cxx/security/signing-helpers.hpp>
Junxiao Shi7c068032017-05-28 13:40:47 +000033#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -070034#include <ndn-cxx/metadata-object.hpp>
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080035
36namespace ndn {
37namespace ndncert {
38
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070039static const int IS_SUBNAME_MIN_OFFSET = 5;
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -070040static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -070041static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070042
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080043_LOG_INIT(ndncert.ca);
44
45CaModule::CaModule(Face& face, security::v2::KeyChain& keyChain,
46 const std::string& configPath, const std::string& storageType)
47 : m_face(face)
48 , m_keyChain(keyChain)
49{
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);
52 m_storage = CaStorage::createCaStorage(storageType);
53
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080054 registerPrefix();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080055}
56
57CaModule::~CaModule()
58{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070059 for (auto handle : m_interestFilterHandles) {
60 handle.cancel();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080061 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062 for (auto handle : m_registeredPrefixHandles) {
63 handle.unregister();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -080064 }
65}
66
67void
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080068CaModule::registerPrefix()
69{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070070 // register localhop discovery prefix
swa77020643ac2020-03-26 02:24:45 -070071 Name localhopInfoPrefix("/localhop/CA/INFO");
72 auto prefixId = m_face.setInterestFilter(InterestFilter(localhopInfoPrefix),
73 bind(&CaModule::onInfo, this, _2),
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080074 bind(&CaModule::onRegisterFailed, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 m_registeredPrefixHandles.push_back(prefixId);
swa77020643ac2020-03-26 02:24:45 -070076 _LOG_TRACE("Prefix " << localhopInfoPrefix << " got registered");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080077
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070078 // register prefixes
Suyong Won256c9062020-05-11 02:45:56 -070079 Name prefix = m_config.m_caPrefix;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070080 prefix.append("CA");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +080081
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070082 prefixId = m_face.registerPrefix(prefix,
83 [&] (const Name& name) {
swa77020643ac2020-03-26 02:24:45 -070084 // 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);
88
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070089 // register PROBE prefix
swa77020643ac2020-03-26 02:24:45 -070090 filterId = m_face.setInterestFilter(Name(name).append("PROBE"),
91 bind(&CaModule::onProbe, this, _2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070092 m_interestFilterHandles.push_back(filterId);
93
94 // register NEW prefix
swa770de007bc2020-03-24 21:26:21 -070095 filterId = m_face.setInterestFilter(Name(name).append("NEW"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070096 bind(&CaModule::onNew, this, _2));
97 m_interestFilterHandles.push_back(filterId);
98
99 // register SELECT prefix
swa770de007bc2020-03-24 21:26:21 -0700100 filterId = m_face.setInterestFilter(Name(name).append("CHALLENGE"),
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700101 bind(&CaModule::onChallenge, this, _2));
102 m_interestFilterHandles.push_back(filterId);
103
tylerliu182bc532020-09-25 01:54:45 -0700104 // register REVOKE prefix
105 filterId = m_face.setInterestFilter(Name(name).append("REVOKE"),
106 bind(&CaModule::onRevoke, this, _2));
107 m_interestFilterHandles.push_back(filterId);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700108 _LOG_TRACE("Prefix " << name << " got registered");
109 },
110 bind(&CaModule::onRegisterFailed, this, _2));
111 m_registeredPrefixHandles.push_back(prefixId);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800112}
113
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800114bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115CaModule::setProbeHandler(const ProbeHandler& handler)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800116{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700117 m_config.m_probeHandler = handler;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800118 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800119}
120
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800121bool
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700122CaModule::setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback)
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800123{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 m_config.m_statusUpdateCallback = onUpdateCallback;
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800125 return false;
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800126}
127
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700128shared_ptr<Data>
129CaModule::generateCaConfigMetaData()
swa77020643ac2020-03-26 02:24:45 -0700130{
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700131 // @TODO
132 // make metadata a class member variable m_infoMetadata
133 // check whether the m_infoMetadata has the latest versioned name, if not, then generate a new one
134 // otherwise, directly reply m_infoMetadata.makeData
135
136 auto infoPacket = generateCaConfigData();
137 MetadataObject metadata;
138 metadata.setVersionedName(infoPacket->getName().getPrefix(-1));
139 Name discoveryInterestName(infoPacket->getName().getPrefix(-2));
140 name::Component metadataComponent(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata"));
141 discoveryInterestName.append(metadataComponent);
142 auto metadataData = metadata.makeData(discoveryInterestName, m_keyChain, signingByIdentity(m_config.m_caPrefix));
143 return make_shared<Data>(metadataData);
144}
145
146shared_ptr<Data>
147CaModule::generateCaConfigData()
148{
149 // @TODO
150 // make CaInfo Data packet a class member variable m_infoData
151 // check whether the m_infoData is still valid, if not, then generate a new one
152 // otherwise, directly reply m_infoData
Suyong Won19fba4d2020-05-09 13:39:46 -0700153
154 const auto& pib = m_keyChain.getPib();
Suyong Won256c9062020-05-11 02:45:56 -0700155 const auto& identity = pib.getIdentity(m_config.m_caPrefix);
Suyong Won19fba4d2020-05-09 13:39:46 -0700156 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
157 Block contentTLV = INFO::encodeContentFromCAConfig(m_config, cert);
swa77020643ac2020-03-26 02:24:45 -0700158
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700159 Name infoPacketName(m_config.m_caPrefix);
160 infoPacketName.append("CA").append("INFO").appendVersion().appendSegment(0);
161 Data infoData(infoPacketName);
162 infoData.setContent(contentTLV);
163 infoData.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
164 m_keyChain.sign(infoData, signingByIdentity(m_config.m_caPrefix));
165 return make_shared<Data>(infoData);
166}
swa77020643ac2020-03-26 02:24:45 -0700167
Zhiyi Zhangfc1678a2020-05-12 16:52:14 -0700168void
169CaModule::onInfo(const Interest& request)
170{
171 _LOG_TRACE("Received INFO request");
172
173 if (request.getName().get(-1).type() == 32) {
174 m_face.put(*generateCaConfigMetaData());
175 }
176 else {
177 m_face.put(*generateCaConfigData());
178 }
swa77020643ac2020-03-26 02:24:45 -0700179
180 _LOG_TRACE("Handle INFO: send out the INFO response");
181}
182
183void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700184CaModule::onProbe(const Interest& request)
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800185{
swa77020643ac2020-03-26 02:24:45 -0700186 // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
187 _LOG_TRACE("Received PROBE request");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800188
swa77020643ac2020-03-26 02:24:45 -0700189 // process PROBE requests: find an available name
190 std::string availableId;
Suyong Won19fba4d2020-05-09 13:39:46 -0700191 const auto& parameterTLV = request.getApplicationParameters();
Suyong Won44d0cce2020-05-10 04:07:43 -0700192 parameterTLV.parse();
Suyong Won19fba4d2020-05-09 13:39:46 -0700193 if (!parameterTLV.hasValue()) {
194 _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
swa77020643ac2020-03-26 02:24:45 -0700195 return;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700196 }
swa77020643ac2020-03-26 02:24:45 -0700197 //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
198 if (m_config.m_probeHandler) {
199 try {
Suyong Won19fba4d2020-05-09 13:39:46 -0700200 availableId = m_config.m_probeHandler(parameterTLV);
swa77020643ac2020-03-26 02:24:45 -0700201 }
202 catch (const std::exception& e) {
203 _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700204 return;
205 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800206 }
swa77020643ac2020-03-26 02:24:45 -0700207 else {
208 // if there is no app-specified name lookup, use a random name id
209 availableId = std::to_string(random::generateSecureWord64());
210 }
Suyong Won256c9062020-05-11 02:45:56 -0700211 Name newIdentityName = m_config.m_caPrefix;
swa77020643ac2020-03-26 02:24:45 -0700212 newIdentityName.append(availableId);
213 _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
Suyong Won44d0cce2020-05-10 04:07:43 -0700214
Suyong Won19fba4d2020-05-09 13:39:46 -0700215 Block contentTLV = PROBE::encodeDataContent(newIdentityName.toUri(), m_config.m_probe, parameterTLV);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800216
217 Data result;
218 result.setName(request.getName());
Suyong Won19fba4d2020-05-09 13:39:46 -0700219 result.setContent(contentTLV);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700220 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Suyong Won256c9062020-05-11 02:45:56 -0700221 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800222 m_face.put(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700223 _LOG_TRACE("Handle PROBE: send out the PROBE response");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800224}
225
226void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700227CaModule::onNew(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800228{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700229 // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700230 // get ECDH pub key and cert request
Suyong Won19fba4d2020-05-09 13:39:46 -0700231 const auto& parameterTLV = request.getApplicationParameters();
Suyong Won7968f7a2020-05-12 01:01:25 -0700232 parameterTLV.parse();
233
Suyong Won19fba4d2020-05-09 13:39:46 -0700234 if (!parameterTLV.hasValue()) {
235 _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
Zhiyi Zhang547c8512019-06-18 23:46:14 -0700236 return;
237 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700238
239 std::string peerKeyBase64 = readString(parameterTLV.get(tlv_ecdh_pub));
240
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700241 if (peerKeyBase64 == "") {
Suyong Won19fba4d2020-05-09 13:39:46 -0700242 _LOG_ERROR("Empty ECDH PUB obtained from the Interest parameter.");
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700243 return;
244 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700245
246 // get server's ECDH pub key
247 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700248 try {
249 m_ecdh.deriveSecret(peerKeyBase64);
250 }
251 catch (const std::exception& e) {
252 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
253 return;
254 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700255 // generate salt for HKDF
256 auto saltInt = random::generateSecureWord64();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700257 // hkdf
258 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
Zhiyi Zhang36706832019-07-04 21:33:03 -0700259 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700260
261 // parse certificate request
Suyong Won19fba4d2020-05-09 13:39:46 -0700262 Block cert_req = parameterTLV.get(tlv_cert_request);
Suyong Won7968f7a2020-05-12 01:01:25 -0700263 cert_req.parse();
264
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700265 shared_ptr<security::v2::Certificate> clientCert = nullptr;
Suyong Won7968f7a2020-05-12 01:01:25 -0700266
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800267 try {
Suyong Won7968f7a2020-05-12 01:01:25 -0700268 security::v2::Certificate cert = security::v2::Certificate(cert_req.get(tlv::Data));
269 clientCert = make_shared<security::v2::Certificate>(cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800270 }
271 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700272 _LOG_ERROR("Unrecognized certificate request: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800273 return;
274 }
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700275 // check the validity period
276 auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
277 auto currentTime = time::system_clock::now();
278 if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
279 _LOG_ERROR("Client requests a too old notBefore timepoint.");
280 return;
281 }
Suyong Won7968f7a2020-05-12 01:01:25 -0700282 if (expectedPeriod.second > currentTime + m_config.m_maxValidityPeriod ||
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700283 expectedPeriod.second <= expectedPeriod.first) {
284 _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
285 return;
286 }
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700287
Zhiyi Zhang5f749a22019-06-12 17:02:33 -0700288 // verify the self-signed certificate, the request, and the token
Suyong Won256c9062020-05-11 02:45:56 -0700289 if (!m_config.m_caPrefix.isPrefixOf(clientCert->getName()) // under ca prefix
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700290 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
tylerliu0e060f12020-09-25 00:07:07 -0700291 || clientCert->getName().size() < m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700292 _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
293 return;
294 }
295 if (!security::verifySignature(*clientCert, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700296 _LOG_ERROR("Cert request with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700297 return;
298 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700299 if (!security::verifySignature(request, *clientCert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700300 _LOG_ERROR("Interest with bad signature.");
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700301 return;
302 }
303
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700304 // create new request instance
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800305 std::string requestId = std::to_string(random::generateWord64());
tylerliu182bc532020-09-25 01:54:45 -0700306 CertificateRequest certRequest(m_config.m_caPrefix, requestId, REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, *clientCert);
Suyong Won19fba4d2020-05-09 13:39:46 -0700307
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800308 try {
309 m_storage->addRequest(certRequest);
310 }
311 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700312 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800313 return;
314 }
315
316 Data result;
317 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700318 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Suyong Won19fba4d2020-05-09 13:39:46 -0700319 result.setContent(NEW::encodeDataContent(myEcdhPubKeyBase64,
320 std::to_string(saltInt),
321 certRequest,
322 m_config.m_supportedChallenges));
Suyong Won256c9062020-05-11 02:45:56 -0700323 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800324 m_face.put(result);
Zhiyi Zhanga63b7372017-05-17 14:14:34 -0700325
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700326 if (m_config.m_statusUpdateCallback) {
327 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang7420cf52017-12-18 18:59:21 +0800328 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800329}
330
331void
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700332CaModule::onChallenge(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800333{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700334 // get certificate request state
335 CertificateRequest certRequest = getCertificateRequest(request);
336 if (certRequest.m_requestId == "") {
337 // cannot get the request state
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700338 _LOG_ERROR("Cannot find certificate request state from CA's storage.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800339 return;
340 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700341 // verify signature
342 if (!security::verifySignature(request, certRequest.m_cert)) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700343 _LOG_ERROR("Challenge Interest with bad signature.");
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800344 return;
345 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700346 // decrypt the parameters
Suyong Won19fba4d2020-05-09 13:39:46 -0700347 Buffer paramTLVPayload;
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700348 try {
Suyong Won19fba4d2020-05-09 13:39:46 -0700349 paramTLVPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), m_aesKey,
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700350 (uint8_t*)"test", strlen("test"));
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700351 }
352 catch (const std::exception& e) {
353 _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
354 return;
355 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700356 if (paramTLVPayload.size() == 0) {
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700357 _LOG_ERROR("Got an empty buffer from content decryption.");
358 return;
359 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700360
Suyong Won44d0cce2020-05-10 04:07:43 -0700361 Block paramTLV = makeBinaryBlock(tlv_encrypted_payload, paramTLVPayload.data(), paramTLVPayload.size());
362 paramTLV.parse();
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800363
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700364 // load the corresponding challenge module
Suyong Won19fba4d2020-05-09 13:39:46 -0700365 std::string challengeType = readString(paramTLV.get(tlv_selected_challenge));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800366 auto challenge = ChallengeModule::createChallengeModule(challengeType);
Suyong Won19fba4d2020-05-09 13:39:46 -0700367
Suyong Won44d0cce2020-05-10 04:07:43 -0700368 Block payload;
Suyong Won19fba4d2020-05-09 13:39:46 -0700369
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800370 if (challenge == nullptr) {
371 _LOG_TRACE("Unrecognized challenge type " << challengeType);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700372 certRequest.m_status = STATUS_FAILURE;
373 certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
Suyong Won44d0cce2020-05-10 04:07:43 -0700374 payload = CHALLENGE::encodeDataPayload(certRequest);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800375 }
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700376 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700377 _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
378 // let challenge module handle the request
Suyong Won19fba4d2020-05-09 13:39:46 -0700379 challenge->handleChallengeRequest(paramTLV, certRequest);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700380 if (certRequest.m_status == STATUS_FAILURE) {
381 // if challenge failed
382 m_storage->deleteRequest(certRequest.m_requestId);
Suyong Won44d0cce2020-05-10 04:07:43 -0700383 payload = CHALLENGE::encodeDataPayload(certRequest);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700384 _LOG_TRACE("Challenge failed");
Zhiyi Zhanga9bda732017-05-20 22:58:55 -0700385 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700386 else if (certRequest.m_status == STATUS_PENDING) {
387 // if challenge succeeded
tylerliu182bc532020-09-25 01:54:45 -0700388 if (certRequest.m_requestType == REQUEST_TYPE_NEW) {
389 auto issuedCert = issueCertificate(certRequest);
390 certRequest.m_cert = issuedCert;
391 certRequest.m_status = STATUS_SUCCESS;
392 try {
393 m_storage->addCertificate(certRequest.m_requestId, issuedCert);
394 m_storage->deleteRequest(certRequest.m_requestId);
395 _LOG_TRACE("New Certificate Issued " << issuedCert.getName());
396 }
397 catch (const std::exception& e) {
398 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
399 return;
400 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700401
tylerliu182bc532020-09-25 01:54:45 -0700402 payload = CHALLENGE::encodeDataPayload(certRequest);
403 payload.parse();
404 payload.push_back(makeNestedBlock(tlv_issued_cert_name, issuedCert.getName()));
405 payload.encode();
Suyong Won19fba4d2020-05-09 13:39:46 -0700406
tylerliu182bc532020-09-25 01:54:45 -0700407 //contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
408 _LOG_TRACE("Challenge succeeded. Certificate has been issued");
409 }
410 else if (certRequest.m_requestType == REQUEST_TYPE_REVOKE) {
411 certRequest.m_status = STATUS_SUCCESS;
412 try {
413 m_storage->deleteRequest(certRequest.m_requestId);
414 _LOG_TRACE("Certificate Revoked");
415 }
416 catch (const std::exception& e) {
417 _LOG_ERROR("Cannot add issued cert and remove the request: " << e.what());
418 return;
419 }
420
421 payload = CHALLENGE::encodeDataPayload(certRequest);
422 payload.parse();
423 _LOG_TRACE("Challenge succeeded. Certificate has been revoked");
424 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800425 }
426 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700427 try {
428 m_storage->updateRequest(certRequest);
429 }
430 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700431 _LOG_TRACE("Cannot update request instance: " << e.what());
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700432 return;
433 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700434 payload = CHALLENGE::encodeDataPayload(certRequest);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700435 _LOG_TRACE("No failure no success. Challenge moves on");
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800436 }
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800437 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700438
439 Data result;
440 result.setName(request.getName());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700441 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700442
443 // encrypt the content
Suyong Won7968f7a2020-05-12 01:01:25 -0700444 auto contentBlock = encodeBlockWithAesGcm128(tlv::Content, m_aesKey, payload.value(),
445 payload.value_size(), (uint8_t*)"test", strlen("test"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700446 result.setContent(contentBlock);
Suyong Won256c9062020-05-11 02:45:56 -0700447 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700448 m_face.put(result);
449
450 if (m_config.m_statusUpdateCallback) {
451 m_config.m_statusUpdateCallback(certRequest);
Zhiyi Zhang1c0bd372017-12-18 18:32:55 +0800452 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700453}
454
tylerliu182bc532020-09-25 01:54:45 -0700455void
456CaModule::onRevoke(const Interest& request)
457{
458 // NEW Naming Convention: /<CA-prefix>/CA/REVOKE/[SignedInterestParameters_Digest]
459 // get ECDH pub key and cert request
460 const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
461 if (parameterJson.empty()) {
462 _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
463 return;
464 }
465 std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
466 if (peerKeyBase64 == "") {
467 _LOG_ERROR("Empty JSON_CLIENT_ECDH obtained from the Interest parameter.");
468 return;
469 }
470
471 // get server's ECDH pub key
472 auto myEcdhPubKeyBase64 = m_ecdh.getBase64PubKey();
473 try {
474 m_ecdh.deriveSecret(peerKeyBase64);
475 }
476 catch (const std::exception& e) {
477 _LOG_ERROR("Cannot derive a shared secret using the provided ECDH key: " << e.what());
478 return;
479 }
480
481 // parse certificate request
482 std::string certRevokeStr = parameterJson.get(JSON_CLIENT_CERT_REVOKE, "");
483 shared_ptr<security::v2::Certificate> clientCert = nullptr;
484 try {
485 std::stringstream ss(certRevokeStr);
486 clientCert = io::load<security::v2::Certificate>(ss);
487 }
488 catch (const std::exception& e) {
489 _LOG_ERROR("Unrecognized revocation request: " << e.what());
490 return;
491 }
492
493 // verify the certificate
494 if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
495 || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
496 || clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
497 _LOG_ERROR("Invalid certificate name " << clientCert->getName());
498 return;
499 }
500 const auto& cert = m_keyChain.getPib().getIdentity(m_config.m_caName).getDefaultKey().getDefaultCertificate();
501 if (!security::verifySignature(*clientCert, cert)) {
502 _LOG_ERROR("Cert request with bad signature.");
503 return;
504 }
505
506 // generate salt for HKDF
507 auto saltInt = random::generateSecureWord64();
508 // hkdf
509 hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
510 (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
511
512 // create new request instance
513 std::string requestId = std::to_string(random::generateWord64());
514 CertificateRequest certRequest(m_config.m_caName, requestId, REQUEST_TYPE_REVOKE, STATUS_BEFORE_CHALLENGE,
515 *clientCert);
516 try {
517 m_storage->addRequest(certRequest);
518 }
519 catch (const std::exception& e) {
520 _LOG_ERROR("Cannot add new request instance into the storage: " << e.what());
521 return;
522 }
523
524 Data result;
525 result.setName(request.getName());
526 result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
527 result.setContent(REVOKE::encodeDataContent(myEcdhPubKeyBase64,
528 std::to_string(saltInt),
529 certRequest,
530 m_config.m_supportedChallenges));
531 m_keyChain.sign(result, signingByIdentity(m_config.m_caPrefix));
532 m_face.put(result);
533
534 if (m_config.m_statusUpdateCallback) {
535 m_config.m_statusUpdateCallback(certRequest);
536 }
537}
538
539
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800540security::v2::Certificate
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700541CaModule::issueCertificate(const CertificateRequest& certRequest)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800542{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700543 auto expectedPeriod =
544 certRequest.m_cert.getValidityPeriod().getPeriod();
Zhiyi Zhang1a735bc2019-07-04 21:36:49 -0700545 security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800546 security::v2::Certificate newCert;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700547
548 Name certName = certRequest.m_cert.getKeyName();
549 certName.append("NDNCERT").append(std::to_string(random::generateSecureWord64()));
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800550 newCert.setName(certName);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700551 newCert.setContent(certRequest.m_cert.getContent());
552 _LOG_TRACE("cert request content " << certRequest.m_cert);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800553 SignatureInfo signatureInfo;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800554 signatureInfo.setValidityPeriod(period);
Zhiyi Zhangad6cf932017-10-26 16:19:15 -0700555 security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
Suyong Won256c9062020-05-11 02:45:56 -0700556 m_config.m_caPrefix, signatureInfo);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800557
558 m_keyChain.sign(newCert, signingInfo);
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700559 _LOG_TRACE("new cert got signed" << newCert);
Zhiyi Zhang343cdfb2018-01-17 12:04:28 -0800560 return newCert;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800561}
562
563CertificateRequest
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700564CaModule::getCertificateRequest(const Interest& request)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800565{
Davide Pesaventob48bbda2020-07-27 19:41:37 -0400566 std::string requestId;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800567 CertificateRequest certRequest;
568 try {
Suyong Won256c9062020-05-11 02:45:56 -0700569 requestId = readString(request.getName().at(m_config.m_caPrefix.size() + 2));
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700570 }
571 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700572 _LOG_ERROR("Cannot read the request ID out from the request: " << e.what());
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700573 }
574 try {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700575 _LOG_TRACE("Request Id to query the database " << requestId);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800576 certRequest = m_storage->getRequest(requestId);
577 }
578 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700579 _LOG_ERROR("Cannot get certificate request record from the storage: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800580 }
581 return certRequest;
582}
583
584void
585CaModule::onRegisterFailed(const std::string& reason)
586{
Zhiyi Zhang693c1272017-05-20 22:58:55 -0700587 _LOG_ERROR("Failed to register prefix in local hub's daemon, REASON: " << reason);
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800588}
589
590Block
591CaModule::dataContentFromJson(const JsonSection& jsonSection)
592{
593 std::stringstream ss;
594 boost::property_tree::write_json(ss, jsonSection);
595 return makeStringBlock(ndn::tlv::Content, ss.str());
596}
597
598JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700599CaModule::jsonFromBlock(const Block& block)
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800600{
601 std::string jsonString;
602 try {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700603 jsonString = encoding::readString(block);
Zhiyi Zhang42e1cf32019-06-22 17:11:42 -0700604 std::istringstream ss(jsonString);
605 JsonSection json;
606 boost::property_tree::json_parser::read_json(ss, json);
607 return json;
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800608 }
609 catch (const std::exception& e) {
Zhiyi Zhangbc2d4122019-09-10 12:10:54 -0700610 _LOG_ERROR("Cannot read JSON string from TLV Value: " << e.what());
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800611 return JsonSection();
612 }
Zhiyi Zhangf5246c42017-01-26 09:39:20 -0800613}
614
615} // namespace ndncert
616} // namespace ndn