checker.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2021 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 
27 
28 #include <boost/algorithm/string/predicate.hpp>
29 
30 namespace ndn {
31 namespace security {
32 inline namespace v2 {
33 namespace validator_config {
34 
36  : m_sigType(sigType)
37 {
38 }
39 
40 Checker::Result::Result(std::string error)
41  : m_error(std::move(error))
42 {
43 }
44 
45 class Checker::NegativeResultBuilder
46 {
47 public:
48  template<typename T>
49  NegativeResultBuilder&
50  operator<<(const T& value)
51  {
52  m_ss << value;
53  return *this;
54  }
55 
56  operator Checker::Result() const
57  {
58  auto error = m_ss.str();
59  return Checker::Result(error.empty() ? "checker failed" : std::move(error));
60  }
61 
62 private:
63  std::ostringstream m_ss;
64 };
65 
66 Checker::NegativeResultBuilder
68 {
69  return NegativeResultBuilder();
70 }
71 
73 Checker::check(uint32_t pktType, tlv::SignatureTypeValue sigType, const Name& pktName, const Name& klName,
74  const ValidationState& state)
75 {
76  BOOST_ASSERT(pktType == tlv::Interest || pktType == tlv::Data);
77 
78  if (sigType != m_sigType) {
79  return reject() << "signature type does not match the checker "
80  << sigType << " != " << m_sigType;
81  }
82 
83  if (pktType == tlv::Interest) {
84  auto fmt = state.getTag<SignedInterestFormatTag>();
85  BOOST_ASSERT(fmt);
86 
87  if (*fmt == SignedInterestFormat::V03) {
88  // This check is redundant if parameter digest checking is enabled. However, the parameter
89  // digest checking can be disabled in API.
90  if (pktName.size() == 0 || pktName[-1].type() != tlv::ParametersSha256DigestComponent) {
91  return reject() << "ParametersSha256DigestComponent missing";
92  }
93  return checkNames(pktName.getPrefix(-1), klName);
94  }
95  else {
96  if (pktName.size() < signed_interest::MIN_SIZE)
97  return reject() << "name too short";
98 
99  return checkNames(pktName.getPrefix(-signed_interest::MIN_SIZE), klName);
100  }
101  }
102  else {
103  return checkNames(pktName, klName);
104  }
105 }
106 
108 Checker::checkNames(const Name& pktName, const Name& klName)
109 {
110  return accept();
111 }
112 
114  : Checker(sigType)
115  , m_name(name)
116  , m_relation(relation)
117 {
118 }
119 
121 NameRelationChecker::checkNames(const Name& pktName, const Name& klName)
122 {
123  // pktName not used in this check
124  Name identity = extractIdentityNameFromKeyLocator(klName);
125  if (checkNameRelation(m_relation, m_name, identity)) {
126  return accept();
127  }
128 
129  return reject() << "identity " << identity << " and packet name do not satisfy "
130  << m_relation << " relation";
131 }
132 
134  : Checker(sigType)
135  , m_regex(regex)
136 {
137 }
138 
140 RegexChecker::checkNames(const Name& pktName, const Name& klName)
141 {
142  if (m_regex.match(klName)) {
143  return accept();
144  }
145 
146  return reject() << "KeyLocator does not match regex " << m_regex;
147 }
148 
150  const std::string& pktNameExpr, const std::string pktNameExpand,
151  const std::string& klNameExpr, const std::string klNameExpand,
152  const NameRelation& hyperRelation)
153  : Checker(sigType)
154  , m_hyperPRegex(pktNameExpr, pktNameExpand)
155  , m_hyperKRegex(klNameExpr, klNameExpand)
156  , m_hyperRelation(hyperRelation)
157 {
158 }
159 
161 HyperRelationChecker::checkNames(const Name& pktName, const Name& klName)
162 {
163  if (!m_hyperPRegex.match(pktName)) {
164  return reject() << "packet name does not match p-regex " << m_hyperPRegex;
165  }
166 
167  if (!m_hyperKRegex.match(klName)) {
168  return reject() << "KeyLocator does not match k-regex " << m_hyperKRegex;
169  }
170 
171  auto kExpand = m_hyperKRegex.expand();
172  auto pExpand = m_hyperPRegex.expand();
173  if (checkNameRelation(m_hyperRelation, kExpand, pExpand)) {
174  return accept();
175  }
176 
177  return reject() << "expanded names " << kExpand << " and " << pExpand
178  << " do not satisfy " << m_hyperRelation << " relation";
179 }
180 
181 unique_ptr<Checker>
182 Checker::create(const ConfigSection& configSection, const std::string& configFilename)
183 {
184  auto propertyIt = configSection.begin();
185 
186  // Get checker.type
187  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) {
188  NDN_THROW(Error("Expecting <checker.type>"));
189  }
190 
191  std::string type = propertyIt->second.data();
192  if (boost::iequals(type, "customized")) {
193  return createCustomizedChecker(configSection, configFilename);
194  }
195  else if (boost::iequals(type, "hierarchical")) {
196  return createHierarchicalChecker(configSection, configFilename);
197  }
198  else {
199  NDN_THROW(Error("Unrecognized <checker.type>: " + type));
200  }
201 }
202 
204 parseSigType(const std::string& value)
205 {
206  if (boost::iequals(value, "rsa-sha256")) {
208  }
209  else if (boost::iequals(value, "ecdsa-sha256")) {
211  }
212  // TODO: uncomment when HMAC logic is defined/implemented
213  // else if (boost::iequals(value, "hmac-sha256")) {
214  // return tlv::SignatureHmacWithSha256;
215  // }
216  else if (boost::iequals(value, "sha256")) {
217  return tlv::DigestSha256;
218  }
219  else {
220  NDN_THROW(Error("Unrecognized value of <checker.sig-type>: " + value));
221  }
222 }
223 
224 unique_ptr<Checker>
225 Checker::createCustomizedChecker(const ConfigSection& configSection,
226  const std::string& configFilename)
227 {
228  auto propertyIt = configSection.begin();
229  propertyIt++;
230 
231  // assume that checker by default is for ecdsa-sha256, unless explicitly specified
232  auto sigType = tlv::SignatureSha256WithEcdsa;
233 
234  if (propertyIt != configSection.end() && boost::iequals(propertyIt->first, "sig-type")) {
235  sigType = parseSigType(propertyIt->second.data());
236  propertyIt++;
237  }
238 
239  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator")) {
240  if (sigType == tlv::DigestSha256) {
241  // for sha256, key-locator is optional
242  return make_unique<Checker>(sigType);
243  }
244  NDN_THROW(Error("Expecting <checker.key-locator>"));
245  }
246 
247  auto checker = createKeyLocatorChecker(sigType, propertyIt->second, configFilename);
248  propertyIt++;
249 
250  if (propertyIt != configSection.end()) {
251  NDN_THROW(Error("Expecting end of <checker>"));
252  }
253  return checker;
254 }
255 
256 unique_ptr<Checker>
257 Checker::createHierarchicalChecker(const ConfigSection& configSection,
258  const std::string& configFilename)
259 {
260  auto propertyIt = configSection.begin();
261  propertyIt++;
262 
263  // assume that checker by default is for ecdsa-sha256, unless explicitly specificied
264  auto sigType = tlv::SignatureSha256WithEcdsa;
265 
266  if (propertyIt != configSection.end() && boost::iequals(propertyIt->first, "sig-type")) {
267  sigType = parseSigType(propertyIt->second.data());
268  propertyIt++;
269  }
270 
271  if (propertyIt != configSection.end()) {
272  NDN_THROW(Error("Expecting end of <checker>"));
273  }
274  return make_unique<HyperRelationChecker>(sigType,
275  "^(<>*)$", "\\1",
276  "^(<>*)<KEY><>{1,3}$", "\\1",
278 }
279 
280 unique_ptr<Checker>
281 Checker::createKeyLocatorChecker(tlv::SignatureTypeValue sigType,
282  const ConfigSection& configSection, const std::string& configFilename)
283 {
284  auto propertyIt = configSection.begin();
285 
286  // Get checker.key-locator.type
287  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
288  NDN_THROW(Error("Expecting <checker.key-locator.type>"));
289 
290  std::string type = propertyIt->second.data();
291  if (boost::iequals(type, "name"))
292  return createKeyLocatorNameChecker(sigType, configSection, configFilename);
293  else
294  NDN_THROW(Error("Unrecognized <checker.key-locator.type>: " + type));
295 }
296 
297 unique_ptr<Checker>
298 Checker::createKeyLocatorNameChecker(tlv::SignatureTypeValue sigType,
299  const ConfigSection& configSection, const std::string& configFilename)
300 {
301  auto propertyIt = configSection.begin();
302  propertyIt++;
303 
304  if (propertyIt == configSection.end())
305  NDN_THROW(Error("Unexpected end of <checker.key-locator>"));
306 
307  if (boost::iequals(propertyIt->first, "name")) {
308  Name name;
309  try {
310  name = Name(propertyIt->second.data());
311  }
312  catch (const Name::Error&) {
313  NDN_THROW_NESTED(Error("Invalid <checker.key-locator.name>: " + propertyIt->second.data()));
314  }
315  propertyIt++;
316 
317  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "relation")) {
318  NDN_THROW(Error("Expecting <checker.key-locator.relation>"));
319  }
320 
321  std::string relationString = propertyIt->second.data();
322  propertyIt++;
323 
324  NameRelation relation = getNameRelationFromString(relationString);
325 
326  if (propertyIt != configSection.end()) {
327  NDN_THROW(Error("Expecting end of <checker.key-locator>"));
328  }
329  return make_unique<NameRelationChecker>(sigType, name, relation);
330  }
331  else if (boost::iequals(propertyIt->first, "regex")) {
332  std::string regexString = propertyIt->second.data();
333  propertyIt++;
334 
335  if (propertyIt != configSection.end()) {
336  NDN_THROW(Error("Expecting end of <checker.key-locator>"));
337  }
338 
339  try {
340  return make_unique<RegexChecker>(sigType, Regex(regexString));
341  }
342  catch (const Regex::Error&) {
343  NDN_THROW_NESTED(Error("Invalid <checker.key-locator.regex>: " + regexString));
344  }
345  }
346  else if (boost::iequals(propertyIt->first, "hyper-relation")) {
347  const ConfigSection& hSection = propertyIt->second;
348  auto hPropertyIt = hSection.begin();
349 
350  // Get k-regex
351  if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "k-regex")) {
352  NDN_THROW(Error("Expecting <checker.key-locator.hyper-relation.k-regex>"));
353  }
354 
355  std::string kRegex = hPropertyIt->second.data();
356  hPropertyIt++;
357 
358  // Get k-expand
359  if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "k-expand")) {
360  NDN_THROW(Error("Expecting <checker.key-locator.hyper-relation.k-expand>"));
361  }
362 
363  std::string kExpand = hPropertyIt->second.data();
364  hPropertyIt++;
365 
366  // Get h-relation
367  if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "h-relation")) {
368  NDN_THROW(Error("Expecting <checker.key-locator.hyper-relation.h-relation>"));
369  }
370 
371  std::string hRelation = hPropertyIt->second.data();
372  hPropertyIt++;
373 
374  // Get p-regex
375  if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "p-regex")) {
376  NDN_THROW(Error("Expecting <checker.key-locator.hyper-relation.p-regex>"));
377  }
378 
379  std::string pRegex = hPropertyIt->second.data();
380  hPropertyIt++;
381 
382  // Get p-expand
383  if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "p-expand")) {
384  NDN_THROW(Error("Expecting <checker.key-locator.hyper-relation.p-expand>"));
385  }
386 
387  std::string pExpand = hPropertyIt->second.data();
388  hPropertyIt++;
389 
390  if (hPropertyIt != hSection.end()) {
391  NDN_THROW(Error("Expecting end of <checker.key-locator.hyper-relation>"));
392  }
393 
394  NameRelation relation = getNameRelationFromString(hRelation);
395  try {
396  return make_unique<HyperRelationChecker>(sigType, pRegex, pExpand, kRegex, kExpand, relation);
397  }
398  catch (const Regex::Error&) {
399  NDN_THROW_NESTED(Error("Invalid regex for <key-locator.hyper-relation>"));
400  }
401  }
402  else {
403  NDN_THROW(Error("Unrecognized <checker.key-locator>: " + propertyIt->first));
404  }
405 }
406 
407 } // namespace validator_config
408 } // inline namespace v2
409 } // namespace security
410 } // namespace ndn
Represents an absolute name.
Definition: name.hpp:44
name::Component::Error Error
Definition: name.hpp:46
PartialName getPrefix(ssize_t nComponents) const
Returns a prefix of the name.
Definition: name.hpp:216
size_t size() const noexcept
Returns the number of components.
Definition: name.hpp:155
bool match(const Name &name)
virtual Name expand(const std::string &expand="")
Provides a tag type for simple types.
Definition: tag.hpp:56
shared_ptr< T > getTag() const
Get a tag item.
Definition: tag-host.hpp:67
static NegativeResultBuilder reject()
Definition: checker.cpp:67
Checker(tlv::SignatureTypeValue sigType)
Definition: checker.cpp:35
Result check(uint32_t pktType, tlv::SignatureTypeValue sigType, const Name &pktName, const Name &klName, const ValidationState &state)
Check if packet name and KeyLocator satisfy the checker's conditions.
Definition: checker.cpp:73
static unique_ptr< Checker > create(const ConfigSection &configSection, const std::string &configFilename)
Create a checker from configuration section.
Definition: checker.cpp:182
virtual Result checkNames(const Name &pktName, const Name &klName)
Base version of name checking.
Definition: checker.cpp:108
Result checkNames(const Name &pktName, const Name &klName) override
Base version of name checking.
Definition: checker.cpp:161
HyperRelationChecker(tlv::SignatureTypeValue sigType, const std::string &pktNameExpr, const std::string pktNameExpand, const std::string &klNameExpr, const std::string klNameExpand, const NameRelation &hyperRelation)
Definition: checker.cpp:149
Result checkNames(const Name &pktName, const Name &klName) override
Base version of name checking.
Definition: checker.cpp:121
NameRelationChecker(tlv::SignatureTypeValue sigType, const Name &name, const NameRelation &relation)
Definition: checker.cpp:113
RegexChecker(tlv::SignatureTypeValue sigType, const Regex &regex)
Definition: checker.cpp:133
Result checkNames(const Name &pktName, const Name &klName) override
Base version of name checking.
Definition: checker.cpp:140
#define NDN_THROW_NESTED(e)
Definition: exception.hpp:71
#define NDN_THROW(e)
Definition: exception.hpp:61
boost::property_tree::ptree ConfigSection
Definition: common.hpp:36
std::ostream & operator<<(std::ostream &os, NameRelation relation)
bool checkNameRelation(NameRelation relation, const Name &name1, const Name &name2)
Check whether name1 and name2 satisfies relation.
NameRelation getNameRelationFromString(const std::string &relationString)
Convert relationString to NameRelation.
Name extractIdentityNameFromKeyLocator(const Name &keyLocator)
Extract identity name from key, version-less certificate, or certificate name.
@ V03
Sign Interest using Packet Specification v0.3 semantics.
const size_t MIN_SIZE
Minimum number of name components for an old-style Signed Interest.
@ Name
Definition: tlv.hpp:71
@ Data
Definition: tlv.hpp:69
@ ParametersSha256DigestComponent
Definition: tlv.hpp:74
@ Interest
Definition: tlv.hpp:68
SignatureTypeValue
SignatureType values.
Definition: tlv.hpp:127
@ SignatureSha256WithRsa
Definition: tlv.hpp:129
@ DigestSha256
Definition: tlv.hpp:128
@ SignatureSha256WithEcdsa
Definition: tlv.hpp:130
Definition: data.cpp:25
RegexTopMatcher Regex
Definition: regex.hpp:31