IBR-DTNSuite  0.12
SecurityBlock.cpp
Go to the documentation of this file.
1 /*
2  * SecurityBlock.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
22 #include "ibrdtn/config.h"
25 #include "ibrdtn/data/Bundle.h"
28 
29 #include <ibrcommon/Logger.h>
30 #include <cstdlib>
31 #include <openssl/rand.h>
32 #include <openssl/err.h>
33 #include <openssl/rsa.h>
34 #include <vector>
35 
36 #ifdef __DEVELOPMENT_ASSERTIONS__
37 #include <cassert>
38 #endif
39 
40 // include code for platform-independent endianess conversion
41 #include "ibrdtn/data/Endianess.h"
42 
43 namespace dtn
44 {
45  namespace security
46  {
47  const std::string SecurityBlock::TLVList::toString() const
48  {
49  std::stringstream ss;
50 
51  for (std::set<TLV>::const_iterator iter = begin(); iter != end(); ++iter)
52  {
53  ss << (*iter);
54  }
55 
56  return ss.str();
57  }
58 
60  {
61  return getPayloadLength();
62  }
63 
64  dtn::data::Length SecurityBlock::TLVList::getPayloadLength() const
65  {
66  dtn::data::Length len = 0;
67 
68  for (std::set<SecurityBlock::TLV>::const_iterator iter = begin(); iter != end(); ++iter)
69  {
70  len += (*iter).getLength();
71  }
72 
73  return len;
74  }
75 
77  {
78  for (std::set<SecurityBlock::TLV>::const_iterator iter = begin(); iter != end(); ++iter)
79  {
80  if ((*iter).getType() == type)
81  {
82  return (*iter).getValue();
83  }
84  }
85 
87  }
88 
89  void SecurityBlock::TLVList::get(TLV_TYPES type, unsigned char *value, dtn::data::Length length) const
90  {
91  const std::string data = get(type);
92 
93  if (length < data.size())
94  {
95  ::memcpy(value, data.c_str(), length);
96  }
97  else
98  {
99  ::memcpy(value, data.c_str(), data.size());
100  }
101  }
102 
104  {
105  SecurityBlock::TLV tlv(type, value);
106 
107  erase(tlv);
108  insert(tlv);
109  }
110 
111  void SecurityBlock::TLVList::set(TLV_TYPES type, const unsigned char *value, dtn::data::Length length)
112  {
113  const std::string data(reinterpret_cast<const char *>(value), length);
114  set(type, data);
115  }
116 
118  {
119  erase(SecurityBlock::TLV(type, ""));
120  }
121 
122  const std::string SecurityBlock::TLV::getValue() const
123  {
124  return _value;
125  }
126 
128  {
129  return _type;
130  }
131 
133  {
134  return _value.getLength() + sizeof(char);
135  }
136 
137  std::ostream& operator<<(std::ostream &stream, const SecurityBlock::TLVList &tlvlist)
138  {
139  dtn::data::Number length(tlvlist.getPayloadLength());
140  stream << length;
141 
142  for (std::set<SecurityBlock::TLV>::const_iterator iter = tlvlist.begin(); iter != tlvlist.end(); ++iter)
143  {
144  stream << (*iter);
145  }
146  return stream;
147  }
148 
149  std::istream& operator>>(std::istream &stream, SecurityBlock::TLVList &tlvlist)
150  {
151  dtn::data::Number length;
152  stream >> length;
153  dtn::data::Length read_length = 0;
154 
155  while (length > read_length)
156  {
157  SecurityBlock::TLV tlv;
158  stream >> tlv;
159  tlvlist.insert(tlv);
160  read_length += tlv.getLength();
161  }
162 
163  return stream;
164  }
165 
167  {
168  return (_type < tlv._type);
169  }
170 
172  {
173  return (_type == tlv._type);
174  }
175 
176  std::ostream& operator<<(std::ostream &stream, const SecurityBlock::TLV &tlv)
177  {
178  stream.put((char)tlv._type);
179  stream << tlv._value;
180  return stream;
181  }
182 
183  std::istream& operator>>(std::istream &stream, SecurityBlock::TLV &tlv)
184  {
185  char tlv_type;
186  stream.get(tlv_type); tlv._type = SecurityBlock::TLV_TYPES(tlv_type);
187  stream >> tlv._value;
188  return stream;
189  }
190 
193  {
194 
195  }
196 
198  : Block(type), _ciphersuite_id(0), _ciphersuite_flags(0), _correlator(0)
199  {
200 
201  }
202 
204  {
205  }
206 
208  {
209  // clear the EID list
210  _eids.clear();
211 
212  // first the security source
214  {
216  }
217  else
218  {
220  _eids.push_back(_security_source);
221  }
222 
223  // then the destination
225  {
227  }
228  else
229  {
231  _eids.push_back(_security_destination);
232  }
233 
234  if (_eids.size() > 0)
235  {
236  set(Block::BLOCK_CONTAINS_EIDS, true);
237  }
238  else
239  {
240  set(Block::BLOCK_CONTAINS_EIDS, false);
241  }
242  }
243 
245  {
246  return _security_source;
247  }
248 
250  {
251  return _security_destination;
252  }
253 
255  {
256  _security_source = source;
258  }
259 
261  {
262  _security_destination = destination;
264  }
265 
267  {
268  _ciphersuite_id = static_cast<uint64_t>(id);
269  }
270 
272  {
273  _correlator = corr;
275  }
276 
278  {
279  bool return_val = false;
280  for (dtn::data::Bundle::const_iterator it = bundle.begin(); it != bundle.end() && !return_val; ++it)
281  {
282  const dtn::data::Block &b = (**it);
283  const dtn::data::block_t type = b.getType();
284  if (type == BUNDLE_AUTHENTICATION_BLOCK
285  || type == PAYLOAD_INTEGRITY_BLOCK
286  || type == PAYLOAD_CONFIDENTIAL_BLOCK
287  || type == EXTENSION_SECURITY_BLOCK)
288  return_val = static_cast<const SecurityBlock&>(b)._correlator == correlator;
289  }
290  return return_val;
291  }
292 
294  {
295  dtn::data::Number corr;
296  corr.random();
297 
298  while (isCorrelatorPresent(bundle, corr)) {
299  corr.random();
300  }
301  return corr;
302  }
303 
305  {
308 
310  {
311  length += _correlator.getLength();
312  }
313 
315  {
317  length += size.getLength() + size.get<dtn::data::Length>();
318  }
319 
321  {
323  length += size.getLength() + size.get<dtn::data::Length>();
324  }
325 
326  return length;
327  }
328 
330  {
331  // ciphersuite_id
333 
334  // ciphersuite_flags
336 
337  // correlator
339  {
341  }
342 
343  // ciphersuite parameters
345  {
347  length += _ciphersuite_params.getLength();
348  }
349  // security result
351  {
353  }
354 
355  return length;
356  }
357 
358  std::ostream& SecurityBlock::serialize(std::ostream &stream, dtn::data::Length &) const
359  {
360  stream << _ciphersuite_id << _ciphersuite_flags;
361 
362  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
363  {
364  stream << _correlator;
365  }
366 
367  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
368  {
369  stream << _ciphersuite_params;
370  }
371 
372  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
373  {
374  stream << _security_result;
375  }
376 
377  return stream;
378  }
379 
380  std::ostream& SecurityBlock::serialize_strict(std::ostream &stream, dtn::data::Length &) const
381  {
382  stream << _ciphersuite_id << _ciphersuite_flags;
383 
384  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
385  {
386  stream << _correlator;
387  }
388 
389  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
390  {
391  stream << _ciphersuite_params;
392  }
393 
394  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
395  {
397  }
398 
399  return stream;
400  }
401 
402  std::istream& SecurityBlock::deserialize(std::istream &stream, const dtn::data::Length&)
403  {
404 #ifdef __DEVELOPMENT_ASSERTIONS__
405  // recheck blocktype. if blocktype is set wrong, this will be a huge fail
407 #endif
408 
409  stream >> _ciphersuite_id >> _ciphersuite_flags;
410 
411 #ifdef __DEVELOPMENT_ASSERTIONS__
412  // recheck ciphersuite_id
414  // recheck ciphersuite_flags, could be more exhaustive
415  assert(_ciphersuite_flags < 32);
416 #endif
417 
418  // copy security source and destination
419  if (_ciphersuite_flags & SecurityBlock::CONTAINS_SECURITY_SOURCE)
420  {
421  if (_eids.size() == 0)
422  throw dtn::SerializationFailedException("ciphersuite flags indicate a security source, but it is not present");
423 
424  _security_source = _eids.front();
425  }
426 
427  if (_ciphersuite_flags & SecurityBlock::CONTAINS_SECURITY_DESTINATION)
428  {
429  if (_ciphersuite_flags & SecurityBlock::CONTAINS_SECURITY_SOURCE)
430  {
431  if (_eids.size() < 2)
432  throw dtn::SerializationFailedException("ciphersuite flags indicate a security destination, but it is not present");
433 
434  _security_destination = *(++_eids.begin());
435  }
436  else
437  {
438  if (_eids.size() == 0)
439  throw dtn::SerializationFailedException("ciphersuite flags indicate a security destination, but it is not present");
440 
441  _security_destination = _eids.front();
442  }
443  }
444 
445  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
446  {
447  stream >> _correlator;
448  }
449  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
450  {
451  stream >> _ciphersuite_params;
452 #ifdef __DEVELOPMENT_ASSERTIONS__
453  assert(_ciphersuite_params.getLength() > 0);
454 #endif
455  }
456 
457  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
458  {
459  stream >> _security_result;
460 #ifdef __DEVELOPMENT_ASSERTIONS__
461  assert(_security_result.getLength() > 0);
462 #endif
463  }
464 
465  return stream;
466  }
467 
469  {
470  serializer << _ciphersuite_id;
471  serializer << _ciphersuite_flags;
472 
473  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
474  {
475  serializer << _correlator;
476  }
477 
478  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
479  {
480  serializer << _ciphersuite_params;
481  }
482 
483  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
484  {
485  if (include_security_result) {
486  serializer << _security_result;
487  } else {
488  serializer << dtn::data::Number(getSecurityResultSize());
489  }
490  }
491 
492  return serializer;
493  }
494 
496  {
497 #ifdef __DEVELOPMENT_ASSERTIONS__
498  assert(_security_result.getLength() != 0);
499 #endif
500  return _security_result.getLength();
501  }
502 
503  void SecurityBlock::createSaltAndKey(uint32_t& salt, unsigned char* key, dtn::data::Length key_size)
504  {
505 
506  if (!RAND_bytes(reinterpret_cast<unsigned char *>(&salt), sizeof(uint32_t)))
507  {
508  IBRCOMMON_LOGGER_ex(critical) << "failed to generate salt. maybe /dev/urandom is missing for seeding the PRNG" << IBRCOMMON_LOGGER_ENDL;
509  ERR_print_errors_fp(stderr);
510  }
511  if (!RAND_bytes(key, static_cast<int>(key_size)))
512  {
513  IBRCOMMON_LOGGER_ex(critical) << "failed to generate key. maybe /dev/urandom is missing for seeding the PRNG" << IBRCOMMON_LOGGER_ENDL;
514  ERR_print_errors_fp(stderr);
515  }
516  }
517 
518  void SecurityBlock::addKey(TLVList& security_parameter, unsigned char const * const key, dtn::data::Length key_size, RSA * rsa)
519  {
520  // encrypt the ephemeral key and place it in _ciphersuite_params
521 #ifdef __DEVELOPMENT_ASSERTIONS__
522  assert(key_size < (dtn::data::Length)RSA_size(rsa)-41);
523 #endif
524  std::vector<unsigned char> encrypted_key(RSA_size(rsa));
525  int encrypted_key_len = RSA_public_encrypt(static_cast<int>(key_size), key, &encrypted_key[0], rsa, RSA_PKCS1_OAEP_PADDING);
526  if (encrypted_key_len == -1)
527  {
528  IBRCOMMON_LOGGER_ex(critical) << "failed to encrypt the symmetric AES key" << IBRCOMMON_LOGGER_ENDL;
529  ERR_print_errors_fp(stderr);
530  return;
531  }
532  security_parameter.set(SecurityBlock::key_information, std::string(reinterpret_cast<char *>(&encrypted_key[0]), encrypted_key_len));
533  }
534 
535  bool SecurityBlock::getKey(const TLVList& security_parameter, unsigned char * key, dtn::data::Length key_size, RSA * rsa)
536  {
537  std::string key_string = security_parameter.get(SecurityBlock::key_information);
538  // get key, convert with reinterpret_cast
539  const unsigned char *encrypted_key = reinterpret_cast<const unsigned char*>(key_string.c_str());
540  std::vector<unsigned char> the_key(RSA_size(rsa));
541  RSA_blinding_on(rsa, NULL);
542  int plaintext_key_len = RSA_private_decrypt(static_cast<int>(key_string.size()), encrypted_key, &the_key[0], rsa, RSA_PKCS1_OAEP_PADDING);
543  RSA_blinding_off(rsa);
544  if (plaintext_key_len == -1)
545  {
546  IBRCOMMON_LOGGER_ex(critical) << "failed to decrypt the symmetric AES key" << IBRCOMMON_LOGGER_ENDL;
547  ERR_print_errors_fp(stderr);
548  return false;
549  }
550 #ifdef __DEVELOPMENT_ASSERTIONS__
551  assert((dtn::data::Length)plaintext_key_len == key_size);
552 #endif
553  std::copy(&the_key[0], &the_key[key_size], key);
554  return true;
555  }
556 
557  void SecurityBlock::copyEID(const Block& from, Block& to, dtn::data::Length skip)
558  {
559  // take eid list, getEIDList() is broken
560  std::list<dtn::data::EID> their_eids = from.getEIDList();
561  std::list<dtn::data::EID>::iterator it = their_eids.begin();
562 
563  while (it != their_eids.end() && skip > 0)
564  {
565  skip--;
566  ++it;
567  }
568 
569  for (; it != their_eids.end(); ++it)
570  to.addEID(*it);
571  }
572 
573  void SecurityBlock::addSalt(TLVList& security_parameters, const uint32_t &salt)
574  {
575  uint32_t nsalt = GUINT32_TO_BE(salt);
576  security_parameters.set(SecurityBlock::salt, (const unsigned char*)&nsalt, sizeof(nsalt));
577  }
578 
579  uint32_t SecurityBlock::getSalt(const TLVList& security_parameters)
580  {
581  uint32_t nsalt = 0;
582  security_parameters.get(SecurityBlock::salt, (unsigned char*)&nsalt, sizeof(nsalt));
583  return GUINT32_TO_BE(nsalt);
584  }
585 
587  {
588  const dtn::security::SecurityBlock &block = dynamic_cast<const dtn::security::SecurityBlock&>(**it);
589 
590  // the array for the extracted tag
591  unsigned char tag[ibrcommon::AES128Stream::tag_len];
592 
593  // the array for the extracted iv
594  unsigned char iv[ibrcommon::AES128Stream::iv_len];
595 
596  // get iv, convert with reinterpret_cast
598 
599  // get data and tag, the last tag_len bytes are the tag. cut them of and reinterpret_cast
600  std::string block_data = block._security_result.get(SecurityBlock::encapsulated_block);
601 
602  // create a pointer to the tag begin
603  const char *tag_p = block_data.c_str() + (block_data.size() - ibrcommon::AES128Stream::tag_len);
604 
605  // copy the tag
606  ::memcpy(tag, tag_p, ibrcommon::AES128Stream::tag_len);
607 
608  // strip off the tag from block data
609  block_data.resize(block_data.size() - ibrcommon::AES128Stream::tag_len);
610 
611  // decrypt block
612  std::stringstream plaintext;
613  ibrcommon::AES128Stream decrypt(ibrcommon::CipherStream::CIPHER_DECRYPT, plaintext, key, salt, iv);
614  decrypt << block_data << std::flush;
615 
616  // verify the decrypt tag
617  if (!decrypt.verify(tag))
618  {
619  throw DecryptException("decryption of block failed - tag is bad");
620  }
621 
622  // deserialize block
623  dtn::data::DefaultDeserializer ddser(plaintext);
624 
625  // peek the block type
626  dtn::data::block_t block_type = (dtn::data::block_t)plaintext.peek();
627 
628  // get the position of "block"
629  dtn::data::Bundle::iterator b_it = bundle.find(block);
630 
631  if (block_type == dtn::data::PayloadBlock::BLOCK_TYPE)
632  {
633  dtn::data::PayloadBlock &plaintext_block = bundle.insert<dtn::data::PayloadBlock>(b_it);
634  ddser >> plaintext_block;
635  }
636  else
637  {
638  try {
640  dtn::data::Block &plaintext_block = bundle.insert(b_it, f);
641  ddser >> plaintext_block;
642 
643  plaintext_block.clearEIDs();
644 
645  // copy eids
646  // remove security_source and destination
647  dtn::data::Length skip = 0;
649  skip++;
651  skip++;
652  copyEID(plaintext_block, plaintext_block, skip);
653 
654  } catch (const ibrcommon::Exception &ex) {
655  dtn::data::ExtensionBlock &plaintext_block = bundle.insert<dtn::data::ExtensionBlock>(b_it);
656  ddser >> plaintext_block;
657 
658  plaintext_block.clearEIDs();
659 
660  // copy eids
661  // remove security_source and destination
662  dtn::data::Length skip = 0;
664  skip++;
666  skip++;
667  copyEID(plaintext_block, plaintext_block, skip);
668  }
669  }
670 
671  bundle.remove(block);
672  }
673 
674  void SecurityBlock::addFragmentRange(TLVList& ciphersuite_params, const dtn::data::Number &offset, const dtn::data::Number &range)
675  {
676  std::stringstream ss;
677  ss << offset << range;
678 
679  ciphersuite_params.set(SecurityBlock::fragment_range, ss.str());
680  }
681 
683  {
684  IBRCOMMON_LOGGER_DEBUG_TAG("SecurityBlock", 30) << "check security source: " << getSecuritySource(bundle).getString() << " == " << eid.getNode().getString() << IBRCOMMON_LOGGER_ENDL;
685  return getSecuritySource(bundle).sameHost(eid);
686  }
687 
689  {
690  IBRCOMMON_LOGGER_DEBUG_TAG("SecurityBlock", 30) << "check security destination: " << getSecurityDestination(bundle).getString() << " == " << eid.getNode().getString() << IBRCOMMON_LOGGER_ENDL;
691  return getSecurityDestination(bundle).sameHost(eid);
692  }
693 
695  {
696  const dtn::data::EID source = getSecuritySource();
697  if (source.isNone())
698  return bundle.source.getNode();
699  return source;
700  }
701 
703  {
704  const dtn::data::EID destination = getSecurityDestination();
705  if (destination.isNone())
706  return bundle.destination.getNode();
707  return destination;
708  }
709  }
710 }