blob: cb56d85e566a572a6690fa30fb46bb4e23e2327f [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
383 return make_shared<CustomizedChecker>(boost::cref(getSigType(sigType)),
384 boost::cref(keyLocatorChecker));
385 }
386
387 static shared_ptr<Checker>
388 createHierarchicalChecker(const ConfigSection& configSection,
389 const std::string& configFilename)
390 {
391 ConfigSection::const_iterator propertyIt = configSection.begin();
392 propertyIt++;
393
394 // Get checker.sig-type
395 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
396 throw Error("Expect <checker.sig-type>!");
397
398 std::string sigType = propertyIt->second.data();
399 propertyIt++;
400
401 if (propertyIt != configSection.end())
402 throw Error("Expect the end of checker!");
403
404 return make_shared<HierarchicalChecker>(boost::cref(getSigType(sigType)));
405 }
406
407 static shared_ptr<Checker>
408 createFixedSignerChecker(const ConfigSection& configSection,
409 const std::string& configFilename)
410 {
411 ConfigSection::const_iterator propertyIt = configSection.begin();
412 propertyIt++;
413
414 // Get checker.sig-type
415 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
416 throw Error("Expect <checker.sig-type>!");
417
418 std::string sigType = propertyIt->second.data();
419 propertyIt++;
420
421 std::vector<shared_ptr<IdentityCertificate> > signers;
422 for (; propertyIt != configSection.end(); propertyIt++)
423 {
424 if (!boost::iequals(propertyIt->first, "signer"))
425 throw Error("Expect <checker.signer> but get <checker."
426 + propertyIt->first + ">");
427
428 signers.push_back(getSigner(propertyIt->second, configFilename));
429 }
430
431 if (propertyIt != configSection.end())
432 throw Error("Expect the end of checker!");
433
434 return shared_ptr<FixedSignerChecker>(new FixedSignerChecker(getSigType(sigType),
435 signers));
436 }
437
438 static shared_ptr<IdentityCertificate>
439 getSigner(const ConfigSection& configSection, const std::string& configFilename)
440 {
441 using namespace boost::filesystem;
442
443 ConfigSection::const_iterator propertyIt = configSection.begin();
444
445 // Get checker.signer.type
446 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
447 throw Error("Expect <checker.signer.type>!");
448
449 std::string type = propertyIt->second.data();
450 propertyIt++;
451
452 if (boost::iequals(type, "file"))
453 {
454 // Get checker.signer.file-name
455 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
456 throw Error("Expect <checker.signer.file-name>!");
457
458 path certfilePath = absolute(propertyIt->second.data(),
459 path(configFilename).parent_path());
460 propertyIt++;
461
462 if (propertyIt != configSection.end())
463 throw Error("Expect the end of checker.signer");
464
465 shared_ptr<IdentityCertificate> idCert
466 = io::load<IdentityCertificate>(certfilePath.c_str());
467
468 if (static_cast<bool>(idCert))
469 return idCert;
470 else
471 throw Error("Cannot read certificate from file: "
472 + certfilePath.native());
473 }
474 else if (boost::iequals(type, "base64"))
475 {
476 // Get checker.signer.base64-string
477 if (propertyIt == configSection.end() ||
478 !boost::iequals(propertyIt->first, "base64-string"))
479 throw Error("Expect <checker.signer.base64-string>!");
480
481 std::stringstream ss(propertyIt->second.data());
482 propertyIt++;
483
484 if (propertyIt != configSection.end())
485 throw Error("Expect the end of checker.signer");
486
487 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
488
489 if (static_cast<bool>(idCert))
490 return idCert;
491 else
492 throw Error("Cannot decode certificate from string");
493 }
494 else
495 throw Error("Unsupported checker.signer type: " + type);
496 }
497
498 static int32_t
499 getSigType(const std::string& sigType)
500 {
501 if (boost::iequals(sigType, "rsa-sha256"))
502 return Signature::Sha256WithRsa;
503 else if (boost::iequals(sigType, "sha256"))
504 return Signature::Sha256;
505 else
506 return -1;
507 }
508};
509
510} // namespace conf
511} // namespace security
512} // namespace ndn
513
514#endif // NDN_SECURITY_SEC_CONF_RULE_SIGNER_HPP