blob: 12017e2fde055397e3ab9b2564565ec970425aff [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:
196 HierarchicalChecker(uint32_t sigType)
197 : CustomizedChecker(sigType,
198 make_shared<HyperKeyLocatorNameChecker>("^(<>*)$", "\\1",
199 "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
200 "\\1\\2",
201 KeyLocatorChecker::RELATION_IS_PREFIX_OF))
202 {
203 }
204};
205
206class FixedSignerChecker : public Checker
207{
208 enum
209 {
210 INTEREST_SIG_VALUE = -1,
Alexander Afanasyevb78bc4d2014-04-09 21:20:52 -0700211 INTEREST_SIG_INFO = -2
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700212 };
213public:
214 FixedSignerChecker(uint32_t sigType,
215 const std::vector<shared_ptr<IdentityCertificate> >& signers)
216 : m_sigType(sigType)
217 {
218 for (std::vector<shared_ptr<IdentityCertificate> >::const_iterator it = signers.begin();
219 it != signers.end(); it++)
220 m_signers[(*it)->getName().getPrefix(-1)] = (*it);
221 }
222
223 virtual int8_t
224 check(const Data& data,
225 const OnDataChecked& onValidated,
226 const OnDataCheckFailed& onValidationFailed)
227 {
228 return check(data, data.getSignature(), onValidated, onValidationFailed);
229 }
230
231 virtual int8_t
232 check(const Interest& interest,
233 const OnInterestChecked& onValidated,
234 const OnInterestCheckFailed& onValidationFailed)
235 {
Yingdi Yu20a06962014-04-17 12:56:04 -0700236 try
237 {
238 const Name& interestName = interest.getName();
239 Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
240 interestName[INTEREST_SIG_VALUE].blockFromValue());
241 return check(interest, signature, onValidated, onValidationFailed);
242 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700243 catch (Signature::Error& e)
Yingdi Yu20a06962014-04-17 12:56:04 -0700244 {
Yingdi Yu96e64062014-04-15 19:57:33 -0700245 onValidationFailed(interest.shared_from_this(), "Invalid signature");
Yingdi Yu20a06962014-04-17 12:56:04 -0700246 return -1;
247 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700248 catch (Tlv::Error& e)
249 {
250 onValidationFailed(interest.shared_from_this(), "Cannot decode signature related TLVs");
251 return -1;
252 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700253 }
254
255private:
256 template<class Packet, class OnValidated, class OnFailed>
257 int8_t
258 check(const Packet& packet, const Signature& signature,
259 const OnValidated& onValidated,
260 const OnFailed& onValidationFailed)
261 {
262 if (m_sigType != signature.getType())
263 {
264 onValidationFailed(packet.shared_from_this(),
265 "Signature type does not match: "
266 + boost::lexical_cast<std::string>(m_sigType)
267 + "!="
268 + boost::lexical_cast<std::string>(signature.getType()));
269 return -1;
270 }
271
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700272 switch (signature.getType())
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700273 {
274 case Signature::Sha256WithRsa:
275 {
276 try
277 {
278 SignatureSha256WithRsa sig(signature);
279
280 const Name& keyLocatorName = sig.getKeyLocator().getName();
281 if (m_signers.find(keyLocatorName) == m_signers.end())
282 {
283 onValidationFailed(packet.shared_from_this(),
284 "Signer is not in the fixed signer list: "
285 + keyLocatorName.toUri());
286 return -1;
287 }
288
289 if (Validator::verifySignature(packet, sig,
290 m_signers[keyLocatorName]->getPublicKeyInfo()))
291 {
292 onValidated(packet.shared_from_this());
293 return 1;
294 }
295 else
296 {
297 onValidationFailed(packet.shared_from_this(),
298 "Signature cannot be validated!");
299 return -1;
300 }
301 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700302 catch (KeyLocator::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700303 {
304 onValidationFailed(packet.shared_from_this(),
305 "KeyLocator does not have name!");
306 return -1;
307 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700308 catch (SignatureSha256WithRsa::Error& e)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700309 {
310 onValidationFailed(packet.shared_from_this(),
311 "Cannot decode signature!");
312 return -1;
313 }
314 }
315 case Signature::Sha256:
316 {
317 onValidationFailed(packet.shared_from_this(),
318 "FixedSigner does not allow Sha256 signature type!");
319 return -1;
320 }
321 default:
322 {
323 onValidationFailed(packet.shared_from_this(),
324 "Unsupported signature type: "
325 + boost::lexical_cast<std::string>(signature.getType()));
326 return -1;
327 }
328 }
329 }
330
331private:
332 typedef std::map<Name, shared_ptr<IdentityCertificate> > SignerList;
333
334 uint32_t m_sigType;
335 SignerList m_signers;
336};
337
338class CheckerFactory
339{
340public:
341 /**
342 * @brief create a checker from configuration file.
343 *
344 * @param configSection The section containing the definition of checker.
345 * @param configFilename The configuration file name.
346 * @return a shared pointer to the created checker.
347 */
348 static shared_ptr<Checker>
349 create(const ConfigSection& configSection, const std::string& configFilename)
350 {
351 ConfigSection::const_iterator propertyIt = configSection.begin();
352
353 // Get checker.type
354 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
355 throw Error("Expect <checker.type>!");
356
357 std::string type = propertyIt->second.data();
358
359 if (boost::iequals(type, "customized"))
360 return createCustomizedChecker(configSection, configFilename);
361 else if (boost::iequals(type, "hierarchical"))
362 return createHierarchicalChecker(configSection, configFilename);
363 else if (boost::iequals(type, "fixed-signer"))
364 return createFixedSignerChecker(configSection, configFilename);
365 else
366 throw Error("Unsupported checker type: " + type);
367 }
368
369private:
370 static shared_ptr<Checker>
371 createCustomizedChecker(const ConfigSection& configSection,
372 const std::string& configFilename)
373 {
374 ConfigSection::const_iterator propertyIt = configSection.begin();
375 propertyIt++;
376
377 // Get checker.sig-type
378 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
379 throw Error("Expect <checker.sig-type>!");
380
381 std::string sigType = propertyIt->second.data();
382 propertyIt++;
383
384 // Get checker.key-locator
385 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator"))
386 throw Error("Expect <checker.key-locator>!");
387
388 shared_ptr<KeyLocatorChecker> keyLocatorChecker =
389 KeyLocatorCheckerFactory::create(propertyIt->second, configFilename);
390 propertyIt++;
391
392 if (propertyIt != configSection.end())
393 throw Error("Expect the end of checker!");
394
Alexander Afanasyevf73f0632014-05-12 18:02:37 -0700395 return make_shared<CustomizedChecker>(getSigType(sigType), keyLocatorChecker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700396 }
397
398 static shared_ptr<Checker>
399 createHierarchicalChecker(const ConfigSection& configSection,
400 const std::string& configFilename)
401 {
402 ConfigSection::const_iterator propertyIt = configSection.begin();
403 propertyIt++;
404
405 // Get checker.sig-type
406 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
407 throw Error("Expect <checker.sig-type>!");
408
409 std::string sigType = propertyIt->second.data();
410 propertyIt++;
411
412 if (propertyIt != configSection.end())
413 throw Error("Expect the end of checker!");
414
Alexander Afanasyevb67090a2014-04-29 22:31:01 -0700415 return make_shared<HierarchicalChecker>(getSigType(sigType));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700416 }
417
418 static shared_ptr<Checker>
419 createFixedSignerChecker(const ConfigSection& configSection,
420 const std::string& configFilename)
421 {
422 ConfigSection::const_iterator propertyIt = configSection.begin();
423 propertyIt++;
424
425 // Get checker.sig-type
426 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
427 throw Error("Expect <checker.sig-type>!");
428
429 std::string sigType = propertyIt->second.data();
430 propertyIt++;
431
432 std::vector<shared_ptr<IdentityCertificate> > signers;
433 for (; propertyIt != configSection.end(); propertyIt++)
434 {
435 if (!boost::iequals(propertyIt->first, "signer"))
436 throw Error("Expect <checker.signer> but get <checker."
437 + propertyIt->first + ">");
438
439 signers.push_back(getSigner(propertyIt->second, configFilename));
440 }
441
442 if (propertyIt != configSection.end())
443 throw Error("Expect the end of checker!");
444
445 return shared_ptr<FixedSignerChecker>(new FixedSignerChecker(getSigType(sigType),
446 signers));
447 }
448
449 static shared_ptr<IdentityCertificate>
450 getSigner(const ConfigSection& configSection, const std::string& configFilename)
451 {
452 using namespace boost::filesystem;
453
454 ConfigSection::const_iterator propertyIt = configSection.begin();
455
456 // Get checker.signer.type
457 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
458 throw Error("Expect <checker.signer.type>!");
459
460 std::string type = propertyIt->second.data();
461 propertyIt++;
462
463 if (boost::iequals(type, "file"))
464 {
465 // Get checker.signer.file-name
466 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
467 throw Error("Expect <checker.signer.file-name>!");
468
469 path certfilePath = absolute(propertyIt->second.data(),
470 path(configFilename).parent_path());
471 propertyIt++;
472
473 if (propertyIt != configSection.end())
474 throw Error("Expect the end of checker.signer");
475
476 shared_ptr<IdentityCertificate> idCert
477 = io::load<IdentityCertificate>(certfilePath.c_str());
478
479 if (static_cast<bool>(idCert))
480 return idCert;
481 else
482 throw Error("Cannot read certificate from file: "
483 + certfilePath.native());
484 }
485 else if (boost::iequals(type, "base64"))
486 {
487 // Get checker.signer.base64-string
488 if (propertyIt == configSection.end() ||
489 !boost::iequals(propertyIt->first, "base64-string"))
490 throw Error("Expect <checker.signer.base64-string>!");
491
492 std::stringstream ss(propertyIt->second.data());
493 propertyIt++;
494
495 if (propertyIt != configSection.end())
496 throw Error("Expect the end of checker.signer");
497
498 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
499
500 if (static_cast<bool>(idCert))
501 return idCert;
502 else
503 throw Error("Cannot decode certificate from string");
504 }
505 else
506 throw Error("Unsupported checker.signer type: " + type);
507 }
508
509 static int32_t
510 getSigType(const std::string& sigType)
511 {
512 if (boost::iequals(sigType, "rsa-sha256"))
513 return Signature::Sha256WithRsa;
514 else if (boost::iequals(sigType, "sha256"))
515 return Signature::Sha256;
516 else
517 return -1;
518 }
519};
520
521} // namespace conf
522} // namespace security
523} // namespace ndn
524
525#endif // NDN_SECURITY_SEC_CONF_RULE_SIGNER_HPP