Panda3D
encryptStreamBuf.cxx
1 // Filename: encryptStreamBuf.cxx
2 // Created by: drose (01Sep04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "encryptStreamBuf.h"
16 #include "config_prc.h"
17 #include "streamReader.h"
18 #include "streamWriter.h"
19 #include "configVariableInt.h"
20 #include "configVariableString.h"
21 
22 #ifdef HAVE_OPENSSL
23 
24 #include "openssl/rand.h"
25 
26 #ifndef HAVE_STREAMSIZE
27 // Some compilers (notably SGI) don't define this for us
28 typedef int streamsize;
29 #endif /* HAVE_STREAMSIZE */
30 
31 // The iteration count is scaled by this factor for writing to the
32 // stream.
33 static const int iteration_count_factor = 1000;
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: EncryptStreamBuf::Constructor
37 // Access: Public
38 // Description:
39 ////////////////////////////////////////////////////////////////////
40 EncryptStreamBuf::
41 EncryptStreamBuf() {
42  _source = (istream *)NULL;
43  _owns_source = false;
44  _dest = (ostream *)NULL;
45  _owns_dest = false;
46 
47  ConfigVariableString encryption_algorithm
48  ("encryption-algorithm", "bf-cbc",
49  PRC_DESC("This defines the OpenSSL encryption algorithm which is used to "
50  "encrypt any streams created by the current runtime. The default is "
51  "Blowfish; the complete set of available algorithms is defined by "
52  "the current version of OpenSSL. This value is used only to control "
53  "encryption; the correct algorithm will automatically be selected on "
54  "decryption."));
55 
56  ConfigVariableInt encryption_key_length
57  ("encryption-key-length", 0,
58  PRC_DESC("This defines the key length, in bits, for the selected encryption "
59  "algorithm. Some algorithms have a variable key length. Specifying "
60  "a value of 0 here means to use the default key length for the "
61  "algorithm as defined by OpenSSL. This value is used only to "
62  "control encryption; the correct key length will automatically be "
63  "selected on decryption."));
64 
65  ConfigVariableInt encryption_iteration_count
66  ("encryption-iteration-count", 100000,
67  PRC_DESC("This defines the number of times a password is hashed to generate a "
68  "key when encrypting. Its purpose is to make it computationally "
69  "more expensive for an attacker to search the key space "
70  "exhaustively. This should be a multiple of 1,000 and should not "
71  "exceed about 65 million; the value 0 indicates just one application "
72  "of the hashing algorithm. This value is used only to control "
73  "encryption; the correct count will automatically be selected on "
74  "decryption."));
75 
76  _algorithm = encryption_algorithm;
77  _key_length = encryption_key_length;
78  _iteration_count = encryption_iteration_count;
79 
80  _read_valid = false;
81  _write_valid = false;
82 
83  _read_overflow_buffer = NULL;
84  _in_read_overflow_buffer = 0;
85 
86 #ifdef PHAVE_IOSTREAM
87  char *buf = new char[4096];
88  char *ebuf = buf + 4096;
89  setg(buf, ebuf, ebuf);
90  setp(buf, ebuf);
91 
92 #else
93  allocate();
94  setg(base(), ebuf(), ebuf());
95  setp(base(), ebuf());
96 #endif
97 }
98 
99 ////////////////////////////////////////////////////////////////////
100 // Function: EncryptStreamBuf::Destructor
101 // Access: Public, Virtual
102 // Description:
103 ////////////////////////////////////////////////////////////////////
104 EncryptStreamBuf::
105 ~EncryptStreamBuf() {
106  close_read();
107  close_write();
108 }
109 
110 ////////////////////////////////////////////////////////////////////
111 // Function: EncryptStreamBuf::open_read
112 // Access: Public
113 // Description:
114 ////////////////////////////////////////////////////////////////////
115 void EncryptStreamBuf::
116 open_read(istream *source, bool owns_source, const string &password) {
117  OpenSSL_add_all_algorithms();
118 
119  _source = source;
120  _owns_source = owns_source;
121  _read_valid = false;
122 
123  // Now read the header information.
124  StreamReader sr(_source, false);
125  int nid = sr.get_uint16();
126  int key_length = sr.get_uint16();
127  int count = sr.get_uint16();
128 
129  const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid);
130 
131  if (cipher == NULL) {
132  prc_cat.error()
133  << "Unknown encryption algorithm in stream.\n";
134  return;
135  }
136 
137  _algorithm = OBJ_nid2sn(nid);
138  _key_length = key_length * 8;
139  _iteration_count = count * iteration_count_factor;
140 
141  if (prc_cat.is_debug()) {
142  prc_cat.debug()
143  << "Using decryption algorithm " << _algorithm << " with key length "
144  << _key_length << " bits.\n";
145  prc_cat.debug()
146  << "Key is hashed " << _iteration_count << " extra times.\n";
147  }
148 
149  int iv_length = EVP_CIPHER_iv_length(cipher);
150  _read_block_size = EVP_CIPHER_block_size(cipher);
151 
152  string iv = sr.extract_bytes(iv_length);
153 
154  // Initialize the context
155  int result;
156  result = EVP_DecryptInit(&_read_ctx, cipher, NULL, (unsigned char *)iv.data());
157  nassertv(result > 0);
158 
159  result = EVP_CIPHER_CTX_set_key_length(&_read_ctx, key_length);
160  if (result <= 0) {
161  prc_cat.error()
162  << "Invalid key length " << key_length * 8 << " bits for algorithm "
163  << OBJ_nid2sn(nid) << "\n";
164  EVP_CIPHER_CTX_cleanup(&_read_ctx);
165  return;
166  }
167 
168  // Hash the supplied password into a key of the appropriate length.
169  unsigned char *key = (unsigned char *)alloca(key_length);
170  result =
171  PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(),
172  (unsigned char *)iv.data(), iv.length(),
173  count * iteration_count_factor + 1,
174  key_length, key);
175  nassertv(result > 0);
176 
177  // Store the key within the context.
178  result = EVP_DecryptInit(&_read_ctx, NULL, key, NULL);
179  nassertv(result > 0);
180 
181  _read_valid = true;
182 
183  _read_overflow_buffer = new unsigned char[_read_block_size];
184  _in_read_overflow_buffer = 0;
185  thread_consider_yield();
186 }
187 
188 ////////////////////////////////////////////////////////////////////
189 // Function: EncryptStreamBuf::close_read
190 // Access: Public
191 // Description:
192 ////////////////////////////////////////////////////////////////////
193 void EncryptStreamBuf::
194 close_read() {
195  if (_read_valid) {
196  EVP_CIPHER_CTX_cleanup(&_read_ctx);
197  _read_valid = false;
198  }
199 
200  if (_read_overflow_buffer != (unsigned char *)NULL) {
201  delete[] _read_overflow_buffer;
202  _read_overflow_buffer = NULL;
203  }
204 
205  if (_source != (istream *)NULL) {
206  if (_owns_source) {
207  delete _source;
208  _owns_source = false;
209  }
210  _source = (istream *)NULL;
211  }
212 }
213 
214 ////////////////////////////////////////////////////////////////////
215 // Function: EncryptStreamBuf::open_write
216 // Access: Public
217 // Description:
218 ////////////////////////////////////////////////////////////////////
219 void EncryptStreamBuf::
220 open_write(ostream *dest, bool owns_dest, const string &password) {
221  OpenSSL_add_all_algorithms();
222 
223  close_write();
224  _dest = dest;
225  _owns_dest = owns_dest;
226  _write_valid = false;
227 
228  const EVP_CIPHER *cipher =
229  EVP_get_cipherbyname(_algorithm.c_str());
230 
231  if (cipher == NULL) {
232  prc_cat.error()
233  << "Unknown encryption algorithm: " << _algorithm << "\n";
234  return;
235  };
236 
237  int nid = EVP_CIPHER_nid(cipher);
238 
239  int iv_length = EVP_CIPHER_iv_length(cipher);
240  _write_block_size = EVP_CIPHER_block_size(cipher);
241 
242  unsigned char *iv = (unsigned char *)alloca(iv_length);
243 
244  // Generate a random IV. It doesn't need to be cryptographically
245  // secure, just unique.
246  RAND_pseudo_bytes(iv, iv_length);
247 
248  int result;
249  result = EVP_EncryptInit(&_write_ctx, cipher, NULL, iv);
250  nassertv(result > 0);
251 
252  // Store the appropriate key length in the context.
253  int key_length = (_key_length + 7) / 8;
254  if (key_length == 0) {
255  key_length = EVP_CIPHER_key_length(cipher);
256  }
257  result = EVP_CIPHER_CTX_set_key_length(&_write_ctx, key_length);
258  if (result <= 0) {
259  prc_cat.error()
260  << "Invalid key length " << key_length * 8 << " bits for algorithm "
261  << OBJ_nid2sn(nid) << "\n";
262  EVP_CIPHER_CTX_cleanup(&_write_ctx);
263  return;
264  }
265 
266  int count = _iteration_count / iteration_count_factor;
267 
268  if (prc_cat.is_debug()) {
269  prc_cat.debug()
270  << "Using encryption algorithm " << OBJ_nid2sn(nid) << " with key length "
271  << key_length * 8 << " bits.\n";
272  prc_cat.debug()
273  << "Hashing key " << count * iteration_count_factor
274  << " extra times.\n";
275  }
276 
277  // Hash the supplied password into a key of the appropriate length.
278  unsigned char *key = (unsigned char *)alloca(key_length);
279  result =
280  PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(),
281  iv, iv_length, count * iteration_count_factor + 1,
282  key_length, key);
283  nassertv(result > 0);
284 
285  // Store the key in the context.
286  result = EVP_EncryptInit(&_write_ctx, NULL, key, NULL);
287  nassertv(result > 0);
288 
289  // Now write the header information to the stream.
290  StreamWriter sw(_dest, false);
291  nassertv((PN_uint16)nid == nid);
292  sw.add_uint16(nid);
293  nassertv((PN_uint16)key_length == key_length);
294  sw.add_uint16(key_length);
295  nassertv((PN_uint16)count == count);
296  sw.add_uint16(count);
297  sw.append_data(iv, iv_length);
298 
299  _write_valid = true;
300  thread_consider_yield();
301 }
302 
303 ////////////////////////////////////////////////////////////////////
304 // Function: EncryptStreamBuf::close_write
305 // Access: Public
306 // Description:
307 ////////////////////////////////////////////////////////////////////
308 void EncryptStreamBuf::
309 close_write() {
310  if (_dest != (ostream *)NULL) {
311  size_t n = pptr() - pbase();
312  write_chars(pbase(), n);
313  pbump(-(int)n);
314 
315  if (_write_valid) {
316  unsigned char *write_buffer = (unsigned char *)alloca(_write_block_size);
317  int bytes_written = 0;
318  EVP_EncryptFinal(&_write_ctx, write_buffer, &bytes_written);
319  thread_consider_yield();
320 
321  _dest->write((const char *)write_buffer, bytes_written);
322 
323  _write_valid = false;
324  }
325 
326  if (_owns_dest) {
327  delete _dest;
328  _owns_dest = false;
329  }
330  _dest = (ostream *)NULL;
331  }
332 }
333 
334 ////////////////////////////////////////////////////////////////////
335 // Function: EncryptStreamBuf::overflow
336 // Access: Protected, Virtual
337 // Description: Called by the system ostream implementation when its
338 // internal buffer is filled, plus one character.
339 ////////////////////////////////////////////////////////////////////
340 int EncryptStreamBuf::
341 overflow(int ch) {
342  size_t n = pptr() - pbase();
343  if (n != 0) {
344  write_chars(pbase(), n);
345  pbump(-(int)n);
346  }
347 
348  if (ch != EOF) {
349  // Write one more character.
350  char c = ch;
351  write_chars(&c, 1);
352  }
353 
354  return 0;
355 }
356 
357 ////////////////////////////////////////////////////////////////////
358 // Function: EncryptStreamBuf::sync
359 // Access: Protected, Virtual
360 // Description: Called by the system iostream implementation to
361 // implement a flush operation.
362 ////////////////////////////////////////////////////////////////////
363 int EncryptStreamBuf::
364 sync() {
365  if (_source != (istream *)NULL) {
366  size_t n = egptr() - gptr();
367  gbump(n);
368  }
369 
370  if (_dest != (ostream *)NULL) {
371  size_t n = pptr() - pbase();
372  write_chars(pbase(), n);
373  pbump(-(int)n);
374  }
375 
376  _dest->flush();
377  return 0;
378 }
379 
380 ////////////////////////////////////////////////////////////////////
381 // Function: EncryptStreamBuf::underflow
382 // Access: Protected, Virtual
383 // Description: Called by the system istream implementation when its
384 // internal buffer needs more characters.
385 ////////////////////////////////////////////////////////////////////
386 int EncryptStreamBuf::
387 underflow() {
388  // Sometimes underflow() is called even if the buffer is not empty.
389  if (gptr() >= egptr()) {
390  size_t buffer_size = egptr() - eback();
391  gbump(-(int)buffer_size);
392 
393  size_t num_bytes = buffer_size;
394  size_t read_count = read_chars(gptr(), buffer_size);
395 
396  if (read_count != num_bytes) {
397  // Oops, we didn't read what we thought we would.
398  if (read_count == 0) {
399  gbump(num_bytes);
400  return EOF;
401  }
402 
403  // Slide what we did read to the top of the buffer.
404  nassertr(read_count < num_bytes, EOF);
405  size_t delta = num_bytes - read_count;
406  memmove(gptr() + delta, gptr(), read_count);
407  gbump(delta);
408  }
409  }
410 
411  return (unsigned char)*gptr();
412 }
413 
414 
415 ////////////////////////////////////////////////////////////////////
416 // Function: EncryptStreamBuf::read_chars
417 // Access: Private
418 // Description: Gets some characters from the source stream.
419 ////////////////////////////////////////////////////////////////////
420 size_t EncryptStreamBuf::
421 read_chars(char *start, size_t length) {
422  if (length == 0) {
423  return 0;
424  }
425 
426  if (_in_read_overflow_buffer != 0) {
427  // Take from the overflow buffer.
428  length = min(length, _in_read_overflow_buffer);
429  memcpy(start, _read_overflow_buffer, length);
430  _in_read_overflow_buffer -= length;
431  memcpy(_read_overflow_buffer + length, _read_overflow_buffer, _in_read_overflow_buffer);
432  return length;
433  }
434 
435  unsigned char *source_buffer = (unsigned char *)alloca(length);
436  size_t max_read_buffer = length + _read_block_size;
437  unsigned char *read_buffer = (unsigned char *)alloca(max_read_buffer);
438 
439  int bytes_read = 0;
440 
441  do {
442  // Get more bytes from the stream.
443  if (!_read_valid) {
444  return 0;
445  }
446 
447  _source->read((char *)source_buffer, length);
448  size_t source_length = _source->gcount();
449 
450  bytes_read = 0;
451  int result;
452  if (source_length != 0) {
453  result =
454  EVP_DecryptUpdate(&_read_ctx, read_buffer, &bytes_read,
455  source_buffer, source_length);
456  } else {
457  result =
458  EVP_DecryptFinal(&_read_ctx, read_buffer, &bytes_read);
459  _read_valid = false;
460  }
461 
462  if (result <= 0) {
463  prc_cat.error()
464  << "Error decrypting stream.\n";
465  if (_read_valid) {
466  EVP_CIPHER_CTX_cleanup(&_read_ctx);
467  _read_valid = false;
468  }
469  }
470  thread_consider_yield();
471 
472  } while (bytes_read == 0);
473 
474  // Now store the read bytes in the output stream.
475  if ((size_t)bytes_read <= length) {
476  // No overflow.
477  memcpy(start, read_buffer, bytes_read);
478  return bytes_read;
479 
480  } else {
481  // We have to save some of the returned bytes in the overflow
482  // buffer.
483  _in_read_overflow_buffer = bytes_read - length;
484  nassertr(_in_read_overflow_buffer <= _read_block_size, 0);
485 
486  memcpy(_read_overflow_buffer, read_buffer + length,
487  _in_read_overflow_buffer);
488  memcpy(start, read_buffer, length);
489  return length;
490  }
491 }
492 
493 ////////////////////////////////////////////////////////////////////
494 // Function: EncryptStreamBuf::write_chars
495 // Access: Private
496 // Description: Sends some characters to the dest stream.
497 ////////////////////////////////////////////////////////////////////
498 void EncryptStreamBuf::
499 write_chars(const char *start, size_t length) {
500  if (_write_valid && length != 0) {
501  size_t max_write_buffer = length + _write_block_size;
502  unsigned char *write_buffer = (unsigned char *)alloca(max_write_buffer);
503 
504  int bytes_written = 0;
505  int result =
506  EVP_EncryptUpdate(&_write_ctx, write_buffer, &bytes_written,
507  (unsigned char *)start, length);
508  if (result <= 0) {
509  prc_cat.error()
510  << "Error encrypting stream.\n";
511  }
512  thread_consider_yield();
513  _dest->write((const char *)write_buffer, bytes_written);
514  }
515 }
516 
517 #endif // HAVE_OPENSSL
A StreamWriter object is used to write sequential binary data directly to an ostream.
Definition: streamWriter.h:33
This is a convenience class to specialize ConfigVariable as a string type.
This is a convenience class to specialize ConfigVariable as an integer type.
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:30