23#include <openssl/rand.h>
24#include <openssl/evp.h>
26#if OPENSSL_VERSION_MAJOR >= 3
27#include <openssl/provider.h>
33static bool load_legacy_provider() {
34 static bool tried =
false;
37 if (OSSL_PROVIDER_try_load(
nullptr,
"legacy", 1) !=
nullptr) {
38 if (prc_cat.is_debug()) {
40 <<
"Loaded legacy OpenSSL provider.\n";
45 <<
"Failed to load legacy OpenSSL provider.\n";
53static const int iteration_count_factor = 1000;
66 (
"encryption-algorithm",
"bf-cbc",
67 PRC_DESC(
"This defines the OpenSSL encryption algorithm which is used to "
68 "encrypt any streams created by the current runtime. The default is "
69 "Blowfish; the complete set of available algorithms is defined by "
70 "the current version of OpenSSL. This value is used only to control "
71 "encryption; the correct algorithm will automatically be selected on "
75 (
"encryption-key-length", 0,
76 PRC_DESC(
"This defines the key length, in bits, for the selected encryption "
77 "algorithm. Some algorithms have a variable key length. Specifying "
78 "a value of 0 here means to use the default key length for the "
79 "algorithm as defined by OpenSSL. This value is used only to "
80 "control encryption; the correct key length will automatically be "
81 "selected on decryption."));
84 (
"encryption-iteration-count", 100000,
85 PRC_DESC(
"This defines the number of times a password is hashed to generate a "
86 "key when encrypting. Its purpose is to make it computationally "
87 "more expensive for an attacker to search the key space "
88 "exhaustively. This should be a multiple of 1,000 and should not "
89 "exceed about 65 million; the value 0 indicates just one application "
90 "of the hashing algorithm. This value is used only to control "
91 "encryption; the correct count will automatically be selected on "
94 _algorithm = encryption_algorithm;
95 _key_length = encryption_key_length;
96 _iteration_count = encryption_iteration_count;
101 _read_overflow_buffer =
nullptr;
102 _in_read_overflow_buffer = 0;
105 char *buf =
new char[4096];
106 char *ebuf = buf + 4096;
107 setg(buf, ebuf, ebuf);
112 setg(base(), ebuf(), ebuf());
113 setp(base(), ebuf());
133void EncryptStreamBuf::
134open_read(std::istream *source,
bool owns_source,
const std::string &password) {
135 OpenSSL_add_all_algorithms();
138 _owns_source = owns_source;
140 if (_read_ctx !=
nullptr) {
141 EVP_CIPHER_CTX_free(_read_ctx);
147 int nid = sr.get_uint16();
148 int key_length = sr.get_uint16();
149 int count = sr.get_uint16();
151#if OPENSSL_VERSION_MAJOR >= 3
153 const char *cipher_name = OBJ_nid2ln(nid);
155 const EVP_CIPHER *cipher =
nullptr;
156 if (cipher_name !=
nullptr) {
158 cipher = EVP_CIPHER_fetch(
nullptr, cipher_name,
nullptr);
160 if (cipher ==
nullptr && EVP_get_cipherbynid(nid) !=
nullptr) {
161 if (load_legacy_provider()) {
162 cipher = EVP_CIPHER_fetch(
nullptr, cipher_name,
nullptr);
165 if (cipher ==
nullptr) {
167 <<
"No implementation available for encryption algorithm in stream: "
168 << cipher_name <<
"\n";
174 const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid);
177 if (cipher ==
nullptr) {
179 <<
"Unknown encryption algorithm in stream.\n";
183 _algorithm = OBJ_nid2sn(nid);
184 _key_length = key_length * 8;
185 _iteration_count = count * iteration_count_factor;
187 if (prc_cat.is_debug()) {
189 <<
"Using decryption algorithm " << _algorithm <<
" with key length "
190 << _key_length <<
" bits.\n";
192 <<
"Key is hashed " << _iteration_count <<
" extra times.\n";
195 int iv_length = EVP_CIPHER_iv_length(cipher);
196 _read_block_size = EVP_CIPHER_block_size(cipher);
198 unsigned char *iv = (
unsigned char *)alloca(iv_length);
199 iv_length = (int)sr.extract_bytes(iv, iv_length);
201 _read_ctx = EVP_CIPHER_CTX_new();
202 nassertv(_read_ctx !=
nullptr);
206 result = EVP_DecryptInit(_read_ctx, cipher,
nullptr, (
unsigned char *)iv);
207 nassertv(result > 0);
209 result = EVP_CIPHER_CTX_set_key_length(_read_ctx, key_length);
212 <<
"Invalid key length " << key_length * 8 <<
" bits for algorithm "
213 << OBJ_nid2sn(nid) <<
"\n";
214 EVP_CIPHER_CTX_free(_read_ctx);
220 unsigned char *key = (
unsigned char *)alloca(key_length);
222 PKCS5_PBKDF2_HMAC_SHA1((
const char *)password.data(), password.length(),
224 count * iteration_count_factor + 1,
226 nassertv(result > 0);
229 result = EVP_DecryptInit(_read_ctx,
nullptr, key,
nullptr);
230 nassertv(result > 0);
232 _read_overflow_buffer =
new unsigned char[_read_block_size];
233 _in_read_overflow_buffer = 0;
234 thread_consider_yield();
240void EncryptStreamBuf::
242 if (_read_ctx !=
nullptr) {
243 EVP_CIPHER_CTX_free(_read_ctx);
247 if (_read_overflow_buffer !=
nullptr) {
248 delete[] _read_overflow_buffer;
249 _read_overflow_buffer =
nullptr;
252 if (_source !=
nullptr) {
255 _owns_source =
false;
264void EncryptStreamBuf::
265open_write(std::ostream *dest,
bool owns_dest,
const std::string &password) {
266 OpenSSL_add_all_algorithms();
270 _owns_dest = owns_dest;
272#if OPENSSL_VERSION_MAJOR >= 3
274 const EVP_CIPHER *cipher =
275 EVP_CIPHER_fetch(
nullptr, _algorithm.c_str(),
nullptr);
277 if (cipher ==
nullptr &&
278 EVP_get_cipherbyname(_algorithm.c_str()) !=
nullptr) {
280 if (load_legacy_provider()) {
281 cipher = EVP_CIPHER_fetch(
nullptr, _algorithm.c_str(),
nullptr);
284 if (cipher ==
nullptr) {
286 <<
"No implementation available for encryption algorithm: "
287 << _algorithm <<
"\n";
292 const EVP_CIPHER *cipher =
293 EVP_get_cipherbyname(_algorithm.c_str());
296 if (cipher ==
nullptr) {
298 <<
"Unknown encryption algorithm: " << _algorithm <<
"\n";
302 int nid = EVP_CIPHER_nid(cipher);
304 int iv_length = EVP_CIPHER_iv_length(cipher);
305 _write_block_size = EVP_CIPHER_block_size(cipher);
309 unsigned char *iv = (
unsigned char *)alloca(iv_length);
310 RAND_bytes(iv, iv_length);
312 _write_ctx = EVP_CIPHER_CTX_new();
313 nassertv(_write_ctx !=
nullptr);
316 result = EVP_EncryptInit(_write_ctx, cipher,
nullptr, iv);
317 nassertv(result > 0);
320 int key_length = (_key_length + 7) / 8;
321 if (key_length == 0) {
322 key_length = EVP_CIPHER_key_length(cipher);
324 result = EVP_CIPHER_CTX_set_key_length(_write_ctx, key_length);
327 <<
"Invalid key length " << key_length * 8 <<
" bits for algorithm "
328 << OBJ_nid2sn(nid) <<
"\n";
329 EVP_CIPHER_CTX_free(_write_ctx);
330 _write_ctx =
nullptr;
334 int count = _iteration_count / iteration_count_factor;
336 if (prc_cat.is_debug()) {
338 <<
"Using encryption algorithm " << OBJ_nid2sn(nid) <<
" with key length "
339 << key_length * 8 <<
" bits.\n";
341 <<
"Hashing key " << count * iteration_count_factor
342 <<
" extra times.\n";
346 unsigned char *key = (
unsigned char *)alloca(key_length);
348 PKCS5_PBKDF2_HMAC_SHA1((
const char *)password.data(), password.length(),
349 iv, iv_length, count * iteration_count_factor + 1,
351 nassertv(result > 0);
354 result = EVP_EncryptInit(_write_ctx,
nullptr, key,
nullptr);
355 nassertv(result > 0);
359 nassertv((uint16_t)nid == nid);
360 sw.add_uint16((uint16_t)nid);
361 nassertv((uint16_t)key_length == key_length);
362 sw.add_uint16((uint16_t)key_length);
363 nassertv((uint16_t)count == count);
364 sw.add_uint16((uint16_t)count);
365 sw.append_data(iv, iv_length);
367 thread_consider_yield();
373void EncryptStreamBuf::
375 if (_dest !=
nullptr) {
376 size_t n = pptr() - pbase();
377 write_chars(pbase(), n);
380 if (_write_ctx !=
nullptr) {
381 unsigned char *write_buffer = (
unsigned char *)alloca(_write_block_size);
382 int bytes_written = 0;
383 EVP_EncryptFinal(_write_ctx, write_buffer, &bytes_written);
384 thread_consider_yield();
386 _dest->write((
const char *)write_buffer, bytes_written);
388 EVP_CIPHER_CTX_free(_write_ctx);
389 _write_ctx =
nullptr;
404int EncryptStreamBuf::
406 size_t n = pptr() - pbase();
408 write_chars(pbase(), n);
425int EncryptStreamBuf::
427 if (_source !=
nullptr) {
428 size_t n = egptr() - gptr();
432 if (_dest !=
nullptr) {
433 size_t n = pptr() - pbase();
434 write_chars(pbase(), n);
446int EncryptStreamBuf::
449 if (gptr() >= egptr()) {
450 size_t buffer_size = egptr() - eback();
451 gbump(-(
int)buffer_size);
453 size_t num_bytes = buffer_size;
454 size_t read_count = read_chars(gptr(), buffer_size);
456 if (read_count != num_bytes) {
458 if (read_count == 0) {
459 gbump((
int)num_bytes);
464 nassertr(read_count < num_bytes, EOF);
465 size_t delta = num_bytes - read_count;
466 memmove(gptr() + delta, gptr(), read_count);
471 return (
unsigned char)*gptr();
478size_t EncryptStreamBuf::
479read_chars(
char *start,
size_t length) {
484 if (_in_read_overflow_buffer != 0) {
486 length = std::min(length, _in_read_overflow_buffer);
487 memcpy(start, _read_overflow_buffer, length);
488 _in_read_overflow_buffer -= length;
489 memcpy(_read_overflow_buffer + length, _read_overflow_buffer, _in_read_overflow_buffer);
493 unsigned char *source_buffer = (
unsigned char *)alloca(length);
494 size_t max_read_buffer = length + _read_block_size;
495 unsigned char *read_buffer = (
unsigned char *)alloca(max_read_buffer);
501 if (_read_ctx ==
nullptr) {
505 _source->read((
char *)source_buffer, length);
506 size_t source_length = _source->gcount();
510 if (source_length != 0) {
512 EVP_DecryptUpdate(_read_ctx, read_buffer, &bytes_read,
513 source_buffer, source_length);
516 EVP_DecryptFinal(_read_ctx, read_buffer, &bytes_read);
517 EVP_CIPHER_CTX_free(_read_ctx);
523 <<
"Error decrypting stream.\n";
524 if (_read_ctx !=
nullptr) {
525 EVP_CIPHER_CTX_free(_read_ctx);
529 thread_consider_yield();
531 }
while (bytes_read == 0);
534 if ((
size_t)bytes_read <= length) {
536 memcpy(start, read_buffer, bytes_read);
541 _in_read_overflow_buffer = bytes_read - length;
542 nassertr(_in_read_overflow_buffer <= _read_block_size, 0);
544 memcpy(_read_overflow_buffer, read_buffer + length,
545 _in_read_overflow_buffer);
546 memcpy(start, read_buffer, length);
554void EncryptStreamBuf::
555write_chars(
const char *start,
size_t length) {
556 if (_write_ctx !=
nullptr && length != 0) {
557 size_t max_write_buffer = length + _write_block_size;
558 unsigned char *write_buffer = (
unsigned char *)alloca(max_write_buffer);
560 int bytes_written = 0;
562 EVP_EncryptUpdate(_write_ctx, write_buffer, &bytes_written,
563 (
unsigned char *)start, length);
566 <<
"Error encrypting stream.\n";
568 thread_consider_yield();
569 _dest->write((
const char *)write_buffer, bytes_written);
This is a convenience class to specialize ConfigVariable as an integer type.
This is a convenience class to specialize ConfigVariable as a string type.
A class to read sequential binary data directly from an istream.
A StreamWriter object is used to write sequential binary data directly to an ostream.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.