From 62f45b292ca360a6624a684a51506dae2cd1a11d Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 2 Sep 2004 16:29:26 +0000 Subject: [PATCH] remove explicit algorithm parameter (make config variable only), add key length variable --- panda/src/downloadertools/pencrypt.cxx | 14 +--- panda/src/express/config_express.cxx | 26 ++++++++ panda/src/express/config_express.h | 4 ++ panda/src/express/encryptStream.I | 10 ++- panda/src/express/encryptStream.cxx | 8 +-- panda/src/express/encryptStream.h | 9 +-- panda/src/express/encryptStreamBuf.cxx | 89 ++++++++++++++++++-------- panda/src/express/encryptStreamBuf.h | 3 +- panda/src/express/multifile.I | 41 ++---------- panda/src/express/multifile.cxx | 3 +- panda/src/express/multifile.h | 3 - 11 files changed, 113 insertions(+), 97 deletions(-) diff --git a/panda/src/downloadertools/pencrypt.cxx b/panda/src/downloadertools/pencrypt.cxx index a470ea65dc..039619d016 100644 --- a/panda/src/downloadertools/pencrypt.cxx +++ b/panda/src/downloadertools/pencrypt.cxx @@ -42,11 +42,6 @@ usage() { << "Options:\n\n" - << " -a algorithm\n" - << " Specifies the particular encryption algorithm to use. Available\n" - << " algorithm names are defined by OpenSSL. If this is unspecified a\n" - << " default algorithm is selected.\n\n" - << " -p \"password\"\n" << " Specifies the password to use for encryption. There are no\n" << " restrictions on the password length or contents, but longer passwords\n" @@ -58,9 +53,8 @@ int main(int argc, char *argv[]) { extern char *optarg; extern int optind; - const char *optstr = "a:p:h"; + const char *optstr = "p:h"; - string algorithm; string password; bool got_password = false; @@ -68,10 +62,6 @@ main(int argc, char *argv[]) { while (flag != EOF) { switch (flag) { - case 'a': - algorithm = optarg; - break; - case 'p': password = optarg; got_password = true; @@ -129,7 +119,7 @@ main(int argc, char *argv[]) { bool fail = false; { - OEncryptStream encrypt(&write_stream, false, password, algorithm); + OEncryptStream encrypt(&write_stream, false, password); int ch = read_stream.get(); while (!read_stream.eof() && !read_stream.fail()) { diff --git a/panda/src/express/config_express.cxx b/panda/src/express/config_express.cxx index 005253fc62..79b8a21467 100644 --- a/panda/src/express/config_express.cxx +++ b/panda/src/express/config_express.cxx @@ -79,6 +79,32 @@ config_express.GetDouble("max-dt", -1.0); const double sleep_precision = config_express.GetDouble("sleep-precision", 0.01); +// This defines the OpenSSL encryption algorithm which is used to +// encrypt any streams created by the current runtime. The default is +// Blowfish; the complete set of available algorithms is defined by +// the current version of OpenSSL. This value is used only to control +// encryption; the correct algorithm will automatically be selected on +// decryption. +const string encryption_algorithm = +config_express.GetString("encryption-algorithm", "bf-cbc"); + +// This defines the key length, in bits, for the selected encryption +// algorithm. Some algorithms have a variable key length. Specifying +// a value of 0 here means to use the default key length for the +// algorithm as defined by OpenSSL. +const int encryption_key_length = +config_express.GetInt("encryption-key-length", 0); + +// This defines the number of times the password is hashed to generate +// a key when encrypting. Its purpose is to make it computationally +// more expensive for an attacker to search the key space +// exhaustively. This should be a multiple of 1,000 and should not +// exceed about 65 million. This value is used only to control +// encryption; the correct count will automatically be selected on +// decryption. +const int encryption_iteration_count = +config_express.GetInt("encryption-iteration-count", 100000); + // Set this true to use the VirtualFileSystem mechanism for loading // models, etc. Since the VirtualFileSystem maps to the same as the // actual file system by default, there is probably no reason to set diff --git a/panda/src/express/config_express.h b/panda/src/express/config_express.h index 6ecabe491c..5a23a1d6c9 100644 --- a/panda/src/express/config_express.h +++ b/panda/src/express/config_express.h @@ -56,6 +56,10 @@ extern const double clock_degrade_factor; extern const double max_dt; extern const double sleep_precision; +extern const string encryption_algorithm; +extern const int encryption_key_length; +extern const int encryption_iteration_count; + extern EXPCL_PANDAEXPRESS const bool use_vfs; extern EXPCL_PANDAEXPRESS const bool collect_tcp; diff --git a/panda/src/express/encryptStream.I b/panda/src/express/encryptStream.I index 4ed7951e12..23871cb234 100644 --- a/panda/src/express/encryptStream.I +++ b/panda/src/express/encryptStream.I @@ -77,11 +77,10 @@ OEncryptStream() : ostream(&_buf) { // Description: //////////////////////////////////////////////////////////////////// INLINE OEncryptStream:: -OEncryptStream(ostream *dest, bool owns_dest, const string &password, - const string &encryption_algorithm) : +OEncryptStream(ostream *dest, bool owns_dest, const string &password) : ostream(&_buf) { - open(dest, owns_dest, password, encryption_algorithm); + open(dest, owns_dest, password); } //////////////////////////////////////////////////////////////////// @@ -90,10 +89,9 @@ OEncryptStream(ostream *dest, bool owns_dest, const string &password, // Description: //////////////////////////////////////////////////////////////////// INLINE OEncryptStream &OEncryptStream:: -open(ostream *dest, bool owns_dest, const string &password, - const string &encryption_algorithm) { +open(ostream *dest, bool owns_dest, const string &password) { clear((ios_iostate)0); - _buf.open_write(dest, owns_dest, password, encryption_algorithm); + _buf.open_write(dest, owns_dest, password); return *this; } diff --git a/panda/src/express/encryptStream.cxx b/panda/src/express/encryptStream.cxx index de10d08860..74df072324 100644 --- a/panda/src/express/encryptStream.cxx +++ b/panda/src/express/encryptStream.cxx @@ -22,16 +22,14 @@ // Function: encrypt_string // Access: Published // Description: Encrypts the indicated source string using the given -// password and algorithm (or empty string for the -// default algorithm). Returns the encrypted string. +// password. Returns the encrypted string. //////////////////////////////////////////////////////////////////// string -encrypt_string(const string &source, const string &password, - const string &algorithm) { +encrypt_string(const string &source, const string &password) { ostringstream output; { - OEncryptStream encrypt(&output, false, password, algorithm); + OEncryptStream encrypt(&output, false, password); encrypt.write(source.data(), source.length()); } diff --git a/panda/src/express/encryptStream.h b/panda/src/express/encryptStream.h index e27d35c133..60ef9be259 100644 --- a/panda/src/express/encryptStream.h +++ b/panda/src/express/encryptStream.h @@ -67,12 +67,10 @@ class EXPCL_PANDAEXPRESS OEncryptStream : public ostream { public: INLINE OEncryptStream(); INLINE OEncryptStream(ostream *dest, bool owns_dest, - const string &password, - const string &encryption_algorithm = ""); + const string &password); INLINE OEncryptStream &open(ostream *dest, bool owns_dest, - const string &password, - const string &encryption_algorithm = ""); + const string &password); INLINE OEncryptStream &close(); private: @@ -80,8 +78,7 @@ private: }; BEGIN_PUBLISH -string encrypt_string(const string &source, const string &password, - const string &algorithm = ""); +string encrypt_string(const string &source, const string &password); string decrypt_string(const string &source, const string &password); END_PUBLISH diff --git a/panda/src/express/encryptStreamBuf.cxx b/panda/src/express/encryptStreamBuf.cxx index d49267193f..c465356c97 100644 --- a/panda/src/express/encryptStreamBuf.cxx +++ b/panda/src/express/encryptStreamBuf.cxx @@ -30,6 +30,10 @@ typedef int streamsize; #endif /* HAVE_STREAMSIZE */ +// The iteration count is scaled by this factor for writing to the +// stream. +static const int iteration_count_factor = 1000; + //////////////////////////////////////////////////////////////////// // Function: EncryptStreamBuf::Constructor // Access: Public @@ -88,6 +92,8 @@ open_read(istream *source, bool owns_source, const string &password) { // Now read the header information. StreamReader sr(_source, false); int nid = sr.get_uint16(); + int key_length = sr.get_uint16(); + int count = sr.get_uint16(); const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid); @@ -98,25 +104,39 @@ open_read(istream *source, bool owns_source, const string &password) { } express_cat.debug() - << "Using decryption algorithm " << OBJ_nid2sn(nid) << "\n"; + << "Using decryption algorithm " << OBJ_nid2sn(nid) << " with key length " + << key_length * 8 << " bits.\n"; int iv_length = EVP_CIPHER_iv_length(cipher); - int key_length = EVP_CIPHER_key_length(cipher); _read_block_size = EVP_CIPHER_block_size(cipher); string iv = sr.extract_bytes(iv_length); - unsigned char *key = (unsigned char *)alloca(key_length); + // Initialize the context + int result; + result = EVP_DecryptInit(&_read_ctx, cipher, NULL, (unsigned char *)iv.data()); + nassertv(result > 0); + + result = EVP_CIPHER_CTX_set_key_length(&_read_ctx, key_length); + if (result <= 0) { + express_cat.error() + << "Invalid key length " << key_length * 8 << " bits for algorithm " + << OBJ_nid2sn(nid) << "\n"; + EVP_CIPHER_CTX_cleanup(&_read_ctx); + return; + } // Hash the supplied password into a key of the appropriate length. - int result; + unsigned char *key = (unsigned char *)alloca(key_length); result = PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(), - (unsigned char *)iv.data(), iv.length(), 1, + (unsigned char *)iv.data(), iv.length(), + count * iteration_count_factor, key_length, key); nassertv(result > 0); - result = EVP_DecryptInit(&_read_ctx, cipher, key, (unsigned char *)iv.data()); + // Store the key within the context. + result = EVP_DecryptInit(&_read_ctx, NULL, key, NULL); nassertv(result > 0); _read_valid = true; @@ -157,8 +177,7 @@ close_read() { // Description: //////////////////////////////////////////////////////////////////// void EncryptStreamBuf:: -open_write(ostream *dest, bool owns_dest, const string &password, - const string &encryption_algorithm) { +open_write(ostream *dest, bool owns_dest, const string &password) { OpenSSL_add_all_algorithms(); close_write(); @@ -166,14 +185,8 @@ open_write(ostream *dest, bool owns_dest, const string &password, _owns_dest = owns_dest; _write_valid = false; - const EVP_CIPHER *cipher; - if (encryption_algorithm.empty()) { - // Blowfish is the default algorithm. - cipher = EVP_bf_cbc(); - - } else { - cipher = EVP_get_cipherbyname(encryption_algorithm.c_str()); - } + const EVP_CIPHER *cipher = + EVP_get_cipherbyname(encryption_algorithm.c_str()); if (cipher == NULL) { express_cat.error() @@ -182,36 +195,62 @@ open_write(ostream *dest, bool owns_dest, const string &password, }; int nid = EVP_CIPHER_nid(cipher); - express_cat.debug() - << "Using encryption algorithm " << OBJ_nid2sn(nid) << "\n"; - int key_length = EVP_CIPHER_key_length(cipher); int iv_length = EVP_CIPHER_iv_length(cipher); _write_block_size = EVP_CIPHER_block_size(cipher); - unsigned char *key = (unsigned char *)alloca(key_length); unsigned char *iv = (unsigned char *)alloca(iv_length); // Generate a random IV. It doesn't need to be cryptographically // secure, just unique. RAND_pseudo_bytes(iv, iv_length); - // Hash the supplied password into a key of the appropriate length. int result; + result = EVP_EncryptInit(&_write_ctx, cipher, NULL, iv); + nassertv(result > 0); + + // Store the appropriate key length in the context. + int key_length = (encryption_key_length + 7) / 8; + if (key_length == 0) { + key_length = EVP_CIPHER_CTX_key_length(cipher); + } + result = EVP_CIPHER_CTX_set_key_length(&_write_ctx, key_length); + if (result <= 0) { + express_cat.error() + << "Invalid key length " << key_length * 8 << " bits for algorithm " + << OBJ_nid2sn(nid) << "\n"; + EVP_CIPHER_CTX_cleanup(&_write_ctx); + return; + } + + express_cat.debug() + << "Using encryption algorithm " << OBJ_nid2sn(nid) << " with key length " + << key_length * 8 << " bits.\n"; + + // Hash the supplied password into a key of the appropriate length. + int count = encryption_iteration_count / iteration_count_factor; + unsigned char *key = (unsigned char *)alloca(key_length); result = PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(), - iv, iv_length, 1, key_length, key); + iv, iv_length, count * iteration_count_factor, + key_length, key); nassertv(result > 0); - result = EVP_EncryptInit(&_write_ctx, cipher, key, iv); + // Store the key in the context. + result = EVP_EncryptInit(&_write_ctx, NULL, key, NULL); nassertv(result > 0); - _write_valid = true; - // Now write the header information to the stream. StreamWriter sw(_dest); + nassertv((PN_uint16)nid == nid); sw.add_uint16(nid); + nassertv((PN_uint16)key_length == key_length); + sw.add_uint16(key_length); + nassertv((PN_uint16)count == count); + sw.add_uint16(count); sw.append_data(iv, iv_length); + + _write_valid = true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/express/encryptStreamBuf.h b/panda/src/express/encryptStreamBuf.h index 9dc527712f..c7febf95f8 100644 --- a/panda/src/express/encryptStreamBuf.h +++ b/panda/src/express/encryptStreamBuf.h @@ -39,8 +39,7 @@ public: void open_read(istream *source, bool owns_source, const string &password); void close_read(); - void open_write(ostream *dest, bool owns_dest, const string &password, - const string &encryption_algorithm); + void open_write(ostream *dest, bool owns_dest, const string &password); void close_write(); protected: diff --git a/panda/src/express/multifile.I b/panda/src/express/multifile.I index 58c8153c6b..c6a376e21d 100644 --- a/panda/src/express/multifile.I +++ b/panda/src/express/multifile.I @@ -84,12 +84,11 @@ get_scale_factor() const { // encryption. // // When true, subfiles will be encrypted with the -// password and algorithm specified by -// set_encryption_password() and -// set_encryption_algorithm(). It is possible to apply -// a different password or algorithm to different files, -// but you must call flush() or repack() before changing -// these properties. +// password specified by set_encryption_password(). It +// is possible to apply a different password to +// different files, but you must call flush() or +// repack() before changing these properties, and the +// resulting file can't be mounted via VFS. //////////////////////////////////////////////////////////////////// INLINE void Multifile:: set_encryption_flag(bool flag) { @@ -141,36 +140,6 @@ get_encryption_password() const { return _encryption_password; } -//////////////////////////////////////////////////////////////////// -// Function: Multifile::set_encryption_algorithm -// Access: Published -// Description: Specifies the algorithm that will be used to encrypt -// subfiles subsequently added to the multifile, if the -// encryption flag is also set true (see -// set_encryption_flag()). -// -// The empty string, which is the initial value for this -// parameter, implies the default encryption algorithm. -// The complete list of available encryption algorithms -// is defined by OpenSSL. -//////////////////////////////////////////////////////////////////// -INLINE void Multifile:: -set_encryption_algorithm(const string &algorithm) { - _encryption_algorithm = algorithm; -} - -//////////////////////////////////////////////////////////////////// -// Function: Multifile::get_encryption_algorithm -// Access: Published -// Description: Returns the algorithm that will be used to encrypt -// subfiles subsequently added to the multifile. See -// set_encryption_algorithm(). -//////////////////////////////////////////////////////////////////// -INLINE const string &Multifile:: -get_encryption_algorithm() const { - return _encryption_algorithm; -} - //////////////////////////////////////////////////////////////////// // Function: Multifile::read_subfile // Access: Published diff --git a/panda/src/express/multifile.cxx b/panda/src/express/multifile.cxx index 92527235ab..5dc9b599f7 100644 --- a/panda/src/express/multifile.cxx +++ b/panda/src/express/multifile.cxx @@ -1574,8 +1574,7 @@ write_data(ostream &write, istream *read, streampos fpos, if ((_flags & SF_encrypted) != 0) { // Write it encrypted. putter = new OEncryptStream(putter, delete_putter, - multifile->_encryption_password, - multifile->_encryption_algorithm); + multifile->_encryption_password); delete_putter = true; // Also write the encrypt_header to the beginning of the diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index 3923faa74d..352aa459f2 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -59,8 +59,6 @@ PUBLISHED: INLINE bool get_encryption_flag() const; INLINE void set_encryption_password(const string &password); INLINE const string &get_encryption_password() const; - INLINE void set_encryption_algorithm(const string &algorithm); - INLINE const string &get_encryption_algorithm() const; string add_subfile(const string &subfile_name, const Filename &filename, int compression_level); @@ -167,7 +165,6 @@ private: bool _encryption_flag; string _encryption_password; - string _encryption_algorithm; ifstream _read_file; ofstream _write_file;