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-2020 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  auto fmt = state->getTag<SignedInterestFormatTag>();
56  BOOST_ASSERT(fmt);
57 
58  if (!state->getOutcome()) {
59  return;
60  }
61 
62  if (*fmt == SignedInterestFormat::V03 && !checkIncomingInterest(state, interest)) {
63  return;
64  }
65 
66  getInnerPolicy().checkPolicy(interest, state, std::bind(continueValidation, _1, _2));
67 }
68 
69 bool
70 ValidationPolicySignedInterest::checkIncomingInterest(const shared_ptr<ValidationState>& state,
71  const Interest& interest)
72 {
73  // Extract information from Interest
74  BOOST_ASSERT(interest.getSignatureInfo());
75  Name keyName = getKeyLocatorName(interest, *state);
76  auto timestamp = interest.getSignatureInfo()->getTime();
77  auto seqNum = interest.getSignatureInfo()->getSeqNum();
78  auto nonce = interest.getSignatureInfo()->getNonce();
79 
80  auto record = m_byKeyName.find(keyName);
81 
82  if (m_options.shouldValidateTimestamps) {
83  if (!timestamp.has_value()) {
84  state->fail({ValidationError::POLICY_ERROR,
85  "Timestamp is required by policy but is not present"});
86  return false;
87  }
88 
89  auto now = time::system_clock::now();
90  if (time::abs(now - *timestamp) > m_options.timestampGracePeriod) {
91  state->fail({ValidationError::POLICY_ERROR,
92  "Timestamp is outside the grace period for key " + keyName.toUri()});
93  return false;
94  }
95 
96  if (record != m_byKeyName.end() && record->timestamp.has_value() && timestamp <= record->timestamp) {
97  state->fail({ValidationError::POLICY_ERROR,
98  "Timestamp is reordered for key " + keyName.toUri()});
99  return false;
100  }
101  }
102 
103  if (m_options.shouldValidateSeqNums) {
104  if (!seqNum.has_value()) {
105  state->fail({ValidationError::POLICY_ERROR,
106  "Sequence number is required by policy but is not present"});
107  return false;
108  }
109 
110  if (record != m_byKeyName.end() && record->seqNum.has_value() && seqNum <= record->seqNum) {
111  state->fail({ValidationError::POLICY_ERROR,
112  "Sequence number is reordered for key " + keyName.toUri()});
113  return false;
114  }
115  }
116 
117  if (m_options.shouldValidateNonces) {
118  if (!nonce.has_value()) {
119  state->fail({ValidationError::POLICY_ERROR, "Nonce is required by policy but is not present"});
120  return false;
121  }
122 
123  if (record != m_byKeyName.end() && record->observedNonces.get<NonceSet>().count(*nonce) > 0) {
124  state->fail({ValidationError::POLICY_ERROR,
125  "Nonce matches previously-seen nonce for key " + keyName.toUri()});
126  return false;
127  }
128  }
129 
130  if (m_options.maxRecordCount != 0) {
131  auto interestState = dynamic_pointer_cast<InterestValidationState>(state);
132  BOOST_ASSERT(interestState != nullptr);
133  interestState->afterSuccess.connect([=] (const Interest&) {
134  insertRecord(keyName, timestamp, seqNum, nonce);
135  });
136  }
137  return true;
138 }
139 
140 void
141 ValidationPolicySignedInterest::insertRecord(const Name& keyName,
142  optional<time::system_clock::TimePoint> timestamp,
143  optional<uint64_t> seqNum,
144  optional<SigNonce> nonce)
145 {
146  // If key record exists, update last refreshed time. Otherwise, create new record.
147  Container::nth_index<0>::type::iterator it;
148  bool isOk;
149  std::tie(it, isOk) = m_byKeyName.emplace(keyName, timestamp, seqNum);
150  if (!isOk) {
151  // There was already a record for this key, we just need to update it
152  isOk = m_byKeyName.modify(it, [&] (LastInterestRecord& record) {
153  record.lastRefreshed = time::steady_clock::now();
154  if (timestamp.has_value()) {
155  record.timestamp = timestamp;
156  }
157  if (seqNum.has_value()) {
158  record.seqNum = seqNum;
159  }
160  });
161  BOOST_VERIFY(isOk);
162  }
163 
164  // If has nonce and max nonce list size > 0 (or unlimited), append to observed nonce list
165  if (m_options.shouldValidateNonces && m_options.maxNonceRecordCount != 0 && nonce.has_value()) {
166  isOk = m_byKeyName.modify(it, [this, &nonce] (LastInterestRecord& record) {
167  auto& sigNonceList = record.observedNonces.get<NonceList>();
168  sigNonceList.push_back(*nonce);
169  // Ensure observed nonce list is at or below max nonce list size
170  if (m_options.maxNonceRecordCount >= 0 &&
171  sigNonceList.size() > static_cast<size_t>(m_options.maxNonceRecordCount)) {
172  BOOST_ASSERT(sigNonceList.size() == static_cast<size_t>(m_options.maxNonceRecordCount) + 1);
173  sigNonceList.pop_front();
174  }
175  });
176  BOOST_VERIFY(isOk);
177  }
178 
179  // Ensure record count is at or below max
180  if (m_options.maxRecordCount >= 0 &&
181  m_byLastRefreshed.size() > static_cast<size_t>(m_options.maxRecordCount)) {
182  BOOST_ASSERT(m_byLastRefreshed.size() == static_cast<size_t>(m_options.maxRecordCount) + 1);
183  m_byLastRefreshed.erase(m_byLastRefreshed.begin());
184  }
185 }
186 
187 } // inline namespace v2
188 } // namespace security
189 } // namespace ndn
Represents a Data packet.
Definition: data.hpp:38
Represents an Interest packet.
Definition: interest.hpp:50
optional< SignatureInfo > getSignatureInfo() const
Get the InterestSignatureInfo.
Definition: interest.cpp:549
Represents an absolute name.
Definition: name.hpp:46
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:348
provides a tag type for simple types
Definition: tag.hpp:59
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 Data &data, ValidationState &state)
extract KeyLocator.Name from a Data packet
@ 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:67
@ Interest
Definition: tlv.hpp:65
Definition: data.cpp:25