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