blob: 67ab3b114e74b9eeeab0fe95538ca068ca77082f [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
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070018#include "common.hpp"
19
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070020#include "key-locator-checker.hpp"
21#include "../../util/io.hpp"
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070022
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070023#include <boost/algorithm/string.hpp>
24#include <boost/filesystem.hpp>
25#include <boost/lexical_cast.hpp>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070026
27namespace ndn {
28namespace security {
29namespace conf {
30
31class Checker
32{
33public:
34 typedef function<void(const shared_ptr<const Interest>&)> OnInterestChecked;
35 typedef function<void(const shared_ptr<const Interest>&, const std::string&)> OnInterestCheckFailed;
36 typedef function<void(const shared_ptr<const Data>&)> OnDataChecked;
37 typedef function<void(const shared_ptr<const Data>&, const std::string&)> OnDataCheckFailed;
38
39
40 virtual
41 ~Checker()
42 {
43 }
44
45 /**
46 * @brief check if data satisfies condition defined in the specific checker implementation
47 *
48 * @param data Data packet
49 * @param onValidated Callback function which is called when data is immediately valid
50 * @param onValidationFailed Call function which is called when data is immediately invalid
51 * @return -1 if data is immediately invalid (onValidationFailed has been called)
52 * 1 if data is immediately valid (onValidated has been called)
53 * 0 if further signature verification is needed.
54 */
55 virtual int8_t
56 check(const Data& data,
57 const OnDataChecked& onValidated,
58 const OnDataCheckFailed& onValidationFailed) = 0;
59
60 /**
61 * @brief check if interest satisfies condition defined in the specific checker implementation
62 *
63 * @param interest Interest packet
64 * @param onValidated Callback function which is called when interest is immediately valid
65 * @param onValidationFailed Call function which is called when interest is immediately invalid
66 * @return -1 if interest is immediately invalid (onValidationFailed has been called)
67 * 1 if interest is immediately valid (onValidated has been called)
68 * 0 if further signature verification is needed.
69 */
70 virtual int8_t
71 check(const Interest& interest,
72 const OnInterestChecked& onValidated,
73 const OnInterestCheckFailed& onValidationFailed) = 0;
74};
75
76class CustomizedChecker : public Checker
77{
78 enum
79 {
80 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -070081 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070082 };
83
84public:
85 CustomizedChecker(uint32_t sigType,
86 shared_ptr<KeyLocatorChecker> keyLocatorChecker)
87 : m_sigType(sigType)
88 , m_keyLocatorChecker(keyLocatorChecker)
89 {
90 if (m_sigType == Signature::Sha256WithRsa && !static_cast<bool>(m_keyLocatorChecker))
91 throw Error("Strong signature requires KeyLocatorChecker");
92 }
93
94 virtual int8_t
95 check(const Data& data,
96 const OnDataChecked& onValidated,
97 const OnDataCheckFailed& onValidationFailed)
98 {
99 return check(data, data.getSignature(), onValidated, onValidationFailed);
100 }
101
102 virtual int8_t
103 check(const Interest& interest,
104 const OnInterestChecked& onValidated,
105 const OnInterestCheckFailed& onValidationFailed)
106 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700107 try
108 {
109 const Name& interestName = interest.getName();
110 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
111 interestName[INTEREST_SIG_VALUE].blockFromValue());
112 return check(interest, signature, onValidated, onValidationFailed);
113 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700114 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700115 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700116 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700117 return -1;
118 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700119 catch (Tlv::Error& e)
120 {
121 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
122 return -1;
123 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700124 }
125
126private:
127 template<class Packet, class OnValidated, class OnFailed>
128 int8_t
129 check(const Packet& packet, const Signature& signature,
130 const OnValidated& onValidated,
131 const OnFailed& onValidationFailed)
132 {
133 if (m_sigType != signature.getType())
134 {
135 onValidationFailed(packet.shared_from_this(),
136 "Signature type does not match: " +
137 boost::lexical_cast<std::string>(m_sigType) +
138 "!=" +
139 boost::lexical_cast<std::string>(signature.getType()));
140 return -1;
141 }
142
143 switch (signature.getType())
144 {
145 case Signature::Sha256WithRsa:
146 {
147 try
148 {
149 SignatureSha256WithRsa sig(signature);
150
151 std::string failInfo;
152 if (m_keyLocatorChecker->check(packet, sig.getKeyLocator(), failInfo))
153 return 0;
154 else
155 {
156 onValidationFailed(packet.shared_from_this(), failInfo);
157 return -1;
158 }
159 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700160 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700161 {
162 onValidationFailed(packet.shared_from_this(),
163 "Cannot decode Sha256WithRsa signature!");
164 return -1;
165 }
166 }
167 case Signature::Sha256:
168 return 0;
169 default:
170 {
171 onValidationFailed(packet.shared_from_this(),
172 "Unsupported signature type: " +
173 boost::lexical_cast<std::string>(signature.getType()));
174 return -1;
175 }
176 }
177 }
178
179private:
180 uint32_t m_sigType;
181 shared_ptr<KeyLocatorChecker> m_keyLocatorChecker;
182};
183
184class HierarchicalChecker : public CustomizedChecker
185{
186public:
187 HierarchicalChecker(uint32_t sigType)
188 : CustomizedChecker(sigType,
189 make_shared<HyperKeyLocatorNameChecker>("^(<>*)$", "\\1",
190 "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
191 "\\1\\2",
192 KeyLocatorChecker::RELATION_IS_PREFIX_OF))
193 {
194 }
195};
196
197class FixedSignerChecker : public Checker
198{
199 enum
200 {
201 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -0700202 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700203 };
204public:
205 FixedSignerChecker(uint32_t sigType,
206 const std::vector<shared_ptr<IdentityCertificate> >& signers)
207 : m_sigType(sigType)
208 {
209 for (std::vector<shared_ptr<IdentityCertificate> >::const_iterator it = signers.begin();
210 it != signers.end(); it++)
211 m_signers[(*it)->getName().getPrefix(-1)] = (*it);
212 }
213
214 virtual int8_t
215 check(const Data& data,
216 const OnDataChecked& onValidated,
217 const OnDataCheckFailed& onValidationFailed)
218 {
219 return check(data, data.getSignature(), onValidated, onValidationFailed);
220 }
221
222 virtual int8_t
223 check(const Interest& interest,
224 const OnInterestChecked& onValidated,
225 const OnInterestCheckFailed& onValidationFailed)
226 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700227 try
228 {
229 const Name& interestName = interest.getName();
230 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
231 interestName[INTEREST_SIG_VALUE].blockFromValue());
232 return check(interest, signature, onValidated, onValidationFailed);
233 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700234 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700235 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700236 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700237 return -1;
238 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700239 catch (Tlv::Error& e)
240 {
241 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
242 return -1;
243 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700244 }
245
246private:
247 template<class Packet, class OnValidated, class OnFailed>
248 int8_t
249 check(const Packet& packet, const Signature& signature,
250 const OnValidated& onValidated,
251 const OnFailed& onValidationFailed)
252 {
253 if (m_sigType != signature.getType())
254 {
255 onValidationFailed(packet.shared_from_this(),
256 "Signature type does not match: "
257 + boost::lexical_cast<std::string>(m_sigType)
258 + "!="
259 + boost::lexical_cast<std::string>(signature.getType()));
260 return -1;
261 }
262
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700263 switch (signature.getType())
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700264 {
265 case Signature::Sha256WithRsa:
266 {
267 try
268 {
269 SignatureSha256WithRsa sig(signature);
270
271 const Name& keyLocatorName = sig.getKeyLocator().getName();
272 if (m_signers.find(keyLocatorName) == m_signers.end())
273 {
274 onValidationFailed(packet.shared_from_this(),
275 "Signer is not in the fixed signer list: "
276 + keyLocatorName.toUri());
277 return -1;
278 }
279
280 if (Validator::verifySignature(packet, sig,
281 m_signers[keyLocatorName]->getPublicKeyInfo()))
282 {
283 onValidated(packet.shared_from_this());
284 return 1;
285 }
286 else
287 {
288 onValidationFailed(packet.shared_from_this(),
289 "Signature cannot be validated!");
290 return -1;
291 }
292 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700293 catch (KeyLocator::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700294 {
295 onValidationFailed(packet.shared_from_this(),
296 "KeyLocator does not have name!");
297 return -1;
298 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700299 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700300 {
301 onValidationFailed(packet.shared_from_this(),
302 "Cannot decode signature!");
303 return -1;
304 }
305 }
306 case Signature::Sha256:
307 {
308 onValidationFailed(packet.shared_from_this(),
309 "FixedSigner does not allow Sha256 signature type!");
310 return -1;
311 }
312 default:
313 {
314 onValidationFailed(packet.shared_from_this(),
315 "Unsupported signature type: "
316 + boost::lexical_cast<std::string>(signature.getType()));
317 return -1;
318 }
319 }
320 }
321
322private:
323 typedef std::map<Name, shared_ptr<IdentityCertificate> > SignerList;
324
325 uint32_t m_sigType;
326 SignerList m_signers;
327};
328
329class CheckerFactory
330{
331public:
332 /**
333 * @brief create a checker from configuration file.
334 *
335 * @param configSection The section containing the definition of checker.
336 * @param configFilename The configuration file name.
337 * @return a shared pointer to the created checker.
338 */
339 static shared_ptr<Checker>
340 create(const ConfigSection& configSection, const std::string& configFilename)
341 {
342 ConfigSection::const_iterator propertyIt = configSection.begin();
343
344 // Get checker.type
345 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
346 throw Error("Expect <checker.type>!");
347
348 std::string type = propertyIt->second.data();
349
350 if (boost::iequals(type, "customized"))
351 return createCustomizedChecker(configSection, configFilename);
352 else if (boost::iequals(type, "hierarchical"))
353 return createHierarchicalChecker(configSection, configFilename);
354 else if (boost::iequals(type, "fixed-signer"))
355 return createFixedSignerChecker(configSection, configFilename);
356 else
357 throw Error("Unsupported checker type: " + type);
358 }
359
360private:
361 static shared_ptr<Checker>
362 createCustomizedChecker(const ConfigSection& configSection,
363 const std::string& configFilename)
364 {
365 ConfigSection::const_iterator propertyIt = configSection.begin();
366 propertyIt++;
367
368 // Get checker.sig-type
369 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
370 throw Error("Expect <checker.sig-type>!");
371
372 std::string sigType = propertyIt->second.data();
373 propertyIt++;
374
375 // Get checker.key-locator
376 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator"))
377 throw Error("Expect <checker.key-locator>!");
378
379 shared_ptr<KeyLocatorChecker> keyLocatorChecker =
380 KeyLocatorCheckerFactory::create(propertyIt->second, configFilename);
381 propertyIt++;
382
383 if (propertyIt != configSection.end())
384 throw Error("Expect the end of checker!");
385
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700386 return make_shared<CustomizedChecker>(getSigType(sigType), keyLocatorChecker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700387 }
388
389 static shared_ptr<Checker>
390 createHierarchicalChecker(const ConfigSection& configSection,
391 const std::string& configFilename)
392 {
393 ConfigSection::const_iterator propertyIt = configSection.begin();
394 propertyIt++;
395
396 // Get checker.sig-type
397 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
398 throw Error("Expect <checker.sig-type>!");
399
400 std::string sigType = propertyIt->second.data();
401 propertyIt++;
402
403 if (propertyIt != configSection.end())
404 throw Error("Expect the end of checker!");
405
Alexander Afanasyevb67090a2014-04-29 22:31:01 -0700406 return make_shared<HierarchicalChecker>(getSigType(sigType));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700407 }
408
409 static shared_ptr<Checker>
410 createFixedSignerChecker(const ConfigSection& configSection,
411 const std::string& configFilename)
412 {
413 ConfigSection::const_iterator propertyIt = configSection.begin();
414 propertyIt++;
415
416 // Get checker.sig-type
417 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
418 throw Error("Expect <checker.sig-type>!");
419
420 std::string sigType = propertyIt->second.data();
421 propertyIt++;
422
423 std::vector<shared_ptr<IdentityCertificate> > signers;
424 for (; propertyIt != configSection.end(); propertyIt++)
425 {
426 if (!boost::iequals(propertyIt->first, "signer"))
427 throw Error("Expect <checker.signer> but get <checker."
428 + propertyIt->first + ">");
429
430 signers.push_back(getSigner(propertyIt->second, configFilename));
431 }
432
433 if (propertyIt != configSection.end())
434 throw Error("Expect the end of checker!");
435
436 return shared_ptr<FixedSignerChecker>(new FixedSignerChecker(getSigType(sigType),
437 signers));
438 }
439
440 static shared_ptr<IdentityCertificate>
441 getSigner(const ConfigSection& configSection, const std::string& configFilename)
442 {
443 using namespace boost::filesystem;
444
445 ConfigSection::const_iterator propertyIt = configSection.begin();
446
447 // Get checker.signer.type
448 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
449 throw Error("Expect <checker.signer.type>!");
450
451 std::string type = propertyIt->second.data();
452 propertyIt++;
453
454 if (boost::iequals(type, "file"))
455 {
456 // Get checker.signer.file-name
457 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
458 throw Error("Expect <checker.signer.file-name>!");
459
460 path certfilePath = absolute(propertyIt->second.data(),
461 path(configFilename).parent_path());
462 propertyIt++;
463
464 if (propertyIt != configSection.end())
465 throw Error("Expect the end of checker.signer");
466
467 shared_ptr<IdentityCertificate> idCert
468 = io::load<IdentityCertificate>(certfilePath.c_str());
469
470 if (static_cast<bool>(idCert))
471 return idCert;
472 else
473 throw Error("Cannot read certificate from file: "
474 + certfilePath.native());
475 }
476 else if (boost::iequals(type, "base64"))
477 {
478 // Get checker.signer.base64-string
479 if (propertyIt == configSection.end() ||
480 !boost::iequals(propertyIt->first, "base64-string"))
481 throw Error("Expect <checker.signer.base64-string>!");
482
483 std::stringstream ss(propertyIt->second.data());
484 propertyIt++;
485
486 if (propertyIt != configSection.end())
487 throw Error("Expect the end of checker.signer");
488
489 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
490
491 if (static_cast<bool>(idCert))
492 return idCert;
493 else
494 throw Error("Cannot decode certificate from string");
495 }
496 else
497 throw Error("Unsupported checker.signer type: " + type);
498 }
499
500 static int32_t
501 getSigType(const std::string& sigType)
502 {
503 if (boost::iequals(sigType, "rsa-sha256"))
504 return Signature::Sha256WithRsa;
505 else if (boost::iequals(sigType, "sha256"))
506 return Signature::Sha256;
507 else
508 return -1;
509 }
510};
511
512} // namespace conf
513} // namespace security
514} // namespace ndn
515
516#endif // NDN_SECURITY_SEC_CONF_RULE_SIGNER_HPP