IBR-DTNSuite  0.10
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 
24 #include "ibrdtn/data/Bundle.h"
27 
28 #include <ibrcommon/Logger.h>
29 #include <cstdlib>
30 #include <openssl/rand.h>
31 #include <openssl/err.h>
32 #include <openssl/rsa.h>
33 #include <netinet/in.h>
34 #include <vector>
35 
36 #ifdef __DEVELOPMENT_ASSERTIONS__
37 #include <cassert>
38 #endif
39 
40 namespace dtn
41 {
42  namespace security
43  {
44  const std::string SecurityBlock::TLVList::toString() const
45  {
46  std::stringstream ss;
47 
48  for (std::set<TLV>::const_iterator iter = begin(); iter != end(); ++iter)
49  {
50  ss << (*iter);
51  }
52 
53  return ss.str();
54  }
55 
57  {
58  return getPayloadLength();
59  }
60 
61  dtn::data::Length SecurityBlock::TLVList::getPayloadLength() const
62  {
63  dtn::data::Length len = 0;
64 
65  for (std::set<SecurityBlock::TLV>::const_iterator iter = begin(); iter != end(); ++iter)
66  {
67  len += (*iter).getLength();
68  }
69 
70  return len;
71  }
72 
74  {
75  for (std::set<SecurityBlock::TLV>::const_iterator iter = begin(); iter != end(); ++iter)
76  {
77  if ((*iter).getType() == type)
78  {
79  return (*iter).getValue();
80  }
81  }
82 
83  throw ibrcommon::Exception("element not found");
84  }
85 
86  void SecurityBlock::TLVList::get(TLV_TYPES type, unsigned char *value, dtn::data::Length length) const
87  {
88  const std::string data = get(type);
89 
90  if (length < data.size())
91  {
92  ::memcpy(value, data.c_str(), length);
93  }
94  else
95  {
96  ::memcpy(value, data.c_str(), data.size());
97  }
98  }
99 
101  {
102  SecurityBlock::TLV tlv(type, value);
103 
104  erase(tlv);
105  insert(tlv);
106  }
107 
108  void SecurityBlock::TLVList::set(TLV_TYPES type, const unsigned char *value, dtn::data::Length length)
109  {
110  const std::string data(reinterpret_cast<const char *>(value), length);
111  set(type, data);
112  }
113 
115  {
116  erase(SecurityBlock::TLV(type, ""));
117  }
118 
119  const std::string SecurityBlock::TLV::getValue() const
120  {
121  return _value;
122  }
123 
125  {
126  return _type;
127  }
128 
130  {
131  return _value.getLength() + sizeof(char);
132  }
133 
134  std::ostream& operator<<(std::ostream &stream, const SecurityBlock::TLVList &tlvlist)
135  {
136  dtn::data::Number length(tlvlist.getPayloadLength());
137  stream << length;
138 
139  for (std::set<SecurityBlock::TLV>::const_iterator iter = tlvlist.begin(); iter != tlvlist.end(); ++iter)
140  {
141  stream << (*iter);
142  }
143  return stream;
144  }
145 
146  std::istream& operator>>(std::istream &stream, SecurityBlock::TLVList &tlvlist)
147  {
148  dtn::data::Number length;
149  stream >> length;
150  dtn::data::Length read_length = 0;
151 
152  while (length > read_length)
153  {
154  SecurityBlock::TLV tlv;
155  stream >> tlv;
156  tlvlist.insert(tlv);
157  read_length += tlv.getLength();
158  }
159 
160  return stream;
161  }
162 
164  {
165  return (_type < tlv._type);
166  }
167 
169  {
170  return (_type == tlv._type);
171  }
172 
173  std::ostream& operator<<(std::ostream &stream, const SecurityBlock::TLV &tlv)
174  {
175  stream.put((char)tlv._type);
176  stream << tlv._value;
177  return stream;
178  }
179 
180  std::istream& operator>>(std::istream &stream, SecurityBlock::TLV &tlv)
181  {
182  char tlv_type;
183  stream.get(tlv_type); tlv._type = SecurityBlock::TLV_TYPES(tlv_type);
184  stream >> tlv._value;
185  return stream;
186  }
187 
190  {
191 
192  }
193 
195  : Block(type), _ciphersuite_id(0), _ciphersuite_flags(0), _correlator(0)
196  {
197 
198  }
199 
201  {
202  }
203 
205  {
206  // clear the EID list
207  _eids.clear();
208 
209  // first the security source
211  {
213  }
214  else
215  {
217  _eids.push_back(_security_source);
218  }
219 
220  // then the destination
222  {
224  }
225  else
226  {
228  _eids.push_back(_security_destination);
229  }
230 
231  if (_eids.size() > 0)
232  {
233  set(Block::BLOCK_CONTAINS_EIDS, true);
234  }
235  else
236  {
237  set(Block::BLOCK_CONTAINS_EIDS, false);
238  }
239  }
240 
242  {
243  return _security_source;
244  }
245 
247  {
248  return _security_destination;
249  }
250 
252  {
253  _security_source = source;
255  }
256 
258  {
259  _security_destination = destination;
261  }
262 
264  {
265  _ciphersuite_id = static_cast<uint64_t>(id);
266  }
267 
269  {
270  _correlator = corr;
272  }
273 
275  {
276  bool return_val = false;
277  for (dtn::data::Bundle::const_iterator it = bundle.begin(); it != bundle.end() && !return_val; ++it)
278  {
279  const dtn::data::Block &b = (**it);
280  const dtn::data::block_t type = b.getType();
281  if (type == BUNDLE_AUTHENTICATION_BLOCK
282  || type == PAYLOAD_INTEGRITY_BLOCK
283  || type == PAYLOAD_CONFIDENTIAL_BLOCK
284  || type == EXTENSION_SECURITY_BLOCK)
285  return_val = static_cast<const SecurityBlock&>(b)._correlator == correlator;
286  }
287  return return_val;
288  }
289 
291  {
292  dtn::data::Number corr;
293  corr.random();
294 
295  while (isCorrelatorPresent(bundle, corr)) {
296  corr.random();
297  }
298  return corr;
299  }
300 
302  {
305 
307  {
308  length += _correlator.getLength();
309  }
310 
312  {
314  length += size.getLength() + size.get<dtn::data::Length>();
315  }
316 
318  {
320  length += size.getLength() + size.get<dtn::data::Length>();
321  }
322 
323  return length;
324  }
325 
327  {
328  // ciphersuite_id
330 
331  // ciphersuite_flags
332  length += MutualSerializer::sdnv_size;
333 
334  // correlator
336  {
337  length += MutualSerializer::sdnv_size;
338  }
339 
340  // ciphersuite parameters
342  {
343  length += MutualSerializer::sdnv_size;
344  length += _ciphersuite_params.getLength();
345  }
346  // security result
348  {
350  }
351 
352  return length;
353  }
354 
355  std::ostream& SecurityBlock::serialize(std::ostream &stream, dtn::data::Length &) const
356  {
357  stream << _ciphersuite_id << _ciphersuite_flags;
358 
359  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
360  {
361  stream << _correlator;
362  }
363 
364  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
365  {
366  stream << _ciphersuite_params;
367  }
368 
369  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
370  {
371  stream << _security_result;
372  }
373 
374  return stream;
375  }
376 
377  std::ostream& SecurityBlock::serialize_strict(std::ostream &stream, dtn::data::Length &) const
378  {
379  stream << _ciphersuite_id << _ciphersuite_flags;
380 
381  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
382  {
383  stream << _correlator;
384  }
385 
386  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
387  {
388  stream << _ciphersuite_params;
389  }
390 
391  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
392  {
394  }
395 
396  return stream;
397  }
398 
399  std::istream& SecurityBlock::deserialize(std::istream &stream, const dtn::data::Length&)
400  {
401 #ifdef __DEVELOPMENT_ASSERTIONS__
402  // recheck blocktype. if blocktype is set wrong, this will be a huge fail
404 #endif
405 
406  stream >> _ciphersuite_id >> _ciphersuite_flags;
407 
408 #ifdef __DEVELOPMENT_ASSERTIONS__
409  // recheck ciphersuite_id
411  // recheck ciphersuite_flags, could be more exhaustive
412  assert(_ciphersuite_flags < 32);
413 #endif
414 
415  // copy security source and destination
416  if (_ciphersuite_flags & SecurityBlock::CONTAINS_SECURITY_SOURCE)
417  {
418  if (_eids.size() == 0)
419  throw dtn::SerializationFailedException("ciphersuite flags indicate a security source, but it is not present");
420 
421  _security_source = _eids.front();
422  }
423 
424  if (_ciphersuite_flags & SecurityBlock::CONTAINS_SECURITY_DESTINATION)
425  {
426  if (_ciphersuite_flags & SecurityBlock::CONTAINS_SECURITY_SOURCE)
427  {
428  if (_eids.size() < 2)
429  throw dtn::SerializationFailedException("ciphersuite flags indicate a security destination, but it is not present");
430 
431  _security_destination = *(++_eids.begin());
432  }
433  else
434  {
435  if (_eids.size() == 0)
436  throw dtn::SerializationFailedException("ciphersuite flags indicate a security destination, but it is not present");
437 
438  _security_destination = _eids.front();
439  }
440  }
441 
442  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
443  {
444  stream >> _correlator;
445  }
446  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
447  {
448  stream >> _ciphersuite_params;
449 #ifdef __DEVELOPMENT_ASSERTIONS__
450  assert(_ciphersuite_params.getLength() > 0);
451 #endif
452  }
453 
454  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
455  {
456  stream >> _security_result;
457 #ifdef __DEVELOPMENT_ASSERTIONS__
458  assert(_security_result.getLength() > 0);
459 #endif
460  }
461 
462  return stream;
463  }
464 
466  {
467  serializer << _ciphersuite_id;
468  serializer << _ciphersuite_flags;
469 
470  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
471  serializer << _ciphersuite_flags;
472 
473  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
474  {
475  serializer << _ciphersuite_params;
476  }
477 
478  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
479  {
480  serializer << _security_result;
481  }
482 
483  return serializer;
484  }
485 
487  {
488  serializer << _ciphersuite_id;
489  serializer << _ciphersuite_flags;
490 
491  if (_ciphersuite_flags & CONTAINS_CORRELATOR)
492  serializer << _ciphersuite_flags;
493 
494  if (_ciphersuite_flags & CONTAINS_CIPHERSUITE_PARAMS)
495  {
496  serializer << _ciphersuite_params;
497  }
498 
499  if (_ciphersuite_flags & CONTAINS_SECURITY_RESULT)
500  {
501  serializer << dtn::data::Number(getSecurityResultSize());
502  }
503 
504  return serializer;
505  }
506 
508  {
509 #ifdef __DEVELOPMENT_ASSERTIONS__
510  assert(_security_result.getLength() != 0);
511 #endif
512  return _security_result.getLength();
513  }
514 
515  void SecurityBlock::createSaltAndKey(uint32_t& salt, unsigned char* key, dtn::data::Length key_size)
516  {
517 
518  if (!RAND_bytes(reinterpret_cast<unsigned char *>(&salt), sizeof(uint32_t)))
519  {
520  IBRCOMMON_LOGGER_ex(critical) << "failed to generate salt. maybe /dev/urandom is missing for seeding the PRNG" << IBRCOMMON_LOGGER_ENDL;
521  ERR_print_errors_fp(stderr);
522  }
523  if (!RAND_bytes(key, static_cast<int>(key_size)))
524  {
525  IBRCOMMON_LOGGER_ex(critical) << "failed to generate key. maybe /dev/urandom is missing for seeding the PRNG" << IBRCOMMON_LOGGER_ENDL;
526  ERR_print_errors_fp(stderr);
527  }
528  }
529 
530  void SecurityBlock::addKey(TLVList& security_parameter, unsigned char const * const key, dtn::data::Length key_size, RSA * rsa)
531  {
532  // encrypt the ephemeral key and place it in _ciphersuite_params
533 #ifdef __DEVELOPMENT_ASSERTIONS__
534  assert(key_size < (dtn::data::Length)RSA_size(rsa)-41);
535 #endif
536  std::vector<unsigned char> encrypted_key(RSA_size(rsa));
537  int encrypted_key_len = RSA_public_encrypt(static_cast<int>(key_size), key, &encrypted_key[0], rsa, RSA_PKCS1_OAEP_PADDING);
538  if (encrypted_key_len == -1)
539  {
540  IBRCOMMON_LOGGER_ex(critical) << "failed to encrypt the symmetric AES key" << IBRCOMMON_LOGGER_ENDL;
541  ERR_print_errors_fp(stderr);
542  return;
543  }
544  security_parameter.set(SecurityBlock::key_information, std::string(reinterpret_cast<char *>(&encrypted_key[0]), encrypted_key_len));
545  }
546 
547  bool SecurityBlock::getKey(const TLVList& security_parameter, unsigned char * key, dtn::data::Length key_size, RSA * rsa)
548  {
549  std::string key_string = security_parameter.get(SecurityBlock::key_information);
550  // get key, convert with reinterpret_cast
551  const unsigned char *encrypted_key = reinterpret_cast<const unsigned char*>(key_string.c_str());
552  std::vector<unsigned char> the_key(RSA_size(rsa));
553  RSA_blinding_on(rsa, NULL);
554  int plaintext_key_len = RSA_private_decrypt(static_cast<int>(key_string.size()), encrypted_key, &the_key[0], rsa, RSA_PKCS1_OAEP_PADDING);
555  RSA_blinding_off(rsa);
556  if (plaintext_key_len == -1)
557  {
558  IBRCOMMON_LOGGER_ex(critical) << "failed to decrypt the symmetric AES key" << IBRCOMMON_LOGGER_ENDL;
559  ERR_print_errors_fp(stderr);
560  return false;
561  }
562 #ifdef __DEVELOPMENT_ASSERTIONS__
563  assert((dtn::data::Length)plaintext_key_len == key_size);
564 #endif
565  std::copy(&the_key[0], &the_key[key_size], key);
566  return true;
567  }
568 
569  void SecurityBlock::copyEID(const Block& from, Block& to, dtn::data::Length skip)
570  {
571  // take eid list, getEIDList() is broken
572  std::list<dtn::data::EID> their_eids = from.getEIDList();
573  std::list<dtn::data::EID>::iterator it = their_eids.begin();
574 
575  while (it != their_eids.end() && skip > 0)
576  {
577  skip--;
578  ++it;
579  }
580 
581  for (; it != their_eids.end(); ++it)
582  to.addEID(*it);
583  }
584 
585  void SecurityBlock::addSalt(TLVList& security_parameters, const uint32_t &salt)
586  {
587  uint32_t nsalt = htonl(salt);
588  security_parameters.set(SecurityBlock::salt, (const unsigned char*)&nsalt, sizeof(nsalt));
589  }
590 
591  uint32_t SecurityBlock::getSalt(const TLVList& security_parameters)
592  {
593  uint32_t nsalt = 0;
594  security_parameters.get(SecurityBlock::salt, (unsigned char*)&nsalt, sizeof(nsalt));
595  return ntohl(nsalt);
596  }
597 
599  {
600  const dtn::security::SecurityBlock &block = dynamic_cast<const dtn::security::SecurityBlock&>(**it);
601 
602  // the array for the extracted tag
603  unsigned char tag[ibrcommon::AES128Stream::tag_len];
604 
605  // the array for the extracted iv
606  unsigned char iv[ibrcommon::AES128Stream::iv_len];
607 
608  // get iv, convert with reinterpret_cast
610 
611  // get data and tag, the last tag_len bytes are the tag. cut them of and reinterpret_cast
612  std::string block_data = block._security_result.get(SecurityBlock::encapsulated_block);
613 
614  // create a pointer to the tag begin
615  const char *tag_p = block_data.c_str() + (block_data.size() - ibrcommon::AES128Stream::tag_len);
616 
617  // copy the tag
618  ::memcpy(tag, tag_p, ibrcommon::AES128Stream::tag_len);
619 
620  // strip off the tag from block data
621  block_data.resize(block_data.size() - ibrcommon::AES128Stream::tag_len);
622 
623  // decrypt block
624  std::stringstream plaintext;
625  ibrcommon::AES128Stream decrypt(ibrcommon::CipherStream::CIPHER_DECRYPT, plaintext, key, salt, iv);
626  decrypt << block_data << std::flush;
627 
628  // verify the decrypt tag
629  if (!decrypt.verify(tag))
630  {
631  throw ibrcommon::Exception("decryption of block failed - tag is bad");
632  }
633 
634  // deserialize block
635  dtn::data::DefaultDeserializer ddser(plaintext);
636 
637  // peek the block type
638  dtn::data::block_t block_type = (dtn::data::block_t)plaintext.peek();
639 
640  // get the position of "block"
641  dtn::data::Bundle::iterator b_it = bundle.find(block);
642 
643  if (block_type == dtn::data::PayloadBlock::BLOCK_TYPE)
644  {
645  dtn::data::PayloadBlock &plaintext_block = bundle.insert<dtn::data::PayloadBlock>(b_it);
646  ddser >> plaintext_block;
647  }
648  else
649  {
650  try {
652  dtn::data::Block &plaintext_block = bundle.insert(b_it, f);
653  ddser >> plaintext_block;
654 
655  plaintext_block.clearEIDs();
656 
657  // copy eids
658  // remove security_source and destination
659  dtn::data::Length skip = 0;
661  skip++;
663  skip++;
664  copyEID(plaintext_block, plaintext_block, skip);
665 
666  } catch (const ibrcommon::Exception &ex) {
667  dtn::data::ExtensionBlock &plaintext_block = bundle.insert<dtn::data::ExtensionBlock>(b_it);
668  ddser >> plaintext_block;
669 
670  plaintext_block.clearEIDs();
671 
672  // copy eids
673  // remove security_source and destination
674  dtn::data::Length skip = 0;
676  skip++;
678  skip++;
679  copyEID(plaintext_block, plaintext_block, skip);
680  }
681  }
682 
683  bundle.remove(block);
684  }
685 
686  void SecurityBlock::addFragmentRange(TLVList& ciphersuite_params, const dtn::data::Number &offset, const dtn::data::Number &range)
687  {
688  std::stringstream ss;
689  ss << offset << range;
690 
691  ciphersuite_params.set(SecurityBlock::fragment_range, ss.str());
692  }
693 
695  {
696  IBRCOMMON_LOGGER_DEBUG_TAG("SecurityBlock", 30) << "check security source: " << getSecuritySource(bundle).getString() << " == " << eid.getNode().getString() << IBRCOMMON_LOGGER_ENDL;
697  return getSecuritySource(bundle) == eid.getNode();
698  }
699 
701  {
702  IBRCOMMON_LOGGER_DEBUG_TAG("SecurityBlock", 30) << "check security destination: " << getSecurityDestination(bundle).getString() << " == " << eid.getNode().getString() << IBRCOMMON_LOGGER_ENDL;
703  return getSecurityDestination(bundle) == eid.getNode();
704  }
705 
707  {
709  if (source == dtn::data::EID())
710  source = bundle.source.getNode();
711  return source;
712  }
713 
715  {
716  dtn::data::EID destination = getSecurityDestination();
717  if (destination == dtn::data::EID())
718  destination = bundle.destination.getNode();
719  return destination;
720  }
721  }
722 }