IBR-DTN  1.0.0
DHProtocol.cpp
Go to the documentation of this file.
1 /*
2  * DHProtocol.cpp
3  *
4  * Copyright (C) 2014 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  * Thomas Schrader <schrader.thomas@gmail.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */
22 
25 #include "core/BundleCore.h"
27 
28 #include <ibrcommon/ssl/HMacStream.h>
29 #include <ibrcommon/Logger.h>
30 
31 #include <openssl/rand.h>
32 #include <openssl/pem.h>
33 
34 #define DH_KEY_LENGTH 1024
35 
36 namespace dtn
37 {
38  namespace security
39  {
40  const std::string DHProtocol::TAG = "DHProtocol";
41 
42  DHProtocol::DHState::DHState()
43  : dh(NULL)
44  {
45  }
46 
47  DHProtocol::DHState::~DHState()
48  {
49  if (dh) DH_free(dh);
50  }
51 
53  : KeyExchangeProtocol(manager, 1), _dh_params(NULL), _auto_generate_params(false)
54  {
55  // check if auto generation of parameters is enabled
57  }
58 
60  {
61  if (_dh_params) DH_free(_dh_params);
62  }
63 
64  void DHProtocol::generate_params()
65  {
66  // if there are already parameters do not generate them again
67  if (_dh_params != NULL) return;
68 
69  // check if DH params already exist
70  if (_dh_params_file.exists())
71  {
72  // load DH params from file
73  FILE *fd = fopen(_dh_params_file.getPath().c_str(), "r");
74  if (fd != NULL)
75  {
76  _dh_params = PEM_read_DHparams(fd, NULL, NULL, NULL);
77  fclose(fd);
78 
79  // finish here, if the parameters are loaded from file
80  if (_dh_params != NULL) return;
81  }
82  }
83 
84  // only generate parameters if this feature is enabled
85  if (!_auto_generate_params)
86  throw ibrcommon::Exception("automatic generation of DH parameters disabled");
87 
88  struct timeval time;
89  ::gettimeofday(&time, NULL);
90 
91  // initialize random seed generator
92  RAND_seed(&time, sizeof time);
93 
94  // create new params
95  _dh_params = DH_new();
96 
97  IBRCOMMON_LOGGER_TAG(TAG, notice) << "Generate new DH parameters -- This may take a while!" << IBRCOMMON_LOGGER_ENDL;
98 
99  if (!DH_generate_parameters_ex(_dh_params, DH_KEY_LENGTH, DH_GENERATOR_2, NULL))
100  {
101  throw ibrcommon::Exception("Error while generating DH parameters");
102  }
103 
104  // store params in the file
105  FILE *fd = fopen(_dh_params_file.getPath().c_str(), "w");
106  if (fd != NULL)
107  {
108  PEM_write_DHparams(fd, _dh_params);
109  fclose(fd);
110  }
111 
112  IBRCOMMON_LOGGER_TAG(TAG, notice) << "New DH parameters generated" << IBRCOMMON_LOGGER_ENDL;
113  }
114 
116  {
117  // set path for DH params
118  _dh_params_file = SecurityKeyManager::getInstance().getFilePath("dh_params", "pem");
119 
120  try {
121  // generate parameters
122  generate_params();
123  } catch (const ibrcommon::Exception&) {
124  // generation failed
125  }
126  }
127 
128  KeyExchangeSession* DHProtocol::createSession(const dtn::data::EID &peer, unsigned int uniqueId)
129  {
130  return new KeyExchangeSession(getProtocol(), peer, uniqueId, new DHState());
131  }
132 
134  {
135  // get session state
136  DHState &state = session.getState<DHState>();
137 
138  // generate parameters
139  generate_params();
140 
141  // copy DH params for new context
142  state.dh = DHparams_dup(_dh_params);
143 
144  IBRCOMMON_LOGGER_DEBUG_TAG(TAG, 25) << "Checking DH parameters" << IBRCOMMON_LOGGER_ENDL;
145 
146  int codes;
147  if (!DH_check(state.dh, &codes))
148  {
149  throw ibrcommon::Exception("Error while checking DH parameters");
150  }
151 
152  IBRCOMMON_LOGGER_DEBUG_TAG(TAG, 25) << "Generate DH keys" << IBRCOMMON_LOGGER_ENDL;
153 
154  if (!DH_generate_key(state.dh))
155  {
156  throw ibrcommon::Exception("Error while generating DH key");
157  }
158 
159  // prepare request
160  KeyExchangeData request(KeyExchangeData::REQUEST, session);
161 
162  write(request, state.dh->pub_key);
163  write(request, state.dh->p);
164  write(request, state.dh->g);
165 
166  manager.submit(session, request);
167  }
168 
170  {
171  // get session state
172  DHState &state = session.getState<DHState>();
173 
174  switch (data.getStep())
175  {
176  case 0:
177  {
178  if (data.getAction() == KeyExchangeData::REQUEST)
179  {
180  BIGNUM* pub_key = BN_new();
181  read(data, &pub_key);
182 
183  // create new params
184  state.dh = DH_new();
185 
186  // read p and g paramter from message
187  read(data, &state.dh->p);
188  read(data, &state.dh->g);
189 
190  int codes;
191  if (!DH_check(state.dh, &codes))
192  {
193  throw ibrcommon::Exception("Error while checking DH parameters");
194  }
195 
196  IBRCOMMON_LOGGER_DEBUG_TAG(TAG, 25) << "Generate DH keys" << IBRCOMMON_LOGGER_ENDL;
197 
198  if (!DH_generate_key(state.dh))
199  {
200  throw ibrcommon::Exception("Error while generating DH key");
201  }
202 
203  unsigned char* secret;
204  long int length = sizeof(unsigned char) * (DH_size(state.dh));
205  if (NULL == (secret = (unsigned char*) OPENSSL_malloc(length)))
206  {
207  BN_free(pub_key);
208  throw ibrcommon::Exception("Error while allocating space for secret key");
209  }
210 
211  DH_compute_key(secret, pub_key, state.dh);
212 
213  state.secret.assign((const char*)secret, length);
214 
215  KeyExchangeData response(KeyExchangeData::RESPONSE, session);
216  write(response, state.dh->pub_key);
217 
218  manager.submit(session, response);
219 
220  BN_free(pub_key);
221  }
222  else
223  {
224  BIGNUM* pub_key = BN_new();
225  read(data, &pub_key);
226 
227  unsigned char* secret;
228  long int length = sizeof(unsigned char) * (DH_size(state.dh));
229  if(NULL == (secret = (unsigned char*) OPENSSL_malloc(length)))
230  {
231  BN_free(pub_key);
232  throw ibrcommon::Exception("Error while allocating space for secret key");
233  }
234 
235  DH_compute_key(secret, pub_key, state.dh);
236  state.secret.assign((char*) secret, length);
237 
238  // get local public key
240 
241  ibrcommon::HMacStream hstream(secret, (int) length);
242  hstream << pkey.getData() << std::flush;
243 
244  KeyExchangeData response(KeyExchangeData::REQUEST, session);
245  response.setStep(1);
246 
247  const dtn::data::BundleString hmac(ibrcommon::HashStream::extract(hstream));
248  response << hmac;
249 
250  const dtn::data::BundleString publicK(pkey.getData());
251  response << publicK;
252 
253  manager.submit(session, response);
254 
255  BN_free(pub_key);
256  }
257  break;
258  }
259 
260  case 1:
261  {
262  if (data.getAction() == KeyExchangeData::REQUEST)
263  {
265  data >> hmac;
266 
267  dtn::data::BundleString publicK;
268  data >> publicK;
269 
270  ibrcommon::HMacStream hmacstream((const unsigned char*) state.secret.c_str(), (int) state.secret.size());
271  hmacstream << (const std::string&)publicK << std::flush;
272 
273  if (hmac != ibrcommon::HMacStream::extract(hmacstream))
274  {
275  throw ibrcommon::Exception("Error while comparing hmac");
276  }
277 
278  // write key in tmp file
280 
281  // get local public key
283 
284  // create a HMAC from public key
285  ibrcommon::HMacStream hstream((const unsigned char*) state.secret.c_str(), (int) state.secret.size());
286  hstream << pkey.getData() << std::flush;
287 
288  // bundle erzeugen: EID, hmac/publicKey, response, round 1
289  KeyExchangeData response(KeyExchangeData::RESPONSE, session);
290  response.setStep(1);
291 
292  const dtn::data::BundleString local_hmac(ibrcommon::HashStream::extract(hstream));
293  response << local_hmac;
294 
295  const dtn::data::BundleString local_publicK(pkey.getData());
296  response << local_publicK;
297 
298  // send bundle to peer
299  manager.submit(session, response);
300 
301  // finish the key-exchange
302  manager.finish(session);
303  }
304  else
305  {
307  data >> hmac;
308 
309  dtn::data::BundleString publicK;
310  data >> publicK;
311 
312  ibrcommon::HMacStream hstream((unsigned char*) state.secret.c_str(), (int) state.secret.size());
313  hstream << publicK << std::flush;
314 
315  if (hmac != ibrcommon::HMacStream::extract(hstream))
316  {
317  throw ibrcommon::Exception("Error while comparing hmac");
318  }
319 
320  // write key in tmp file
322 
323  // finish the key-exchange
324  manager.finish(session);
325  }
326  break;
327  }
328  }
329  }
330 
331  void DHProtocol::write(std::ostream &stream, const BIGNUM* bn)
332  {
333  unsigned char* buf = new unsigned char[(BN_num_bits(bn) + 1) * sizeof(char)];
334  dtn::data::Number length = BN_bn2bin(bn, (unsigned char*)buf);
335 
336  if (length > 0)
337  {
338  stream << length;
339  stream.write((char*)buf, length.get<size_t>());
340  delete[] buf;
341  }
342  else
343  {
344  delete[] buf;
345  throw ibrcommon::Exception("Error while parsing BIGNUM to string");
346  }
347  }
348 
349  void DHProtocol::read(std::istream &stream, BIGNUM **bn)
350  {
351  dtn::data::Number length;
352  stream >> length;
353 
354  unsigned char* buf = new unsigned char[length.get<size_t>()];
355  stream.read((char*)buf, length.get<size_t>());
356  *bn = BN_bin2bn((unsigned char*)buf, (int) length.get<size_t>(), NULL);
357  delete[] buf;
358 
359  if (*bn == NULL)
360  {
361  throw ibrcommon::Exception("Error while parsing string to BIGNUM");
362  }
363  }
364  } /* namespace security */
365 } /* namespace dtn */
static Configuration & getInstance(bool reset=false)
static SecurityKeyManager & getInstance()
#define DH_KEY_LENGTH
Definition: DHProtocol.cpp:34
static dtn::data::EID local
Definition: BundleCore.h:79
const Configuration::Security & getSecurity() const
virtual void initialize()
Definition: DHProtocol.cpp:115
virtual void begin(KeyExchangeSession &session, KeyExchangeData &data)
Definition: DHProtocol.cpp:133
void putKey(const std::string &data, const dtn::security::SecurityKey::KeyType type, const dtn::security::SecurityKey::TrustLevel trust) const
const std::string TAG
Definition: dtnoutbox.cpp:62
const ibrcommon::File getFilePath(const std::string &keyword, const std::string &extension) const
virtual const std::string getData() const
Definition: SecurityKey.cpp:64
virtual KeyExchangeSession * createSession(const dtn::data::EID &peer, unsigned int uniqueId)
Definition: DHProtocol.cpp:128
void read(std::istream &stream)
Definition: SDNV.h:425
bool isGenerateDHParamsEnabled() const
Generate DH parameters automatically if necessary.
virtual void finish(KeyExchangeSession &session)=0
dtn::security::SecurityKey get(const dtn::data::EID &ref, const dtn::security::SecurityKey::KeyType type=dtn::security::SecurityKey::KEY_UNSPEC) const
virtual void submit(KeyExchangeSession &session, const KeyExchangeData &data)=0
virtual void step(KeyExchangeSession &session, KeyExchangeData &data)
Definition: DHProtocol.cpp:169
DHProtocol(KeyExchangeManager &manager)
Definition: DHProtocol.cpp:52