blob: a9ab2a99c574b9a2fe137fca84e68004a4852386 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Yingdi Yu48e8c0c2014-03-19 12:01:55 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * 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.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070022 */
23
24#ifndef NDN_SECURITY_CONF_CHECKER_HPP
25#define NDN_SECURITY_CONF_CHECKER_HPP
26
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070027#include "common.hpp"
28
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070029#include "key-locator-checker.hpp"
30#include "../../util/io.hpp"
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070031
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070032#include <boost/algorithm/string.hpp>
33#include <boost/filesystem.hpp>
34#include <boost/lexical_cast.hpp>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070035
36namespace ndn {
37namespace security {
38namespace conf {
39
40class Checker
41{
42public:
43 typedef function<void(const shared_ptr<const Interest>&)> OnInterestChecked;
44 typedef function<void(const shared_ptr<const Interest>&, const std::string&)> OnInterestCheckFailed;
45 typedef function<void(const shared_ptr<const Data>&)> OnDataChecked;
46 typedef function<void(const shared_ptr<const Data>&, const std::string&)> OnDataCheckFailed;
47
48
49 virtual
50 ~Checker()
51 {
52 }
53
54 /**
55 * @brief check if data satisfies condition defined in the specific checker implementation
56 *
57 * @param data Data packet
58 * @param onValidated Callback function which is called when data is immediately valid
59 * @param onValidationFailed Call function which is called when data is immediately invalid
60 * @return -1 if data is immediately invalid (onValidationFailed has been called)
61 * 1 if data is immediately valid (onValidated has been called)
62 * 0 if further signature verification is needed.
63 */
64 virtual int8_t
65 check(const Data& data,
66 const OnDataChecked& onValidated,
67 const OnDataCheckFailed& onValidationFailed) = 0;
68
69 /**
70 * @brief check if interest satisfies condition defined in the specific checker implementation
71 *
72 * @param interest Interest packet
73 * @param onValidated Callback function which is called when interest is immediately valid
74 * @param onValidationFailed Call function which is called when interest is immediately invalid
75 * @return -1 if interest is immediately invalid (onValidationFailed has been called)
76 * 1 if interest is immediately valid (onValidated has been called)
77 * 0 if further signature verification is needed.
78 */
79 virtual int8_t
80 check(const Interest& interest,
81 const OnInterestChecked& onValidated,
82 const OnInterestCheckFailed& onValidationFailed) = 0;
83};
84
85class CustomizedChecker : public Checker
86{
87 enum
88 {
89 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -070090 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070091 };
92
93public:
94 CustomizedChecker(uint32_t sigType,
95 shared_ptr<KeyLocatorChecker> keyLocatorChecker)
96 : m_sigType(sigType)
97 , m_keyLocatorChecker(keyLocatorChecker)
98 {
99 if (m_sigType == Signature::Sha256WithRsa && !static_cast<bool>(m_keyLocatorChecker))
100 throw Error("Strong signature requires KeyLocatorChecker");
101 }
102
103 virtual int8_t
104 check(const Data& data,
105 const OnDataChecked& onValidated,
106 const OnDataCheckFailed& onValidationFailed)
107 {
108 return check(data, data.getSignature(), onValidated, onValidationFailed);
109 }
110
111 virtual int8_t
112 check(const Interest& interest,
113 const OnInterestChecked& onValidated,
114 const OnInterestCheckFailed& onValidationFailed)
115 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700116 try
117 {
118 const Name& interestName = interest.getName();
119 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
120 interestName[INTEREST_SIG_VALUE].blockFromValue());
121 return check(interest, signature, onValidated, onValidationFailed);
122 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700123 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700124 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700125 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700126 return -1;
127 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700128 catch (Tlv::Error& e)
129 {
130 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
131 return -1;
132 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700133 }
134
135private:
136 template<class Packet, class OnValidated, class OnFailed>
137 int8_t
138 check(const Packet& packet, const Signature& signature,
139 const OnValidated& onValidated,
140 const OnFailed& onValidationFailed)
141 {
142 if (m_sigType != signature.getType())
143 {
144 onValidationFailed(packet.shared_from_this(),
145 "Signature type does not match: " +
146 boost::lexical_cast<std::string>(m_sigType) +
147 "!=" +
148 boost::lexical_cast<std::string>(signature.getType()));
149 return -1;
150 }
151
152 switch (signature.getType())
153 {
154 case Signature::Sha256WithRsa:
155 {
156 try
157 {
158 SignatureSha256WithRsa sig(signature);
159
160 std::string failInfo;
161 if (m_keyLocatorChecker->check(packet, sig.getKeyLocator(), failInfo))
162 return 0;
163 else
164 {
165 onValidationFailed(packet.shared_from_this(), failInfo);
166 return -1;
167 }
168 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700169 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700170 {
171 onValidationFailed(packet.shared_from_this(),
172 "Cannot decode Sha256WithRsa signature!");
173 return -1;
174 }
175 }
176 case Signature::Sha256:
177 return 0;
178 default:
179 {
180 onValidationFailed(packet.shared_from_this(),
181 "Unsupported signature type: " +
182 boost::lexical_cast<std::string>(signature.getType()));
183 return -1;
184 }
185 }
186 }
187
188private:
189 uint32_t m_sigType;
190 shared_ptr<KeyLocatorChecker> m_keyLocatorChecker;
191};
192
193class HierarchicalChecker : public CustomizedChecker
194{
195public:
Alexander Afanasyeva4297a62014-06-19 13:29:34 -0700196 explicit
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700197 HierarchicalChecker(uint32_t sigType)
198 : CustomizedChecker(sigType,
199 make_shared<HyperKeyLocatorNameChecker>("^(<>*)$", "\\1",
200 "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
201 "\\1\\2",
202 KeyLocatorChecker::RELATION_IS_PREFIX_OF))
203 {
204 }
205};
206
207class FixedSignerChecker : public Checker
208{
209 enum
210 {
211 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -0700212 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700213 };
214public:
215 FixedSignerChecker(uint32_t sigType,
216 const std::vector<shared_ptr<IdentityCertificate> >& signers)
217 : m_sigType(sigType)
218 {
219 for (std::vector<shared_ptr<IdentityCertificate> >::const_iterator it = signers.begin();
220 it != signers.end(); it++)
221 m_signers[(*it)->getName().getPrefix(-1)] = (*it);
222 }
223
224 virtual int8_t
225 check(const Data& data,
226 const OnDataChecked& onValidated,
227 const OnDataCheckFailed& onValidationFailed)
228 {
229 return check(data, data.getSignature(), onValidated, onValidationFailed);
230 }
231
232 virtual int8_t
233 check(const Interest& interest,
234 const OnInterestChecked& onValidated,
235 const OnInterestCheckFailed& onValidationFailed)
236 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700237 try
238 {
239 const Name& interestName = interest.getName();
240 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
241 interestName[INTEREST_SIG_VALUE].blockFromValue());
242 return check(interest, signature, onValidated, onValidationFailed);
243 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700244 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700245 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700246 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700247 return -1;
248 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700249 catch (Tlv::Error& e)
250 {
251 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
252 return -1;
253 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700254 }
255
256private:
257 template<class Packet, class OnValidated, class OnFailed>
258 int8_t
259 check(const Packet& packet, const Signature& signature,
260 const OnValidated& onValidated,
261 const OnFailed& onValidationFailed)
262 {
263 if (m_sigType != signature.getType())
264 {
265 onValidationFailed(packet.shared_from_this(),
266 "Signature type does not match: "
267 + boost::lexical_cast<std::string>(m_sigType)
268 + "!="
269 + boost::lexical_cast<std::string>(signature.getType()));
270 return -1;
271 }
272
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700273 switch (signature.getType())
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700274 {
275 case Signature::Sha256WithRsa:
276 {
277 try
278 {
279 SignatureSha256WithRsa sig(signature);
280
281 const Name& keyLocatorName = sig.getKeyLocator().getName();
282 if (m_signers.find(keyLocatorName) == m_signers.end())
283 {
284 onValidationFailed(packet.shared_from_this(),
285 "Signer is not in the fixed signer list: "
286 + keyLocatorName.toUri());
287 return -1;
288 }
289
290 if (Validator::verifySignature(packet, sig,
291 m_signers[keyLocatorName]->getPublicKeyInfo()))
292 {
293 onValidated(packet.shared_from_this());
294 return 1;
295 }
296 else
297 {
298 onValidationFailed(packet.shared_from_this(),
299 "Signature cannot be validated!");
300 return -1;
301 }
302 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700303 catch (KeyLocator::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700304 {
305 onValidationFailed(packet.shared_from_this(),
306 "KeyLocator does not have name!");
307 return -1;
308 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700309 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700310 {
311 onValidationFailed(packet.shared_from_this(),
312 "Cannot decode signature!");
313 return -1;
314 }
315 }
316 case Signature::Sha256:
317 {
318 onValidationFailed(packet.shared_from_this(),
319 "FixedSigner does not allow Sha256 signature type!");
320 return -1;
321 }
322 default:
323 {
324 onValidationFailed(packet.shared_from_this(),
325 "Unsupported signature type: "
326 + boost::lexical_cast<std::string>(signature.getType()));
327 return -1;
328 }
329 }
330 }
331
332private:
333 typedef std::map<Name, shared_ptr<IdentityCertificate> > SignerList;
334
335 uint32_t m_sigType;
336 SignerList m_signers;
337};
338
339class CheckerFactory
340{
341public:
342 /**
343 * @brief create a checker from configuration file.
344 *
345 * @param configSection The section containing the definition of checker.
346 * @param configFilename The configuration file name.
347 * @return a shared pointer to the created checker.
348 */
349 static shared_ptr<Checker>
350 create(const ConfigSection& configSection, const std::string& configFilename)
351 {
352 ConfigSection::const_iterator propertyIt = configSection.begin();
353
354 // Get checker.type
355 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
356 throw Error("Expect <checker.type>!");
357
358 std::string type = propertyIt->second.data();
359
360 if (boost::iequals(type, "customized"))
361 return createCustomizedChecker(configSection, configFilename);
362 else if (boost::iequals(type, "hierarchical"))
363 return createHierarchicalChecker(configSection, configFilename);
364 else if (boost::iequals(type, "fixed-signer"))
365 return createFixedSignerChecker(configSection, configFilename);
366 else
367 throw Error("Unsupported checker type: " + type);
368 }
369
370private:
371 static shared_ptr<Checker>
372 createCustomizedChecker(const ConfigSection& configSection,
373 const std::string& configFilename)
374 {
375 ConfigSection::const_iterator propertyIt = configSection.begin();
376 propertyIt++;
377
378 // Get checker.sig-type
379 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
380 throw Error("Expect <checker.sig-type>!");
381
382 std::string sigType = propertyIt->second.data();
383 propertyIt++;
384
385 // Get checker.key-locator
386 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator"))
387 throw Error("Expect <checker.key-locator>!");
388
389 shared_ptr<KeyLocatorChecker> keyLocatorChecker =
390 KeyLocatorCheckerFactory::create(propertyIt->second, configFilename);
391 propertyIt++;
392
393 if (propertyIt != configSection.end())
394 throw Error("Expect the end of checker!");
395
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700396 return make_shared<CustomizedChecker>(getSigType(sigType), keyLocatorChecker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700397 }
398
399 static shared_ptr<Checker>
400 createHierarchicalChecker(const ConfigSection& configSection,
401 const std::string& configFilename)
402 {
403 ConfigSection::const_iterator propertyIt = configSection.begin();
404 propertyIt++;
405
406 // Get checker.sig-type
407 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
408 throw Error("Expect <checker.sig-type>!");
409
410 std::string sigType = propertyIt->second.data();
411 propertyIt++;
412
413 if (propertyIt != configSection.end())
414 throw Error("Expect the end of checker!");
415
Alexander Afanasyevb67090a2014-04-29 22:31:01 -0700416 return make_shared<HierarchicalChecker>(getSigType(sigType));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700417 }
418
419 static shared_ptr<Checker>
420 createFixedSignerChecker(const ConfigSection& configSection,
421 const std::string& configFilename)
422 {
423 ConfigSection::const_iterator propertyIt = configSection.begin();
424 propertyIt++;
425
426 // Get checker.sig-type
427 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
428 throw Error("Expect <checker.sig-type>!");
429
430 std::string sigType = propertyIt->second.data();
431 propertyIt++;
432
433 std::vector<shared_ptr<IdentityCertificate> > signers;
434 for (; propertyIt != configSection.end(); propertyIt++)
435 {
436 if (!boost::iequals(propertyIt->first, "signer"))
437 throw Error("Expect <checker.signer> but get <checker."
438 + propertyIt->first + ">");
439
440 signers.push_back(getSigner(propertyIt->second, configFilename));
441 }
442
443 if (propertyIt != configSection.end())
444 throw Error("Expect the end of checker!");
445
446 return shared_ptr<FixedSignerChecker>(new FixedSignerChecker(getSigType(sigType),
447 signers));
448 }
449
450 static shared_ptr<IdentityCertificate>
451 getSigner(const ConfigSection& configSection, const std::string& configFilename)
452 {
453 using namespace boost::filesystem;
454
455 ConfigSection::const_iterator propertyIt = configSection.begin();
456
457 // Get checker.signer.type
458 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
459 throw Error("Expect <checker.signer.type>!");
460
461 std::string type = propertyIt->second.data();
462 propertyIt++;
463
464 if (boost::iequals(type, "file"))
465 {
466 // Get checker.signer.file-name
467 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
468 throw Error("Expect <checker.signer.file-name>!");
469
470 path certfilePath = absolute(propertyIt->second.data(),
471 path(configFilename).parent_path());
472 propertyIt++;
473
474 if (propertyIt != configSection.end())
475 throw Error("Expect the end of checker.signer");
476
477 shared_ptr<IdentityCertificate> idCert
478 = io::load<IdentityCertificate>(certfilePath.c_str());
479
480 if (static_cast<bool>(idCert))
481 return idCert;
482 else
483 throw Error("Cannot read certificate from file: "
484 + certfilePath.native());
485 }
486 else if (boost::iequals(type, "base64"))
487 {
488 // Get checker.signer.base64-string
489 if (propertyIt == configSection.end() ||
490 !boost::iequals(propertyIt->first, "base64-string"))
491 throw Error("Expect <checker.signer.base64-string>!");
492
493 std::stringstream ss(propertyIt->second.data());
494 propertyIt++;
495
496 if (propertyIt != configSection.end())
497 throw Error("Expect the end of checker.signer");
498
499 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
500
501 if (static_cast<bool>(idCert))
502 return idCert;
503 else
504 throw Error("Cannot decode certificate from string");
505 }
506 else
507 throw Error("Unsupported checker.signer type: " + type);
508 }
509
510 static int32_t
511 getSigType(const std::string& sigType)
512 {
513 if (boost::iequals(sigType, "rsa-sha256"))
514 return Signature::Sha256WithRsa;
515 else if (boost::iequals(sigType, "sha256"))
516 return Signature::Sha256;
517 else
518 return -1;
519 }
520};
521
522} // namespace conf
523} // namespace security
524} // namespace ndn
525
526#endif // NDN_SECURITY_SEC_CONF_RULE_SIGNER_HPP