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-2022 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 {
25 namespace security {
26 inline namespace v2 {
27 
29  const Options& options)
30  : m_options(options)
31  , m_byKeyName(m_container.get<0>())
32  , m_byLastRefreshed(m_container.get<1>())
33 {
34  if (inner == nullptr) {
35  NDN_THROW(std::invalid_argument("Inner policy is missing"));
36  }
37  setInnerPolicy(std::move(inner));
38 
39  m_options.timestampGracePeriod = std::max(m_options.timestampGracePeriod, 0_ns);
40 }
41 
42 void
44  const shared_ptr<ValidationState>& state,
45  const ValidationContinuation& continueValidation)
46 {
47  getInnerPolicy().checkPolicy(data, state, continueValidation);
48 }
49 
50 void
52  const shared_ptr<ValidationState>& state,
53  const ValidationContinuation& continueValidation)
54 {
55  if (!state->getOutcome()) {
56  return;
57  }
58 
59  auto fmt = state->getTag<SignedInterestFormatTag>();
60  BOOST_ASSERT(fmt);
61  if (*fmt == SignedInterestFormat::V03 && !checkIncomingInterest(state, interest)) {
62  return;
63  }
64 
65  getInnerPolicy().checkPolicy(interest, state, continueValidation);
66 }
67 
68 bool
69 ValidationPolicySignedInterest::checkIncomingInterest(const shared_ptr<ValidationState>& state,
70  const Interest& interest)
71 {
72  auto sigInfo = getSignatureInfo(interest, *state);
73  if (!state->getOutcome()) {
74  return false;
75  }
76  Name keyName = getKeyLocatorName(sigInfo, *state);
77  if (!state->getOutcome()) {
78  return false;
79  }
80  auto timestamp = sigInfo.getTime();
81  auto seqNum = sigInfo.getSeqNum();
82  auto nonce = sigInfo.getNonce();
83 
84  auto record = m_byKeyName.find(keyName);
85 
86  if (m_options.shouldValidateTimestamps) {
87  if (!timestamp.has_value()) {
88  state->fail({ValidationError::POLICY_ERROR, "Timestamp is required by policy but is not present"});
89  return false;
90  }
91 
92  auto now = time::system_clock::now();
93  if (time::abs(now - *timestamp) > m_options.timestampGracePeriod) {
94  state->fail({ValidationError::POLICY_ERROR,
95  "Timestamp is outside the grace period for key " + keyName.toUri()});
96  return false;
97  }
98 
99  if (record != m_byKeyName.end() && record->timestamp.has_value() && timestamp <= record->timestamp) {
100  state->fail({ValidationError::POLICY_ERROR,
101  "Timestamp is reordered for key " + keyName.toUri()});
102  return false;
103  }
104  }
105 
106  if (m_options.shouldValidateSeqNums) {
107  if (!seqNum.has_value()) {
108  state->fail({ValidationError::POLICY_ERROR,
109  "Sequence number is required by policy but is not present"});
110  return false;
111  }
112 
113  if (record != m_byKeyName.end() && record->seqNum.has_value() && seqNum <= record->seqNum) {
114  state->fail({ValidationError::POLICY_ERROR,
115  "Sequence number is reordered for key " + keyName.toUri()});
116  return false;
117  }
118  }
119 
120  if (m_options.shouldValidateNonces) {
121  if (!nonce.has_value()) {
122  state->fail({ValidationError::POLICY_ERROR, "Nonce is required by policy but is not present"});
123  return false;
124  }
125 
126  if (record != m_byKeyName.end() && record->observedNonces.get<NonceSet>().count(*nonce) > 0) {
127  state->fail({ValidationError::POLICY_ERROR,
128  "Nonce matches previously-seen nonce for key " + keyName.toUri()});
129  return false;
130  }
131  }
132 
133  if (m_options.maxRecordCount != 0) {
134  auto interestState = dynamic_pointer_cast<InterestValidationState>(state);
135  BOOST_ASSERT(interestState != nullptr);
136  interestState->afterSuccess.connect([=] (const Interest&) {
137  insertRecord(keyName, timestamp, seqNum, nonce);
138  });
139  }
140  return true;
141 }
142 
143 void
144 ValidationPolicySignedInterest::insertRecord(const Name& keyName,
145  optional<time::system_clock::TimePoint> timestamp,
146  optional<uint64_t> seqNum,
147  optional<SigNonce> nonce)
148 {
149  // If key record exists, update last refreshed time. Otherwise, create new record.
150  Container::nth_index<0>::type::iterator it;
151  bool isOk;
152  std::tie(it, isOk) = m_byKeyName.emplace(keyName, timestamp, seqNum);
153  if (!isOk) {
154  // There was already a record for this key, we just need to update it
155  isOk = m_byKeyName.modify(it, [&] (LastInterestRecord& record) {
156  record.lastRefreshed = time::steady_clock::now();
157  if (timestamp.has_value()) {
158  record.timestamp = timestamp;
159  }
160  if (seqNum.has_value()) {
161  record.seqNum = seqNum;
162  }
163  });
164  BOOST_VERIFY(isOk);
165  }
166 
167  // If has nonce and max nonce list size > 0 (or unlimited), append to observed nonce list
168  if (m_options.shouldValidateNonces && m_options.maxNonceRecordCount != 0 && nonce.has_value()) {
169  isOk = m_byKeyName.modify(it, [this, &nonce] (LastInterestRecord& record) {
170  auto& sigNonceList = record.observedNonces.get<NonceList>();
171  sigNonceList.push_back(*nonce);
172  // Ensure observed nonce list is at or below max nonce list size
173  if (m_options.maxNonceRecordCount >= 0 &&
174  sigNonceList.size() > static_cast<size_t>(m_options.maxNonceRecordCount)) {
175  BOOST_ASSERT(sigNonceList.size() == static_cast<size_t>(m_options.maxNonceRecordCount) + 1);
176  sigNonceList.pop_front();
177  }
178  });
179  BOOST_VERIFY(isOk);
180  }
181 
182  // Ensure record count is at or below max
183  if (m_options.maxRecordCount >= 0 &&
184  m_byLastRefreshed.size() > static_cast<size_t>(m_options.maxRecordCount)) {
185  BOOST_ASSERT(m_byLastRefreshed.size() == static_cast<size_t>(m_options.maxRecordCount) + 1);
186  m_byLastRefreshed.erase(m_byLastRefreshed.begin());
187  }
188 }
189 
190 } // inline namespace v2
191 } // namespace security
192 } // namespace ndn
Represents a Data packet.
Definition: data.hpp:39
Represents an Interest packet.
Definition: interest.hpp:50
Represents an absolute name.
Definition: name.hpp:44
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:349
Provides a tag type for simple types.
Definition: tag.hpp:56
@ POLICY_ERROR
The packet violates the validation rules enforced by the policy.
ssize_t maxNonceRecordCount
Number of previous nonces to track for each public key.
bool shouldValidateSeqNums
Whether to validate sequence numbers in signed Interests by ensuring they are present and are strictl...
ssize_t maxRecordCount
Max number of distinct public keys to track.
time::nanoseconds timestampGracePeriod
Tolerance of timestamp differences from the current time.
bool shouldValidateTimestamps
Whether to validate timestamps in signed Interests by ensuring they are not reordered for a given pub...
bool shouldValidateNonces
Whether to validate nonces by ensuring that they are present and do not overlap with one of the last ...
void checkPolicy(const Data &data, const shared_ptr< ValidationState > &state, const ValidationContinuation &continueValidation) override
Check data against the policy.
ValidationPolicySignedInterest(unique_ptr< ValidationPolicy > inner, const Options &options={})
Constructor.
ValidationPolicy & getInnerPolicy()
Return the inner policy.
std::function< void(const shared_ptr< CertificateRequest > &certRequest, const shared_ptr< ValidationState > &state)> ValidationContinuation
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:80
static time_point now() noexcept
Definition: time.cpp:46
#define NDN_THROW(e)
Definition: exception.hpp:61
Name getKeyLocatorName(const SignatureInfo &si, ValidationState &state)
Extract the KeyLocator name from a SignatureInfo element.
SignatureInfo getSignatureInfo(const Interest &interest, ValidationState &state)
Extract SignatureInfo from a signed Interest.
@ V03
Sign Interest using Packet Specification v0.3 semantics.
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
Definition: time.hpp:58
@ Name
Definition: tlv.hpp:71
@ Interest
Definition: tlv.hpp:68
Definition: data.cpp:25