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