validation-policy-signed-interest.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2023 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library 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 Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
23 
24 namespace ndn::security {
25 
27  const Options& options)
28  : m_options(options)
29  , m_byKeyName(m_container.get<0>())
30  , m_byLastRefreshed(m_container.get<1>())
31 {
32  if (inner == nullptr) {
33  NDN_THROW(std::invalid_argument("Inner policy is missing"));
34  }
35  setInnerPolicy(std::move(inner));
36 
37  m_options.timestampGracePeriod = std::max(m_options.timestampGracePeriod, 0_ns);
38 }
39 
40 void
42  const shared_ptr<ValidationState>& state,
43  const ValidationContinuation& continueValidation)
44 {
45  getInnerPolicy().checkPolicy(data, state, continueValidation);
46 }
47 
48 void
50  const shared_ptr<ValidationState>& state,
51  const ValidationContinuation& continueValidation)
52 {
53  if (!state->getOutcome()) {
54  return;
55  }
56 
57  auto fmt = state->getTag<SignedInterestFormatTag>();
58  BOOST_ASSERT(fmt);
59  if (*fmt == SignedInterestFormat::V03 && !checkIncomingInterest(state, interest)) {
60  return;
61  }
62 
63  getInnerPolicy().checkPolicy(interest, state, continueValidation);
64 }
65 
66 bool
67 ValidationPolicySignedInterest::checkIncomingInterest(const shared_ptr<ValidationState>& state,
68  const Interest& interest)
69 {
70  auto sigInfo = getSignatureInfo(interest, *state);
71  if (!state->getOutcome()) {
72  return false;
73  }
74  Name keyName = getKeyLocatorName(sigInfo, *state);
75  if (!state->getOutcome()) {
76  return false;
77  }
78  auto timestamp = sigInfo.getTime();
79  auto seqNum = sigInfo.getSeqNum();
80  auto nonce = sigInfo.getNonce();
81 
82  auto record = m_byKeyName.find(keyName);
83 
84  if (m_options.shouldValidateTimestamps) {
85  if (!timestamp.has_value()) {
86  state->fail({ValidationError::POLICY_ERROR, "Timestamp is required by policy but is not present"});
87  return false;
88  }
89 
90  auto now = time::system_clock::now();
91  if (time::abs(now - *timestamp) > m_options.timestampGracePeriod) {
92  state->fail({ValidationError::POLICY_ERROR,
93  "Timestamp is outside the grace period for key " + keyName.toUri()});
94  return false;
95  }
96 
97  if (record != m_byKeyName.end() && record->timestamp.has_value() && timestamp <= record->timestamp) {
98  state->fail({ValidationError::POLICY_ERROR,
99  "Timestamp is reordered for key " + keyName.toUri()});
100  return false;
101  }
102  }
103 
104  if (m_options.shouldValidateSeqNums) {
105  if (!seqNum.has_value()) {
106  state->fail({ValidationError::POLICY_ERROR,
107  "Sequence number is required by policy but is not present"});
108  return false;
109  }
110 
111  if (record != m_byKeyName.end() && record->seqNum.has_value() && seqNum <= record->seqNum) {
112  state->fail({ValidationError::POLICY_ERROR,
113  "Sequence number is reordered for key " + keyName.toUri()});
114  return false;
115  }
116  }
117 
118  if (m_options.shouldValidateNonces) {
119  if (!nonce.has_value()) {
120  state->fail({ValidationError::POLICY_ERROR, "Nonce is required by policy but is not present"});
121  return false;
122  }
123 
124  if (record != m_byKeyName.end() && record->observedNonces.get<NonceSet>().count(*nonce) > 0) {
125  state->fail({ValidationError::POLICY_ERROR,
126  "Nonce matches previously-seen nonce for key " + keyName.toUri()});
127  return false;
128  }
129  }
130 
131  if (m_options.maxRecordCount != 0) {
132  auto interestState = std::dynamic_pointer_cast<InterestValidationState>(state);
133  BOOST_ASSERT(interestState != nullptr);
134  interestState->afterSuccess.connect([=] (const Interest&) {
135  insertRecord(keyName, timestamp, seqNum, nonce);
136  });
137  }
138  return true;
139 }
140 
141 void
142 ValidationPolicySignedInterest::insertRecord(const Name& keyName,
143  std::optional<time::system_clock::time_point> timestamp,
144  std::optional<uint64_t> seqNum,
145  std::optional<SigNonce> nonce)
146 {
147  // If key record exists, update last refreshed time. Otherwise, create new record.
148  auto [it, isOk] = m_byKeyName.emplace(keyName, timestamp, seqNum);
149  if (!isOk) {
150  // There was already a record for this key, we just need to update it
151  isOk = m_byKeyName.modify(it, [&] (LastInterestRecord& record) {
152  record.lastRefreshed = time::steady_clock::now();
153  if (timestamp.has_value()) {
154  record.timestamp = timestamp;
155  }
156  if (seqNum.has_value()) {
157  record.seqNum = seqNum;
158  }
159  });
160  BOOST_VERIFY(isOk);
161  }
162 
163  // If has nonce and max nonce list size > 0 (or unlimited), append to observed nonce list
164  if (m_options.shouldValidateNonces && m_options.maxNonceRecordCount != 0 && nonce.has_value()) {
165  isOk = m_byKeyName.modify(it, [this, &nonce] (LastInterestRecord& record) {
166  auto& sigNonceList = record.observedNonces.get<NonceList>();
167  sigNonceList.push_back(*nonce);
168  // Ensure observed nonce list is at or below max nonce list size
169  if (m_options.maxNonceRecordCount >= 0 &&
170  sigNonceList.size() > static_cast<size_t>(m_options.maxNonceRecordCount)) {
171  BOOST_ASSERT(sigNonceList.size() == static_cast<size_t>(m_options.maxNonceRecordCount) + 1);
172  sigNonceList.pop_front();
173  }
174  });
175  BOOST_VERIFY(isOk);
176  }
177 
178  // Ensure record count is at or below max
179  if (m_options.maxRecordCount >= 0 &&
180  m_byLastRefreshed.size() > static_cast<size_t>(m_options.maxRecordCount)) {
181  BOOST_ASSERT(m_byLastRefreshed.size() == static_cast<size_t>(m_options.maxRecordCount) + 1);
182  m_byLastRefreshed.erase(m_byLastRefreshed.begin());
183  }
184 }
185 
186 } // namespace ndn::security
Represents a Data packet.
Definition: data.hpp:39
Represents an Interest packet.
Definition: interest.hpp:50
Represents an absolute name.
Definition: name.hpp:45
void toUri(std::ostream &os, name::UriFormat format=name::UriFormat::DEFAULT) const
Write URI representation of the name to the output stream.
Definition: name.cpp:324
Provides a tag type for simple types.
Definition: tag.hpp:56
@ POLICY_ERROR
The packet violates the validation rules enforced by the policy.
bool shouldValidateSeqNums
Whether to validate sequence numbers in signed Interests by ensuring they are present and are strictl...
ssize_t maxNonceRecordCount
Number of previous nonces to track for each public key.
time::nanoseconds timestampGracePeriod
Tolerance of timestamp differences from the current time.
ssize_t maxRecordCount
Max number of distinct public keys to track.
bool shouldValidateNonces
Whether to validate nonces by ensuring that they are present and do not overlap with one of the last ...
bool shouldValidateTimestamps
Whether to validate timestamps in signed Interests by ensuring they are not reordered for a given pub...
ValidationPolicySignedInterest(unique_ptr< ValidationPolicy > inner, const Options &options={})
Constructor.
void checkPolicy(const Data &data, const shared_ptr< ValidationState > &state, const ValidationContinuation &continueValidation) override
Check data against the policy.
std::function< void(const shared_ptr< CertificateRequest > &certRequest, const shared_ptr< ValidationState > &state)> ValidationContinuation
ValidationPolicy & getInnerPolicy()
Return the inner policy.
virtual void checkPolicy(const Data &data, const shared_ptr< ValidationState > &state, const ValidationContinuation &continueValidation)=0
Check data against the policy.
void setInnerPolicy(unique_ptr< ValidationPolicy > innerPolicy)
Set inner policy.
static time_point now() noexcept
Definition: time.cpp:79
static time_point now() noexcept
Definition: time.cpp:45
#define NDN_THROW(e)
Definition: exception.hpp:56
Contains the ndn-cxx security framework.
@ V03
Sign Interest using Packet Specification v0.3 semantics.
SignatureInfo getSignatureInfo(const Interest &interest, ValidationState &state)
Extract SignatureInfo from a signed Interest.
Name getKeyLocatorName(const SignatureInfo &si, ValidationState &state)
Extract the KeyLocator name from a SignatureInfo element.
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
Returns the absolute value of the duration d.
Definition: time.hpp:63
@ Name
Definition: tlv.hpp:71
@ Interest
Definition: tlv.hpp:68