validation-policy-command-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_index(m_container.get<0>())
32  , m_queue(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.gracePeriod = std::max(m_options.gracePeriod, 0_ns);
40 }
41 
42 void
43 ValidationPolicyCommandInterest::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
44  const ValidationContinuation& continueValidation)
45 {
46  getInnerPolicy().checkPolicy(data, state, continueValidation);
47 }
48 
49 void
50 ValidationPolicyCommandInterest::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
51  const ValidationContinuation& continueValidation)
52 {
53  bool isOk;
54  Name keyName;
56  std::tie(isOk, keyName, timestamp) = parseCommandInterest(interest, state);
57  if (!isOk) {
58  return;
59  }
60 
61  if (!checkTimestamp(state, keyName, timestamp)) {
62  return;
63  }
64 
65  getInnerPolicy().checkPolicy(interest, state, continueValidation);
66 }
67 
68 std::tuple<bool, Name, time::system_clock::TimePoint>
69 ValidationPolicyCommandInterest::parseCommandInterest(const Interest& interest,
70  const shared_ptr<ValidationState>& state)
71 {
72  auto sigInfo = getSignatureInfo(interest, *state);
73  if (!state->getOutcome()) { // already failed
74  return std::make_tuple(false, Name(), time::system_clock::TimePoint{});
75  }
76 
78 
79  auto fmt = state->getTag<SignedInterestFormatTag>();
80  BOOST_ASSERT(fmt);
81  if (*fmt == SignedInterestFormat::V03) {
82  // SignatureTime is a hard requirement of this policy
83  // New apps should use the more flexible ValidationPolicySignedInterest (v0.3 only)
84  auto optionalTimestamp = sigInfo.getTime();
85  if (!optionalTimestamp) {
86  state->fail({ValidationError::POLICY_ERROR, "Signed Interest `" +
87  interest.getName().toUri() + "` lacks required SignatureTime element"});
88  return std::make_tuple(false, Name(), time::system_clock::TimePoint{});
89  }
90 
91  timestamp = *optionalTimestamp;
92  }
93  else {
94  const Name& name = interest.getName();
95  if (name.size() < command_interest::MIN_SIZE) {
96  state->fail({ValidationError::POLICY_ERROR,
97  "Command Interest name too short `" + interest.getName().toUri() + "`"});
98  return std::make_tuple(false, Name(), time::system_clock::TimePoint{});
99  }
100 
101  const auto& timestampComp = name.at(command_interest::POS_TIMESTAMP);
102  if (!timestampComp.isNumber()) {
103  state->fail({ValidationError::POLICY_ERROR, "Command Interest `" +
104  interest.getName().toUri() + "` lacks required timestamp component"});
105  return std::make_tuple(false, Name(), time::system_clock::TimePoint{});
106  }
107 
108  timestamp = time::fromUnixTimestamp(time::milliseconds(timestampComp.toNumber()));
109  }
110 
111  Name klName = getKeyLocatorName(sigInfo, *state);
112  if (!state->getOutcome()) { // already failed
113  return std::make_tuple(false, Name(), time::system_clock::TimePoint{});
114  }
115 
116  return std::make_tuple(true, klName, timestamp);
117 }
118 
119 void
120 ValidationPolicyCommandInterest::cleanup()
121 {
122  auto expiring = time::steady_clock::now() - m_options.recordLifetime;
123 
124  while ((!m_queue.empty() && m_queue.front().lastRefreshed <= expiring) ||
125  (m_options.maxRecords >= 0 &&
126  m_queue.size() > static_cast<size_t>(m_options.maxRecords))) {
127  m_queue.pop_front();
128  }
129 }
130 
131 bool
132 ValidationPolicyCommandInterest::checkTimestamp(const shared_ptr<ValidationState>& state,
133  const Name& keyName, time::system_clock::TimePoint timestamp)
134 {
135  this->cleanup();
136 
137  auto now = time::system_clock::now();
138 
139  if (timestamp < now - m_options.gracePeriod || timestamp > now + m_options.gracePeriod) {
140  state->fail({ValidationError::POLICY_ERROR,
141  "Timestamp is outside the grace period for key " + keyName.toUri()});
142  return false;
143  }
144 
145  auto it = m_index.find(keyName);
146  if (it != m_index.end()) {
147  if (timestamp <= it->timestamp) {
148  state->fail({ValidationError::POLICY_ERROR,
149  "Timestamp is reordered for key " + keyName.toUri()});
150  return false;
151  }
152  }
153 
154  auto interestState = dynamic_pointer_cast<InterestValidationState>(state);
155  BOOST_ASSERT(interestState != nullptr);
156  interestState->afterSuccess.connect([=] (const Interest&) { insertNewRecord(keyName, timestamp); });
157  return true;
158 }
159 
160 void
161 ValidationPolicyCommandInterest::insertNewRecord(const Name& keyName, time::system_clock::TimePoint timestamp)
162 {
163  // try to insert new record
164  auto now = time::steady_clock::now();
165  auto i = m_queue.end();
166  bool isNew = false;
167  LastTimestampRecord newRecord{keyName, timestamp, now};
168  std::tie(i, isNew) = m_queue.push_back(newRecord);
169 
170  if (!isNew) {
171  BOOST_ASSERT(i->keyName == keyName);
172 
173  // set lastRefreshed field, and move to queue tail
174  m_queue.erase(i);
175  isNew = m_queue.push_back(newRecord).second;
176  BOOST_VERIFY(isNew);
177  }
178 }
179 
180 } // inline namespace v2
181 } // namespace security
182 } // namespace ndn
Represents a Data packet.
Definition: data.hpp:39
Represents an Interest packet.
Definition: interest.hpp:50
const Name & getName() const noexcept
Definition: interest.hpp:173
Represents an absolute name.
Definition: name.hpp:44
const Component & at(ssize_t i) const
Returns an immutable reference to the component at the specified index, with bounds checking.
Definition: name.cpp:171
@ POLICY_ERROR
The packet violates the validation rules enforced by the policy.
time::nanoseconds recordLifetime
Max lifetime of a last timestamp record.
ssize_t maxRecords
Max number of distinct public keys of which to record the last timestamp.
void checkPolicy(const Data &data, const shared_ptr< ValidationState > &state, const ValidationContinuation &continueValidation) override
Check data against the policy.
ValidationPolicyCommandInterest(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
time_point TimePoint
Definition: time.hpp:203
#define NDN_THROW(e)
Definition: exception.hpp:61
const size_t MIN_SIZE
Minimum number of name components for a Command Interest.
const ssize_t POS_TIMESTAMP
SimpleTag< SignedInterestFormat, 1002 > SignedInterestFormatTag
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.
boost::chrono::milliseconds milliseconds
Definition: time.hpp:48
system_clock::time_point fromUnixTimestamp(milliseconds duration)
Convert UNIX timestamp to system_clock::time_point.
Definition: time.cpp:119
@ Name
Definition: tlv.hpp:71
@ Interest
Definition: tlv.hpp:68
Definition: data.cpp:25