blob: 1ebcf0d340f452f07170670a70ceec10bdfcde1c [file] [log] [blame]
Yingdi Yu48e8c0c2014-03-19 12:01:55 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07003 * Copyright (c) 2013-2014, Regents of the University of California.
4 * All rights reserved.
5 *
6 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
7 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
8 *
9 * This file licensed under New BSD License. See COPYING for detailed information about
10 * ndn-cxx library copyright, permissions, and redistribution restrictions.
11 *
12 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070013 */
14
15#ifndef NDN_SECURITY_CONF_CHECKER_HPP
16#define NDN_SECURITY_CONF_CHECKER_HPP
17
18#include "key-locator-checker.hpp"
19#include "../../util/io.hpp"
20#include <boost/algorithm/string.hpp>
21
22#include "common.hpp"
23
24namespace ndn {
25namespace security {
26namespace conf {
27
28class Checker
29{
30public:
31 typedef function<void(const shared_ptr<const Interest>&)> OnInterestChecked;
32 typedef function<void(const shared_ptr<const Interest>&, const std::string&)> OnInterestCheckFailed;
33 typedef function<void(const shared_ptr<const Data>&)> OnDataChecked;
34 typedef function<void(const shared_ptr<const Data>&, const std::string&)> OnDataCheckFailed;
35
36
37 virtual
38 ~Checker()
39 {
40 }
41
42 /**
43 * @brief check if data satisfies condition defined in the specific checker implementation
44 *
45 * @param data Data packet
46 * @param onValidated Callback function which is called when data is immediately valid
47 * @param onValidationFailed Call function which is called when data is immediately invalid
48 * @return -1 if data is immediately invalid (onValidationFailed has been called)
49 * 1 if data is immediately valid (onValidated has been called)
50 * 0 if further signature verification is needed.
51 */
52 virtual int8_t
53 check(const Data& data,
54 const OnDataChecked& onValidated,
55 const OnDataCheckFailed& onValidationFailed) = 0;
56
57 /**
58 * @brief check if interest satisfies condition defined in the specific checker implementation
59 *
60 * @param interest Interest packet
61 * @param onValidated Callback function which is called when interest is immediately valid
62 * @param onValidationFailed Call function which is called when interest is immediately invalid
63 * @return -1 if interest is immediately invalid (onValidationFailed has been called)
64 * 1 if interest is immediately valid (onValidated has been called)
65 * 0 if further signature verification is needed.
66 */
67 virtual int8_t
68 check(const Interest& interest,
69 const OnInterestChecked& onValidated,
70 const OnInterestCheckFailed& onValidationFailed) = 0;
71};
72
73class CustomizedChecker : public Checker
74{
75 enum
76 {
77 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -070078 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070079 };
80
81public:
82 CustomizedChecker(uint32_t sigType,
83 shared_ptr<KeyLocatorChecker> keyLocatorChecker)
84 : m_sigType(sigType)
85 , m_keyLocatorChecker(keyLocatorChecker)
86 {
87 if (m_sigType == Signature::Sha256WithRsa && !static_cast<bool>(m_keyLocatorChecker))
88 throw Error("Strong signature requires KeyLocatorChecker");
89 }
90
91 virtual int8_t
92 check(const Data& data,
93 const OnDataChecked& onValidated,
94 const OnDataCheckFailed& onValidationFailed)
95 {
96 return check(data, data.getSignature(), onValidated, onValidationFailed);
97 }
98
99 virtual int8_t
100 check(const Interest& interest,
101 const OnInterestChecked& onValidated,
102 const OnInterestCheckFailed& onValidationFailed)
103 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700104 try
105 {
106 const Name& interestName = interest.getName();
107 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
108 interestName[INTEREST_SIG_VALUE].blockFromValue());
109 return check(interest, signature, onValidated, onValidationFailed);
110 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700111 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700112 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700113 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700114 return -1;
115 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700116 catch (Tlv::Error& e)
117 {
118 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
119 return -1;
120 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700121 }
122
123private:
124 template<class Packet, class OnValidated, class OnFailed>
125 int8_t
126 check(const Packet& packet, const Signature& signature,
127 const OnValidated& onValidated,
128 const OnFailed& onValidationFailed)
129 {
130 if (m_sigType != signature.getType())
131 {
132 onValidationFailed(packet.shared_from_this(),
133 "Signature type does not match: " +
134 boost::lexical_cast<std::string>(m_sigType) +
135 "!=" +
136 boost::lexical_cast<std::string>(signature.getType()));
137 return -1;
138 }
139
140 switch (signature.getType())
141 {
142 case Signature::Sha256WithRsa:
143 {
144 try
145 {
146 SignatureSha256WithRsa sig(signature);
147
148 std::string failInfo;
149 if (m_keyLocatorChecker->check(packet, sig.getKeyLocator(), failInfo))
150 return 0;
151 else
152 {
153 onValidationFailed(packet.shared_from_this(), failInfo);
154 return -1;
155 }
156 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700157 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700158 {
159 onValidationFailed(packet.shared_from_this(),
160 "Cannot decode Sha256WithRsa signature!");
161 return -1;
162 }
163 }
164 case Signature::Sha256:
165 return 0;
166 default:
167 {
168 onValidationFailed(packet.shared_from_this(),
169 "Unsupported signature type: " +
170 boost::lexical_cast<std::string>(signature.getType()));
171 return -1;
172 }
173 }
174 }
175
176private:
177 uint32_t m_sigType;
178 shared_ptr<KeyLocatorChecker> m_keyLocatorChecker;
179};
180
181class HierarchicalChecker : public CustomizedChecker
182{
183public:
184 HierarchicalChecker(uint32_t sigType)
185 : CustomizedChecker(sigType,
186 make_shared<HyperKeyLocatorNameChecker>("^(<>*)$", "\\1",
187 "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
188 "\\1\\2",
189 KeyLocatorChecker::RELATION_IS_PREFIX_OF))
190 {
191 }
192};
193
194class FixedSignerChecker : public Checker
195{
196 enum
197 {
198 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -0700199 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700200 };
201public:
202 FixedSignerChecker(uint32_t sigType,
203 const std::vector<shared_ptr<IdentityCertificate> >& signers)
204 : m_sigType(sigType)
205 {
206 for (std::vector<shared_ptr<IdentityCertificate> >::const_iterator it = signers.begin();
207 it != signers.end(); it++)
208 m_signers[(*it)->getName().getPrefix(-1)] = (*it);
209 }
210
211 virtual int8_t
212 check(const Data& data,
213 const OnDataChecked& onValidated,
214 const OnDataCheckFailed& onValidationFailed)
215 {
216 return check(data, data.getSignature(), onValidated, onValidationFailed);
217 }
218
219 virtual int8_t
220 check(const Interest& interest,
221 const OnInterestChecked& onValidated,
222 const OnInterestCheckFailed& onValidationFailed)
223 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700224 try
225 {
226 const Name& interestName = interest.getName();
227 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
228 interestName[INTEREST_SIG_VALUE].blockFromValue());
229 return check(interest, signature, onValidated, onValidationFailed);
230 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700231 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700232 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700233 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700234 return -1;
235 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700236 catch (Tlv::Error& e)
237 {
238 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
239 return -1;
240 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700241 }
242
243private:
244 template<class Packet, class OnValidated, class OnFailed>
245 int8_t
246 check(const Packet& packet, const Signature& signature,
247 const OnValidated& onValidated,
248 const OnFailed& onValidationFailed)
249 {
250 if (m_sigType != signature.getType())
251 {
252 onValidationFailed(packet.shared_from_this(),
253 "Signature type does not match: "
254 + boost::lexical_cast<std::string>(m_sigType)
255 + "!="
256 + boost::lexical_cast<std::string>(signature.getType()));
257 return -1;
258 }
259
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700260 switch (signature.getType())
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700261 {
262 case Signature::Sha256WithRsa:
263 {
264 try
265 {
266 SignatureSha256WithRsa sig(signature);
267
268 const Name& keyLocatorName = sig.getKeyLocator().getName();
269 if (m_signers.find(keyLocatorName) == m_signers.end())
270 {
271 onValidationFailed(packet.shared_from_this(),
272 "Signer is not in the fixed signer list: "
273 + keyLocatorName.toUri());
274 return -1;
275 }
276
277 if (Validator::verifySignature(packet, sig,
278 m_signers[keyLocatorName]->getPublicKeyInfo()))
279 {
280 onValidated(packet.shared_from_this());
281 return 1;
282 }
283 else
284 {
285 onValidationFailed(packet.shared_from_this(),
286 "Signature cannot be validated!");
287 return -1;
288 }
289 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700290 catch (KeyLocator::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700291 {
292 onValidationFailed(packet.shared_from_this(),
293 "KeyLocator does not have name!");
294 return -1;
295 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700296 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700297 {
298 onValidationFailed(packet.shared_from_this(),
299 "Cannot decode signature!");
300 return -1;
301 }
302 }
303 case Signature::Sha256:
304 {
305 onValidationFailed(packet.shared_from_this(),
306 "FixedSigner does not allow Sha256 signature type!");
307 return -1;
308 }
309 default:
310 {
311 onValidationFailed(packet.shared_from_this(),
312 "Unsupported signature type: "
313 + boost::lexical_cast<std::string>(signature.getType()));
314 return -1;
315 }
316 }
317 }
318
319private:
320 typedef std::map<Name, shared_ptr<IdentityCertificate> > SignerList;
321
322 uint32_t m_sigType;
323 SignerList m_signers;
324};
325
326class CheckerFactory
327{
328public:
329 /**
330 * @brief create a checker from configuration file.
331 *
332 * @param configSection The section containing the definition of checker.
333 * @param configFilename The configuration file name.
334 * @return a shared pointer to the created checker.
335 */
336 static shared_ptr<Checker>
337 create(const ConfigSection& configSection, const std::string& configFilename)
338 {
339 ConfigSection::const_iterator propertyIt = configSection.begin();
340
341 // Get checker.type
342 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
343 throw Error("Expect <checker.type>!");
344
345 std::string type = propertyIt->second.data();
346
347 if (boost::iequals(type, "customized"))
348 return createCustomizedChecker(configSection, configFilename);
349 else if (boost::iequals(type, "hierarchical"))
350 return createHierarchicalChecker(configSection, configFilename);
351 else if (boost::iequals(type, "fixed-signer"))
352 return createFixedSignerChecker(configSection, configFilename);
353 else
354 throw Error("Unsupported checker type: " + type);
355 }
356
357private:
358 static shared_ptr<Checker>
359 createCustomizedChecker(const ConfigSection& configSection,
360 const std::string& configFilename)
361 {
362 ConfigSection::const_iterator propertyIt = configSection.begin();
363 propertyIt++;
364
365 // Get checker.sig-type
366 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
367 throw Error("Expect <checker.sig-type>!");
368
369 std::string sigType = propertyIt->second.data();
370 propertyIt++;
371
372 // Get checker.key-locator
373 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator"))
374 throw Error("Expect <checker.key-locator>!");
375
376 shared_ptr<KeyLocatorChecker> keyLocatorChecker =
377 KeyLocatorCheckerFactory::create(propertyIt->second, configFilename);
378 propertyIt++;
379
380 if (propertyIt != configSection.end())
381 throw Error("Expect the end of checker!");
382
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700383 return make_shared<CustomizedChecker>(getSigType(sigType), keyLocatorChecker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700384 }
385
386 static shared_ptr<Checker>
387 createHierarchicalChecker(const ConfigSection& configSection,
388 const std::string& configFilename)
389 {
390 ConfigSection::const_iterator propertyIt = configSection.begin();
391 propertyIt++;
392
393 // Get checker.sig-type
394 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
395 throw Error("Expect <checker.sig-type>!");
396
397 std::string sigType = propertyIt->second.data();
398 propertyIt++;
399
400 if (propertyIt != configSection.end())
401 throw Error("Expect the end of checker!");
402
Alexander Afanasyevb67090a2014-04-29 22:31:01 -0700403 return make_shared<HierarchicalChecker>(getSigType(sigType));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700404 }
405
406 static shared_ptr<Checker>
407 createFixedSignerChecker(const ConfigSection& configSection,
408 const std::string& configFilename)
409 {
410 ConfigSection::const_iterator propertyIt = configSection.begin();
411 propertyIt++;
412
413 // Get checker.sig-type
414 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
415 throw Error("Expect <checker.sig-type>!");
416
417 std::string sigType = propertyIt->second.data();
418 propertyIt++;
419
420 std::vector<shared_ptr<IdentityCertificate> > signers;
421 for (; propertyIt != configSection.end(); propertyIt++)
422 {
423 if (!boost::iequals(propertyIt->first, "signer"))
424 throw Error("Expect <checker.signer> but get <checker."
425 + propertyIt->first + ">");
426
427 signers.push_back(getSigner(propertyIt->second, configFilename));
428 }
429
430 if (propertyIt != configSection.end())
431 throw Error("Expect the end of checker!");
432
433 return shared_ptr<FixedSignerChecker>(new FixedSignerChecker(getSigType(sigType),
434 signers));
435 }
436
437 static shared_ptr<IdentityCertificate>
438 getSigner(const ConfigSection& configSection, const std::string& configFilename)
439 {
440 using namespace boost::filesystem;
441
442 ConfigSection::const_iterator propertyIt = configSection.begin();
443
444 // Get checker.signer.type
445 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
446 throw Error("Expect <checker.signer.type>!");
447
448 std::string type = propertyIt->second.data();
449 propertyIt++;
450
451 if (boost::iequals(type, "file"))
452 {
453 // Get checker.signer.file-name
454 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
455 throw Error("Expect <checker.signer.file-name>!");
456
457 path certfilePath = absolute(propertyIt->second.data(),
458 path(configFilename).parent_path());
459 propertyIt++;
460
461 if (propertyIt != configSection.end())
462 throw Error("Expect the end of checker.signer");
463
464 shared_ptr<IdentityCertificate> idCert
465 = io::load<IdentityCertificate>(certfilePath.c_str());
466
467 if (static_cast<bool>(idCert))
468 return idCert;
469 else
470 throw Error("Cannot read certificate from file: "
471 + certfilePath.native());
472 }
473 else if (boost::iequals(type, "base64"))
474 {
475 // Get checker.signer.base64-string
476 if (propertyIt == configSection.end() ||
477 !boost::iequals(propertyIt->first, "base64-string"))
478 throw Error("Expect <checker.signer.base64-string>!");
479
480 std::stringstream ss(propertyIt->second.data());
481 propertyIt++;
482
483 if (propertyIt != configSection.end())
484 throw Error("Expect the end of checker.signer");
485
486 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
487
488 if (static_cast<bool>(idCert))
489 return idCert;
490 else
491 throw Error("Cannot decode certificate from string");
492 }
493 else
494 throw Error("Unsupported checker.signer type: " + type);
495 }
496
497 static int32_t
498 getSigType(const std::string& sigType)
499 {
500 if (boost::iequals(sigType, "rsa-sha256"))
501 return Signature::Sha256WithRsa;
502 else if (boost::iequals(sigType, "sha256"))
503 return Signature::Sha256;
504 else
505 return -1;
506 }
507};
508
509} // namespace conf
510} // namespace security
511} // namespace ndn
512
513#endif // NDN_SECURITY_SEC_CONF_RULE_SIGNER_HPP