support encryption/decryption streams

This commit is contained in:
David Rose 2004-09-02 15:15:48 +00:00
parent 9bd9b1a99b
commit e51ecacdee
20 changed files with 1615 additions and 78 deletions

View File

@ -88,3 +88,23 @@
pdecompress.cxx
#end bin_target
#begin bin_target
#define TARGET pencrypt
#define BUILD_TARGET $[HAVE_SSL]
#define USE_PACKAGES $[USE_PACKAGES] ssl
#define SOURCES \
pencrypt.cxx
#end bin_target
#begin bin_target
#define TARGET pdecrypt
#define BUILD_TARGET $[HAVE_SSL]
#define USE_PACKAGES $[USE_PACKAGES] ssl
#define SOURCES \
pdecrypt.cxx
#end bin_target

View File

@ -28,21 +28,24 @@
#include <stdio.h>
bool create = false; // -c
bool append = false; // -r
bool update = false; // -u
bool list = false; // -t
bool extract = false; // -x
bool verbose = false; // -v
bool compress = false; // -z
bool create = false; // -c
bool append = false; // -r
bool update = false; // -u
bool list = false; // -t
bool extract = false; // -x
bool verbose = false; // -v
bool compress = false; // -z
int default_compression_level = 6;
Filename multifile_name; // -f
Filename multifile_name; // -f
bool got_multifile_name = false;
bool to_stdout = false; // -O
Filename chdir_to; // -C
bool to_stdout = false; // -O
bool encryption_flag = false; // -e
string password; // -p
bool got_password = false;
Filename chdir_to; // -C
bool got_chdir_to = false;
size_t scale_factor = 0; // -F
pset<string> dont_compress; // -Z
size_t scale_factor = 0; // -F
pset<string> dont_compress; // -Z
// Default extensions not to compress. May be overridden with -Z.
string dont_compress_str = "jpg,mp3";
@ -120,6 +123,21 @@ help() {
" decompressed automatically. Also see -Z, which restricts which\n"
" subfiles will be compressed based on the filename extension.\n\n"
" -e\n"
" Encrypt subfiles as they are written to the Multifile using the password\n"
" specified with -p, below. Subfiles are encrypted individually, rather\n"
" than encrypting the entire multifile, and different subfiles may be\n"
" encrypted using different passwords (although this requires running\n"
" multify multiple times). It is not possible to encrypt the multifile's\n"
" table of contents using this interface, but see the pencrypt program to\n"
" encrypt the entire multifile after it has been generated).\n\n"
" -p \"password\"\n"
" Specifies the password to encrypt or decrypt subfiles. If this is not\n"
" specified, and passwords are required, the user will be prompted from\n"
" standard input.\n\n"
" -F <scale_factor>\n"
" Specify a Multifile scale factor. This is only necessary to support\n"
" Multifiles that will exceed 4GB in size. The default scale factor is\n"
@ -146,6 +164,18 @@ help() {
" default is -" << default_compression_level << ".\n\n";
}
const string &
get_password() {
if (!got_password) {
cerr << "Enter password: ";
getline(cin, password);
got_password = true;
}
return password;
}
bool
is_named(const string &subfile_name, int argc, char *argv[]) {
// Returns true if the indicated subfile appears on the list of
@ -243,6 +273,11 @@ add_files(int argc, char *argv[]) {
}
}
if (encryption_flag) {
multifile.set_encryption_flag(true);
multifile.set_encryption_password(get_password());
}
if (scale_factor != 0 && scale_factor != multifile.get_scale_factor()) {
cerr << "Setting scale factor to " << scale_factor << "\n";
multifile.set_scale_factor(scale_factor);
@ -309,7 +344,26 @@ extract_files(int argc, char *argv[]) {
int num_subfiles = multifile.get_num_subfiles();
for (int i = 0; i < num_subfiles; i++) {
// First, check to see whether any of the named subfiles have been
// encrypted. If any have, we may need to prompt the user to enter
// a password before we can extract them.
int i;
bool any_encrypted = false;
for (i = 0; i < num_subfiles && !any_encrypted; i++) {
string subfile_name = multifile.get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
if (multifile.is_subfile_encrypted(i)) {
any_encrypted = true;
}
}
}
if (any_encrypted) {
multifile.set_encryption_password(get_password());
}
// Now walk back through the list and this time do the extraction.
for (i = 0; i < num_subfiles; i++) {
string subfile_name = multifile.get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
Filename filename = subfile_name;
@ -352,21 +406,32 @@ list_files(int argc, char *argv[]) {
for (int i = 0; i < num_subfiles; i++) {
string subfile_name = multifile.get_subfile_name(i);
if (is_named(subfile_name, argc, argv)) {
char encrypted_symbol = ' ';
if (multifile.is_subfile_encrypted(i)) {
encrypted_symbol = 'e';
}
if (multifile.is_subfile_compressed(i)) {
size_t orig_length = multifile.get_subfile_length(i);
size_t compressed_length = multifile.get_subfile_compressed_length(i);
size_t internal_length = multifile.get_subfile_internal_length(i);
double ratio = 1.0;
if (orig_length != 0) {
ratio = (double)compressed_length / (double)orig_length;
ratio = (double)internal_length / (double)orig_length;
}
if (ratio > 1.0) {
printf("%12d worse %c %s\n",
multifile.get_subfile_length(i),
encrypted_symbol,
subfile_name.c_str());
} else {
printf("%12d %3.0f%% %c %s\n",
multifile.get_subfile_length(i),
100.0 - ratio * 100.0, encrypted_symbol,
subfile_name.c_str());
}
printf("%12d %3.0f%% %s\n",
multifile.get_subfile_length(i),
100.0 - ratio * 100.0,
subfile_name.c_str());
} else {
printf("%12d %s\n",
printf("%12d %c %s\n",
multifile.get_subfile_length(i),
subfile_name.c_str());
encrypted_symbol, subfile_name.c_str());
}
}
}
@ -424,7 +489,7 @@ main(int argc, char *argv[]) {
extern char *optarg;
extern int optind;
static const char *optflags = "crutxvz123456789Z:f:OC:F:h";
static const char *optflags = "crutxvz123456789Z:f:OC:ep:F:h";
int flag = getopt(argc, argv, optflags);
Filename rel_path;
while (flag != EOF) {
@ -500,6 +565,13 @@ main(int argc, char *argv[]) {
case 'O':
to_stdout = true;
break;
case 'e':
encryption_flag = true;
break;
case 'p':
password = optarg;
got_password = true;
break;
case 'F':
{
char *endptr;

View File

@ -46,16 +46,6 @@ main(int argc, char *argv[]) {
return 1;
}
// Determine source file length
read_stream.seekg(0, ios::end);
int source_file_length = read_stream.tellg();
read_stream.seekg(0, ios::beg);
if (source_file_length == 0) {
cerr << "zero length file: " << source_file << endl;
return 1;
}
// Open destination file
ofstream write_stream;
dest_file.set_binary();
@ -64,6 +54,7 @@ main(int argc, char *argv[]) {
return 1;
}
bool fail = false;
{
OCompressStream compress(&write_stream, false);
@ -72,13 +63,20 @@ main(int argc, char *argv[]) {
compress.put(ch);
ch = read_stream.get();
}
fail = compress.fail() && !compress.eof();
}
read_stream.close();
write_stream.close();
if (implicit_dest_file) {
source_file.unlink();
if (fail) {
dest_file.unlink();
} else {
if (implicit_dest_file) {
source_file.unlink();
}
}
return 0;

View File

@ -0,0 +1,151 @@
// Filename: pdecrypt.cxx
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "filename.h"
#include "encryptStream.h"
#include "notify.h"
#ifndef HAVE_GETOPT
#include "gnu_getopt.h"
#else
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#endif
void
usage() {
cerr
<< "\n"
<< "Usage: pdecrypt [opts] <file> [<dest_file>]\n\n"
<< "This program reverses the operation of a previous pencrypt command. It\n"
<< "decrypts the contents of the source file by applying the indicated password.\n"
<< "The encryption algorithm need not be specified; it can be determined by\n"
<< "examining the header of the encrypted file. The password must match exactly.\n"
<< "If it does not, an error may or may not be reported; but the file will not be\n"
<< "decrypted correctly even if no error is reported.\n\n"
<< "Options:\n\n"
<< " -p \"password\"\n"
<< " Specifies the password to use for descryption. If this is not specified,\n"
<< " the user is prompted from standard input.\n\n";
}
int
main(int argc, char *argv[]) {
extern char *optarg;
extern int optind;
const char *optstr = "p:h";
string password;
bool got_password = false;
int flag = getopt(argc, argv, optstr);
while (flag != EOF) {
switch (flag) {
case 'p':
password = optarg;
got_password = true;
break;
case 'h':
case '?':
default:
usage();
return 1;
}
flag = getopt(argc, argv, optstr);
}
argc -= (optind-1);
argv += (optind-1);
if (argc < 2) {
usage();
return 1;
}
bool implicit_dest_file;
Filename source_file = Filename::from_os_specific(argv[1]);
Filename dest_file;
if (argc < 3) {
if (source_file.get_extension() == "pe") {
dest_file = source_file;
dest_file.set_extension("");
} else {
cerr << "Input filename doesn't end in .pe; can't derive filename of output file.\n";
return 1;
}
implicit_dest_file = true;
} else {
dest_file = Filename::from_os_specific(argv[2]);
implicit_dest_file = false;
}
// Open source file
ifstream read_stream;
source_file.set_binary();
if (!source_file.open_read(read_stream)) {
cerr << "failed to open: " << source_file << endl;
return 1;
}
// Open destination file
ofstream write_stream;
dest_file.set_binary();
if (!dest_file.open_write(write_stream, true)) {
cerr << "failed to open: " << dest_file << endl;
return 1;
}
// Prompt for password.
if (!got_password) {
cerr << "Enter password: ";
getline(cin, password);
}
bool fail = false;
{
IDecryptStream decrypt(&read_stream, false, password);
int ch = decrypt.get();
while (!decrypt.eof() && !decrypt.fail()) {
write_stream.put(ch);
ch = decrypt.get();
}
fail = decrypt.fail() && !decrypt.eof();
}
read_stream.close();
write_stream.close();
if (fail) {
dest_file.unlink();
} else {
if (implicit_dest_file) {
source_file.unlink();
}
}
return 0;
}

View File

@ -0,0 +1,156 @@
// Filename: pencrypt.cxx
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "filename.h"
#include "encryptStream.h"
#include "notify.h"
#ifndef HAVE_GETOPT
#include "gnu_getopt.h"
#else
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#endif
void
usage() {
cerr
<< "\n"
<< "Usage: pencrypt [opts] <file> [<dest_file>]\n\n"
<< "This program will apply an encryption algorithm to a file, creating an\n"
<< "encrypted version of the file which can only be recovered using pdecrypt and\n"
<< "the same password that was supplied to pencrypt. If the dest_file name is\n"
<< "not specified, a default output name is generated by appending .pe to the\n"
<< "input file name.\n\n"
<< "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"
<< " are more secure. If this is not specified, the user is prompted from\n"
<< " standard input.\n\n";
}
int
main(int argc, char *argv[]) {
extern char *optarg;
extern int optind;
const char *optstr = "a:p:h";
string algorithm;
string password;
bool got_password = false;
int flag = getopt(argc, argv, optstr);
while (flag != EOF) {
switch (flag) {
case 'a':
algorithm = optarg;
break;
case 'p':
password = optarg;
got_password = true;
break;
case 'h':
case '?':
default:
usage();
return 1;
}
flag = getopt(argc, argv, optstr);
}
argc -= (optind-1);
argv += (optind-1);
if (argc < 2) {
usage();
return 1;
}
bool implicit_dest_file;
Filename source_file = Filename::from_os_specific(argv[1]);
Filename dest_file;
if (argc < 3) {
dest_file = source_file.get_fullpath() + ".pe";
implicit_dest_file = true;
} else {
dest_file = Filename::from_os_specific(argv[2]);
implicit_dest_file = false;
}
// Open source file
ifstream read_stream;
source_file.set_binary();
if (!source_file.open_read(read_stream)) {
cerr << "failed to open: " << source_file << endl;
return 1;
}
// Open destination file
ofstream write_stream;
dest_file.set_binary();
if (!dest_file.open_write(write_stream, true)) {
cerr << "failed to open: " << dest_file << endl;
return 1;
}
// Prompt for password.
if (!got_password) {
cerr << "Enter password: ";
getline(cin, password);
}
bool fail = false;
{
OEncryptStream encrypt(&write_stream, false, password, algorithm);
int ch = read_stream.get();
while (!read_stream.eof() && !read_stream.fail()) {
encrypt.put(ch);
ch = read_stream.get();
}
fail = encrypt.fail() && !encrypt.eof();
}
read_stream.close();
write_stream.close();
if (fail) {
dest_file.unlink();
} else {
if (implicit_dest_file) {
source_file.unlink();
}
}
return 0;
}

View File

@ -23,6 +23,7 @@
datagramGenerator.h \
datagramIterator.I datagramIterator.h datagramSink.I datagramSink.h \
dcast.T dcast.h \
encryptStreamBuf.h encryptStream.h encryptStream.I \
error_utils.h \
get_config_path.h \
hashGeneratorBase.I hashGeneratorBase.h \
@ -40,6 +41,7 @@
namable.h nativeNumericData.I nativeNumericData.h \
numeric_types.h \
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \
pointerTo.I pointerTo.h \
pointerToArray.I pointerToArray.h \
@ -79,7 +81,9 @@
conditionVar.cxx conditionVarDummyImpl.cxx conditionVarNsprImpl.cxx \
config_express.cxx datagram.cxx datagramGenerator.cxx \
datagramIterator.cxx \
datagramSink.cxx dcast.cxx error_utils.cxx \
datagramSink.cxx dcast.cxx \
encryptStreamBuf.cxx encryptStream.cxx \
error_utils.cxx \
get_config_path.cxx \
hashGeneratorBase.cxx hashVal.cxx indent.cxx \
memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
@ -88,6 +92,7 @@
namable.cxx \
nativeNumericData.cxx \
ordered_vector.cxx \
password_hash.cxx \
patchfile.cxx \
profileTimer.cxx \
pta_uchar.cxx \
@ -125,6 +130,7 @@
datagramGenerator.I datagramGenerator.h \
datagramIterator.I datagramIterator.h \
datagramSink.I datagramSink.h dcast.T dcast.h \
encryptStreamBuf.h encryptStream.h encryptStream.I \
error_utils.h get_config_path.h \
hashGeneratorBase.I \
hashGeneratorBase.h hashVal.I hashVal.h \
@ -139,6 +145,7 @@
namable.I namable.h \
nativeNumericData.I nativeNumericData.h numeric_types.h \
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h pointerTo.I pointerTo.h \
pointerToArray.I pointerToArray.h profileTimer.I \
profileTimer.h pta_uchar.h \

View File

@ -0,0 +1,111 @@
// Filename: encryptStream.I
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: IDecryptStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE IDecryptStream::
IDecryptStream() : istream(&_buf) {
}
////////////////////////////////////////////////////////////////////
// Function: IDecryptStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE IDecryptStream::
IDecryptStream(istream *source, bool owns_source,
const string &password) : istream(&_buf) {
open(source, owns_source, password);
}
////////////////////////////////////////////////////////////////////
// Function: IDecryptStream::open
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE IDecryptStream &IDecryptStream::
open(istream *source, bool owns_source, const string &password) {
clear((ios_iostate)0);
_buf.open_read(source, owns_source, password);
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: IDecryptStream::close
// Access: Public
// Description: Resets the EncryptStream to empty, but does not actually
// close the source istream unless owns_source was true.
////////////////////////////////////////////////////////////////////
INLINE IDecryptStream &IDecryptStream::
close() {
_buf.close_read();
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: OEncryptStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE OEncryptStream::
OEncryptStream() : ostream(&_buf) {
}
////////////////////////////////////////////////////////////////////
// Function: OEncryptStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE OEncryptStream::
OEncryptStream(ostream *dest, bool owns_dest, const string &password,
const string &encryption_algorithm) :
ostream(&_buf)
{
open(dest, owns_dest, password, encryption_algorithm);
}
////////////////////////////////////////////////////////////////////
// Function: OEncryptStream::open
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE OEncryptStream &OEncryptStream::
open(ostream *dest, bool owns_dest, const string &password,
const string &encryption_algorithm) {
clear((ios_iostate)0);
_buf.open_write(dest, owns_dest, password, encryption_algorithm);
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: OEncryptStream::close
// Access: Public
// Description: Resets the EncryptStream to empty, but does not actually
// close the dest ostream unless owns_dest was true.
////////////////////////////////////////////////////////////////////
INLINE OEncryptStream &OEncryptStream::
close() {
_buf.close_write();
return *this;
}

View File

@ -0,0 +1,68 @@
// Filename: encryptStream.cxx
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "encryptStream.h"
////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////
string
encrypt_string(const string &source, const string &password,
const string &algorithm) {
ostringstream output;
{
OEncryptStream encrypt(&output, false, password, algorithm);
encrypt.write(source.data(), source.length());
}
return output.str();
}
////////////////////////////////////////////////////////////////////
// Function: decrypt_string
// Access: Published
// Description: Decrypts the previously-encrypted string using the
// given password (which must be the same password
// passed to encrypt()). The return value is the
// decrypted string. Note that a decryption error,
// including an incorrect password, cannot easily be
// detected, and the return value may simply be a
// garbage string.
////////////////////////////////////////////////////////////////////
string
decrypt_string(const string &source, const string &password) {
istringstream input(source);
ostringstream output;
{
IDecryptStream decrypt(&input, false, password);
int ch = decrypt.get();
while (!decrypt.eof() && !decrypt.fail()) {
output.put(ch);
ch = decrypt.get();
}
}
return output.str();
}

View File

@ -0,0 +1,95 @@
// Filename: encryptStream.h
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef ENCRYPTSTREAM_H
#define ENCRYPTSTREAM_H
#include "pandabase.h"
// This module is not compiled if OpenSSL is not available.
#ifdef HAVE_SSL
#include "encryptStreamBuf.h"
////////////////////////////////////////////////////////////////////
// Class : IDecryptStream
// Description : An input stream object that uses OpenSSL to decrypt
// the input from another source stream on-the-fly.
//
// Attach an IDecryptStream to an existing istream that
// provides encrypted data, as generated by an
// OEncryptStream, and read the corresponding
// unencrypted data from the IDecryptStream.
//
// Seeking is not supported.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS IDecryptStream : public istream {
public:
INLINE IDecryptStream();
INLINE IDecryptStream(istream *source, bool owns_source,
const string &password);
INLINE IDecryptStream &open(istream *source, bool owns_source,
const string &password);
INLINE IDecryptStream &close();
private:
EncryptStreamBuf _buf;
};
////////////////////////////////////////////////////////////////////
// Class : OEncryptStream
// Description : An input stream object that uses OpenSSL to encrypt
// data to another destination stream on-the-fly.
//
// Attach an OEncryptStream to an existing ostream that
// will accept encrypted data, and write your
// unencrypted source data to the OEncryptStream.
//
// Seeking is not supported.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS OEncryptStream : public ostream {
public:
INLINE OEncryptStream();
INLINE OEncryptStream(ostream *dest, bool owns_dest,
const string &password,
const string &encryption_algorithm = "");
INLINE OEncryptStream &open(ostream *dest, bool owns_dest,
const string &password,
const string &encryption_algorithm = "");
INLINE OEncryptStream &close();
private:
EncryptStreamBuf _buf;
};
BEGIN_PUBLISH
string encrypt_string(const string &source, const string &password,
const string &algorithm = "");
string decrypt_string(const string &source, const string &password);
END_PUBLISH
#include "encryptStream.I"
#endif // HAVE_SSL
#endif

View File

@ -0,0 +1,423 @@
// Filename: encryptStreamBuf.cxx
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "encryptStreamBuf.h"
#include "config_express.h"
#include "streamReader.h"
#include "streamWriter.h"
#ifdef HAVE_SSL
#include <openssl/rand.h>
#ifndef HAVE_STREAMSIZE
// Some compilers (notably SGI) don't define this for us
typedef int streamsize;
#endif /* HAVE_STREAMSIZE */
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EncryptStreamBuf::
EncryptStreamBuf() {
_source = (istream *)NULL;
_owns_source = false;
_dest = (ostream *)NULL;
_owns_dest = false;
_read_valid = false;
_write_valid = false;
_read_overflow_buffer = NULL;
_in_read_overflow_buffer = 0;
#ifdef HAVE_IOSTREAM
char *buf = new char[4096];
char *ebuf = buf + 4096;
setg(buf, ebuf, ebuf);
setp(buf, ebuf);
#else
allocate();
setg(base(), ebuf(), ebuf());
setp(base(), ebuf());
#endif
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
EncryptStreamBuf::
~EncryptStreamBuf() {
close_read();
close_write();
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::open_read
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EncryptStreamBuf::
open_read(istream *source, bool owns_source, const string &password) {
OpenSSL_add_all_algorithms();
_source = source;
_owns_source = owns_source;
_read_valid = false;
// Now read the header information.
StreamReader sr(_source, false);
int nid = sr.get_uint16();
const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid);
if (cipher == NULL) {
express_cat.error()
<< "Unknown encryption algorithm in stream.\n";
return;
}
express_cat.debug()
<< "Using decryption algorithm " << OBJ_nid2sn(nid) << "\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);
// Hash the supplied password into a key of the appropriate length.
int result;
result =
PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(),
(unsigned char *)iv.data(), iv.length(), 1,
key_length, key);
nassertv(result > 0);
result = EVP_DecryptInit(&_read_ctx, cipher, key, (unsigned char *)iv.data());
nassertv(result > 0);
_read_valid = true;
_read_overflow_buffer = new unsigned char[_read_block_size];
_in_read_overflow_buffer = 0;
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::close_read
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EncryptStreamBuf::
close_read() {
if (_read_valid) {
EVP_CIPHER_CTX_cleanup(&_read_ctx);
_read_valid = false;
}
if (_read_overflow_buffer != (unsigned char *)NULL) {
delete[] _read_overflow_buffer;
_read_overflow_buffer = NULL;
}
if (_source != (istream *)NULL) {
if (_owns_source) {
delete _source;
_owns_source = false;
}
_source = (istream *)NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::open_write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EncryptStreamBuf::
open_write(ostream *dest, bool owns_dest, const string &password,
const string &encryption_algorithm) {
OpenSSL_add_all_algorithms();
close_write();
_dest = dest;
_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());
}
if (cipher == NULL) {
express_cat.error()
<< "Unknown encryption algorithm: " << encryption_algorithm << "\n";
return;
};
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 =
PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(),
iv, iv_length, 1, key_length, key);
nassertv(result > 0);
result = EVP_EncryptInit(&_write_ctx, cipher, key, iv);
nassertv(result > 0);
_write_valid = true;
// Now write the header information to the stream.
StreamWriter sw(_dest);
sw.add_uint16(nid);
sw.append_data(iv, iv_length);
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::close_write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void EncryptStreamBuf::
close_write() {
if (_dest != (ostream *)NULL) {
size_t n = pptr() - pbase();
write_chars(pbase(), n);
pbump(-(int)n);
if (_write_valid) {
unsigned char *write_buffer = (unsigned char *)alloca(_write_block_size);
int bytes_written = 0;
EVP_EncryptFinal(&_write_ctx, write_buffer, &bytes_written);
_dest->write((const char *)write_buffer, bytes_written);
_write_valid = false;
}
if (_owns_dest) {
delete _dest;
_owns_dest = false;
}
_dest = (ostream *)NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::overflow
// Access: Protected, Virtual
// Description: Called by the system ostream implementation when its
// internal buffer is filled, plus one character.
////////////////////////////////////////////////////////////////////
int EncryptStreamBuf::
overflow(int ch) {
size_t n = pptr() - pbase();
if (n != 0) {
write_chars(pbase(), n);
pbump(-(int)n);
}
if (ch != EOF) {
// Write one more character.
char c = ch;
write_chars(&c, 1);
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::sync
// Access: Protected, Virtual
// Description: Called by the system iostream implementation to
// implement a flush operation.
////////////////////////////////////////////////////////////////////
int EncryptStreamBuf::
sync() {
if (_source != (istream *)NULL) {
size_t n = egptr() - gptr();
gbump(n);
}
if (_dest != (ostream *)NULL) {
size_t n = pptr() - pbase();
write_chars(pbase(), n);
pbump(-(int)n);
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::underflow
// Access: Protected, Virtual
// Description: Called by the system istream implementation when its
// internal buffer needs more characters.
////////////////////////////////////////////////////////////////////
int EncryptStreamBuf::
underflow() {
// Sometimes underflow() is called even if the buffer is not empty.
if (gptr() >= egptr()) {
size_t buffer_size = egptr() - eback();
gbump(-(int)buffer_size);
size_t num_bytes = buffer_size;
size_t read_count = read_chars(gptr(), buffer_size);
if (read_count != num_bytes) {
// Oops, we didn't read what we thought we would.
if (read_count == 0) {
gbump(num_bytes);
return EOF;
}
// Slide what we did read to the top of the buffer.
nassertr(read_count < num_bytes, EOF);
size_t delta = num_bytes - read_count;
memmove(gptr() + delta, gptr(), read_count);
gbump(delta);
}
}
return (unsigned char)*gptr();
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::read_chars
// Access: Private
// Description: Gets some characters from the source stream.
////////////////////////////////////////////////////////////////////
size_t EncryptStreamBuf::
read_chars(char *start, size_t length) {
if (_in_read_overflow_buffer != 0) {
// Take from the overflow buffer.
length = min(length, _in_read_overflow_buffer);
memcpy(start, _read_overflow_buffer, length);
_in_read_overflow_buffer -= length;
memcpy(_read_overflow_buffer + length, _read_overflow_buffer, _in_read_overflow_buffer);
return length;
}
unsigned char *source_buffer = (unsigned char *)alloca(length);
size_t max_read_buffer = length + _read_block_size;
unsigned char *read_buffer = (unsigned char *)alloca(max_read_buffer);
int bytes_read = 0;
do {
// Get more bytes from the stream.
if (!_read_valid) {
return 0;
}
_source->read((char *)source_buffer, length);
size_t source_length = _source->gcount();
bytes_read = 0;
int result;
if (source_length != 0) {
result =
EVP_DecryptUpdate(&_read_ctx, read_buffer, &bytes_read,
source_buffer, source_length);
} else {
result =
EVP_DecryptFinal(&_read_ctx, read_buffer, &bytes_read);
_read_valid = false;
}
if (result <= 0) {
express_cat.error()
<< "Error decrypting stream.\n";
if (_read_valid) {
EVP_CIPHER_CTX_cleanup(&_read_ctx);
_read_valid = false;
}
}
} while (bytes_read == 0);
// Now store the read bytes in the output stream.
if ((size_t)bytes_read <= length) {
// No overflow.
memcpy(start, read_buffer, bytes_read);
return bytes_read;
} else {
// We have to save some of the returned bytes in the overflow
// buffer.
_in_read_overflow_buffer = bytes_read - length;
nassertr(_in_read_overflow_buffer <= _read_block_size, 0);
memcpy(_read_overflow_buffer, read_buffer + length,
_in_read_overflow_buffer);
memcpy(start, read_buffer, length);
return length;
}
}
////////////////////////////////////////////////////////////////////
// Function: EncryptStreamBuf::write_chars
// Access: Private
// Description: Sends some characters to the dest stream.
////////////////////////////////////////////////////////////////////
void EncryptStreamBuf::
write_chars(const char *start, size_t length) {
if (_write_valid) {
size_t max_write_buffer = length + _write_block_size;
unsigned char *write_buffer = (unsigned char *)alloca(max_write_buffer);
int bytes_written = 0;
int result =
EVP_EncryptUpdate(&_write_ctx, write_buffer, &bytes_written,
(unsigned char *)start, length);
if (result <= 0) {
express_cat.error()
<< "Error encrypting stream.\n";
}
_dest->write((const char *)write_buffer, bytes_written);
}
}
#endif // HAVE_SSL

View File

@ -0,0 +1,75 @@
// Filename: encryptStreamBuf.h
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef ENCRYPTSTREAMBUF_H
#define ENCRYPTSTREAMBUF_H
#include "pandabase.h"
// This module is not compiled if OpenSSL is not available.
#ifdef HAVE_SSL
#include <openssl/evp.h>
////////////////////////////////////////////////////////////////////
// Class : EncryptStreamBuf
// Description : The streambuf object that implements
// IDecompressStream and OCompressStream.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS EncryptStreamBuf : public streambuf {
public:
EncryptStreamBuf();
virtual ~EncryptStreamBuf();
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 close_write();
protected:
virtual int overflow(int c);
virtual int sync(void);
virtual int underflow(void);
private:
size_t read_chars(char *start, size_t length);
void write_chars(const char *start, size_t length);
private:
istream *_source;
bool _owns_source;
ostream *_dest;
bool _owns_dest;
bool _read_valid;
EVP_CIPHER_CTX _read_ctx;
size_t _read_block_size;
unsigned char *_read_overflow_buffer;
size_t _in_read_overflow_buffer;
bool _write_valid;
EVP_CIPHER_CTX _write_ctx;
size_t _write_block_size;
};
#endif // HAVE_SSL
#endif

View File

@ -14,6 +14,8 @@
#include "datagramSink.cxx"
#include "dcast.cxx"
#include "error_utils.cxx"
#include "encryptStreamBuf.cxx"
#include "encryptStream.cxx"
#include "get_config_path.cxx"
#include "hashGeneratorBase.cxx"
#include "hashVal.cxx"

View File

@ -1,4 +1,5 @@
#include "patchfile.cxx"
#include "password_hash.cxx"
#include "pmutex.cxx"
#include "profileTimer.cxx"
#include "pta_uchar.cxx"

View File

@ -74,6 +74,103 @@ get_scale_factor() const {
return _new_scale_factor;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::set_encryption_flag
// Access: Published
// Description: Sets the flag indicating whether subsequently-added
// subfiles should be encrypted before writing them to
// the multifile. If true, subfiles will be encrypted;
// if false (the default), they will be written without
// 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.
////////////////////////////////////////////////////////////////////
INLINE void Multifile::
set_encryption_flag(bool flag) {
#ifndef HAVE_SSL
if (flag) {
express_cat.warning()
<< "OpenSSL not compiled in; cannot generated compressed multifiles.\n";
flag = false;
}
#endif // HAVE_SSL
_encryption_flag = flag;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_encryption_flag
// Access: Published
// Description: Returns the flag indicating whether
// subsequently-added subfiles should be encrypted
// before writing them to the multifile. See
// set_encryption_flag().
////////////////////////////////////////////////////////////////////
INLINE bool Multifile::
get_encryption_flag() const {
return _encryption_flag;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::set_encryption_password
// Access: Published
// Description: Specifies the password that will be used to encrypt
// subfiles subsequently added to the multifile, if the
// encryption flag is also set true (see
// set_encryption_flag()).
////////////////////////////////////////////////////////////////////
INLINE void Multifile::
set_encryption_password(const string &password) {
_encryption_password = password;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_encryption_password
// Access: Published
// Description: Returns the password that will be used to encrypt
// subfiles subsequently added to the multifile. See
// set_encryption_password().
////////////////////////////////////////////////////////////////////
INLINE const string &Multifile::
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

View File

@ -23,6 +23,7 @@
#include "streamReader.h"
#include "datagram.h"
#include "zStream.h"
#include "encryptStream.h"
#include <algorithm>
@ -37,6 +38,18 @@ const size_t Multifile::_header_size = 6;
const int Multifile::_current_major_ver = 1;
const int Multifile::_current_minor_ver = 0;
// To confirm that the supplied password matches, we write the
// Mutifile magic header at the beginning of the encrypted stream.
// I suppose this does compromise the encryption security a tiny
// bit by making it easy for crackers to validate that a
// particular password guess matches or doesn't match, but the
// encryption algorithm doesn't depend on this being difficult
// anyway.
const char Multifile::_encrypt_header[] = "crypty";
const size_t Multifile::_encrypt_header_size = 6;
//
// A Multifile consists of the following elements:
//
@ -65,9 +78,10 @@ const int Multifile::_current_minor_ver = 0;
// uint32 The address of this subfile's data record.
// uint32 The length in bytes of this subfile's data record.
// uint16 The Subfile::_flags member.
// [uint32] The original, uncompressed length of the subfile, if it
// is compressed. This field is only present if the
// SF_compressed bit is set in _flags.
// [uint32] The original, uncompressed and unencrypted length of the
// subfile, if it is compressed or encrypted. This field
// is only present if one or both of the SF_compressed
// or SF_encrypted bits are set in _flags.
// uint16 The length in bytes of the subfile's name.
// char[n] The subfile's name.
//
@ -93,6 +107,7 @@ Multifile() {
_needs_repack = false;
_scale_factor = 1;
_new_scale_factor = 1;
_encryption_flag = false;
_file_major_ver = 0;
_file_minor_ver = 0;
}
@ -441,6 +456,7 @@ flush() {
Subfile *subfile = (*pi);
_last_index = _next_index;
_next_index = subfile->write_index(*_write, _next_index, this);
nassertr(_next_index == _write->tellp(), false);
_next_index = pad_to_streampos(_next_index);
nassertr(_next_index == _write->tellp(), false);
}
@ -450,12 +466,15 @@ flush() {
StreamWriter writer(_write);
writer.add_uint32(0);
_next_index += 4;
nassertr(_next_index == _write->tellp(), false);
_next_index = pad_to_streampos(_next_index);
// All right, now write out each subfile's data.
for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
Subfile *subfile = (*pi);
_next_index = subfile->write_data(*_write, _read, _next_index);
_next_index = subfile->write_data(*_write, _read, _next_index,
this);
nassertr(_next_index == _write->tellp(), false);
_next_index = pad_to_streampos(_next_index);
if (subfile->is_data_invalid()) {
wrote_ok = false;
@ -751,16 +770,31 @@ is_subfile_compressed(int index) const {
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_subfile_compressed_length
// Function: Multifile::is_subfile_encrypted
// Access: Published
// Description: Returns true if the indicated subfile has been
// encrypted when stored within the archive, false
// otherwise.
////////////////////////////////////////////////////////////////////
bool Multifile::
is_subfile_encrypted(int index) const {
nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
return (_subfiles[index]->_flags & SF_encrypted) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: Multifile::get_subfile_internal_length
// Access: Published
// Description: Returns the number of bytes the indicated subfile
// consumes within the archive. For compressed
// subfiles, this will generally be smaller than
// get_subfile_length(); for noncompressed subfiles, it
// will be equal.
// get_subfile_length(); for encrypted (but
// noncompressed) subfiles, it may be slightly
// different, for noncompressed and nonencrypted
// subfiles, it will be equal.
////////////////////////////////////////////////////////////////////
size_t Multifile::
get_subfile_compressed_length(int index) const {
get_subfile_internal_length(int index) const {
nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
return _subfiles[index]->_data_length;
}
@ -811,6 +845,33 @@ open_read_subfile(int index) {
new ISubStream(_read, subfile->_data_start,
subfile->_data_start + (streampos)subfile->_data_length);
if ((subfile->_flags & SF_encrypted) != 0) {
#ifndef HAVE_SSL
express_cat.error()
<< "OpenSSL not compiled in; cannot read encrypted multifiles.\n";
delete stream;
return NULL;
#else // HAVE_SSL
// The subfile is encrypted. So actually, return an
// IDecryptStream that wraps around the ISubStream.
IDecryptStream *wrapper =
new IDecryptStream(stream, true, _encryption_password);
stream = wrapper;
// Validate the password by confirming that the encryption header
// matches.
char this_header[_encrypt_header_size];
stream->read(this_header, _encrypt_header_size);
if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size ||
memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) {
express_cat.error()
<< "Unable to decrypt subfile " << subfile->_name << ".\n";
delete stream;
return NULL;
}
#endif // HAVE_SSL
}
if ((subfile->_flags & SF_compressed) != 0) {
#ifndef HAVE_ZLIB
express_cat.error()
@ -1086,8 +1147,8 @@ extract_subfile_to(int index, ostream &out) {
// Description: Assumes the _write pointer is at the indicated fpos,
// rounds the fpos up to the next legitimate address
// (using normalize_streampos()), and writes enough
// zeros to the stream to fill the gap. Returns the new
// fpos.
// zeroes to the stream to fill the gap. Returns the
// new fpos.
////////////////////////////////////////////////////////////////////
streampos Multifile::
pad_to_streampos(streampos fpos) {
@ -1098,6 +1159,7 @@ pad_to_streampos(streampos fpos) {
_write->put(0);
fpos += 1; // VC++ doesn't define streampos++ (!)
}
nassertr(_write->tellp() == fpos, fpos);
return fpos;
}
@ -1120,6 +1182,12 @@ add_new_subfile(Subfile *subfile, int compression_level) {
#endif // HAVE_ZLIB
}
#ifdef HAVE_SSL
if (_encryption_flag) {
subfile->_flags |= SF_encrypted;
}
#endif // HAVE_SSL
if (_next_index != (streampos)0) {
// If we're adding a Subfile to an already-existing Multifile, we
// will eventually need to repack the file.
@ -1348,7 +1416,7 @@ read_index(istream &read, streampos fpos, Multifile *multifile) {
_data_start = multifile->word_to_streampos(reader.get_uint32());
_data_length = reader.get_uint32();
_flags = reader.get_uint16();
if ((_flags & SF_compressed) != 0) {
if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
_uncompressed_length = reader.get_uint32();
} else {
_uncompressed_length = _data_length;
@ -1400,7 +1468,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
dg.add_uint32(multifile->streampos_to_word(_data_start));
dg.add_uint32(_data_length);
dg.add_uint16(_flags);
if ((_flags & SF_compressed) != 0) {
if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
dg.add_uint32(_uncompressed_length);
}
dg.add_uint16(_name.length());
@ -1449,7 +1517,8 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
// contents of the Subfile during a repack() operation.
////////////////////////////////////////////////////////////////////
streampos Multifile::Subfile::
write_data(ostream &write, istream *read, streampos fpos) {
write_data(ostream &write, istream *read, streampos fpos,
Multifile *multifile) {
nassertr(write.tellp() == fpos, fpos);
istream *source = _source;
@ -1494,6 +1563,28 @@ write_data(ostream &write, istream *read, streampos fpos) {
} else {
// We do have source data. Copy it in, and also measure its
// length.
ostream *putter = &write;
bool delete_putter = false;
#ifndef HAVE_SSL
// Without OpenSSL, we can't support encryption. The flag had
// better not be set.
nassertr((_flags & SF_encrypted) == 0, fpos);
#else // HAVE_ZLIB
if ((_flags & SF_encrypted) != 0) {
// Write it encrypted.
putter = new OEncryptStream(putter, delete_putter,
multifile->_encryption_password,
multifile->_encryption_algorithm);
delete_putter = true;
// Also write the encrypt_header to the beginning of the
// encrypted stream, so we can validate the password on
// decryption.
putter->write(_encrypt_header, _encrypt_header_size);
}
#endif // HAVE_ZLIB
#ifndef HAVE_ZLIB
// Without ZLIB, we can't support compression. The flag had
// better not be set.
@ -1501,31 +1592,27 @@ write_data(ostream &write, istream *read, streampos fpos) {
#else // HAVE_ZLIB
if ((_flags & SF_compressed) != 0) {
// Write it compressed.
streampos write_start = write.tellp();
_uncompressed_length = 0;
OCompressStream zstream(&write, false, _compression_level);
int byte = source->get();
while (!source->eof() && !source->fail()) {
_uncompressed_length++;
zstream.put(byte);
byte = source->get();
}
zstream.close();
streampos write_end = write.tellp();
_data_length = (size_t)(write_end - write_start);
} else
putter = new OCompressStream(putter, delete_putter, _compression_level);
delete_putter = true;
}
#endif // HAVE_ZLIB
{
// Write it uncompressed.
_uncompressed_length = 0;
int byte = source->get();
while (!source->eof() && !source->fail()) {
_uncompressed_length++;
write.put(byte);
byte = source->get();
}
_data_length = _uncompressed_length;
}
streampos write_start = fpos;
_uncompressed_length = 0;
int byte = source->get();
while (!source->eof() && !source->fail()) {
_uncompressed_length++;
putter->put(byte);
byte = source->get();
}
if (delete_putter) {
delete putter;
}
streampos write_end = write.tellp();
_data_length = (size_t)(write_end - write_start);
}
// We can't set _data_start until down here, after we have read the
@ -1559,7 +1646,7 @@ rewrite_index_data_start(ostream &write, Multifile *multifile) {
writer.add_uint32(multifile->streampos_to_word(_data_start));
writer.add_uint32(_data_length);
writer.add_uint16(_flags);
if ((_flags & SF_compressed) != 0) {
if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
writer.add_uint32(_uncompressed_length);
}
}

View File

@ -55,6 +55,13 @@ PUBLISHED:
void set_scale_factor(size_t scale_factor);
INLINE size_t get_scale_factor() const;
INLINE void set_encryption_flag(bool flag);
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);
string update_subfile(const string &subfile_name, const Filename &filename,
@ -71,7 +78,8 @@ PUBLISHED:
const string &get_subfile_name(int index) const;
size_t get_subfile_length(int index) const;
bool is_subfile_compressed(int index) const;
size_t get_subfile_compressed_length(int index) const;
bool is_subfile_encrypted(int index) const;
size_t get_subfile_internal_length(int index) const;
INLINE string read_subfile(int index);
istream *open_read_subfile(int index);
@ -99,6 +107,7 @@ private:
SF_index_invalid = 0x0002,
SF_data_invalid = 0x0004,
SF_compressed = 0x0008,
SF_encrypted = 0x0010,
};
class Subfile {
@ -109,7 +118,8 @@ private:
Multifile *multfile);
streampos write_index(ostream &write, streampos fpos,
Multifile *multifile);
streampos write_data(ostream &write, istream *read, streampos fpos);
streampos write_data(ostream &write, istream *read, streampos fpos,
Multifile *multifile);
void rewrite_index_data_start(ostream &write, Multifile *multifile);
void rewrite_index_flags(ostream &write);
INLINE bool is_deleted() const;
@ -155,6 +165,10 @@ private:
size_t _scale_factor;
size_t _new_scale_factor;
bool _encryption_flag;
string _encryption_password;
string _encryption_algorithm;
ifstream _read_file;
ofstream _write_file;
fstream _read_write_file;
@ -168,6 +182,9 @@ private:
static const int _current_major_ver;
static const int _current_minor_ver;
static const char _encrypt_header[];
static const size_t _encrypt_header_size;
friend class Subfile;
};

View File

@ -0,0 +1,79 @@
// Filename: password_hash.cxx
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "password_hash.h"
// The functions defined within this file rely on algorithms defined
// within OpenSSL.
#ifdef HAVE_SSL
#include <openssl/evp.h>
////////////////////////////////////////////////////////////////////
// Function: password_hash
// Access: Published
// Description: Generates a non-reversible hash of a particular
// length based on an arbitrary password and a random
// salt. This is much stronger than the algorithm
// implemented by the standard Unix crypt().
//
// The resulting hash can be useful for two primary
// purposes: (1) the hash may be recorded to disk in
// lieu of recording plaintext passwords, for validation
// against a password entered by the user later (which
// should produce the same hash given a particular
// salt), or (2) the hash may be used as input to an
// encryption algorithm that requires a key of a
// particular length.
//
// password is the text password provided by a user.
//
// salt should be a string of arbitrary random bytes (it
// need not be crypotographically secure, just different
// for each different hash).
//
// iters should be a number in the thousands to indicate
// the number of times the hash algorithm should be
// applied. In general, iters should be chosen to make
// the computation as expensive as it can be and still
// be tolerable, to reduce the attractiveness of a
// brute-force attack.
//
// keylen is the length in bytes of the required key
// hash.
////////////////////////////////////////////////////////////////////
string
password_hash(const string &password, const string &salt,
int iters, int keylen) {
nassertr(iters > 0 && keylen > 0, string());
unsigned char *dk = new unsigned char[keylen];
int result =
PKCS5_PBKDF2_HMAC_SHA1((const char *)password.data(), password.length(),
(unsigned char *)salt.data(), salt.length(),
iters, keylen, dk);
nassertr(result > 0, string());
string hash((char *)dk, keylen);
delete[] dk;
return hash;
}
#endif // HAVE_SSL

View File

@ -0,0 +1,37 @@
// Filename: password_hash.h
// Created by: drose (01Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef PASSWORD_HASH_H
#define PASSWORD_HASH_H
#include "pandabase.h"
// The functions defined within this file rely on algorithms defined
// within OpenSSL.
#ifdef HAVE_SSL
BEGIN_PUBLISH
string password_hash(const string &password, const string &salt,
int iters, int keylen);
END_PUBLISH
#endif // HAVE_SSL
#endif

View File

@ -83,7 +83,7 @@ mount(Multifile *multifile, const string &mount_point, int flags) {
////////////////////////////////////////////////////////////////////
bool VirtualFileSystem::
mount(const Filename &physical_filename, const string &mount_point,
int flags) {
int flags, const string &password) {
if (!physical_filename.exists()) {
express_cat.warning()
<< "Attempt to mount " << physical_filename << ", not found.\n";
@ -103,6 +103,8 @@ mount(const Filename &physical_filename, const string &mount_point,
// It's not a directory; it must be a Multifile.
Multifile *multifile = new Multifile;
multifile->set_encryption_password(password);
// For now these are always opened read only. Maybe later we'll
// support read-write on Multifiles.
flags |= MF_read_only;
@ -550,7 +552,21 @@ get_global_ptr() {
mount_desc = ExecutionEnvironment::expand_string(mount_desc);
Filename physical_filename = Filename::from_os_specific(mount_desc);
_global_ptr->mount(physical_filename, mount_point, 0);
int flags = 0;
string password;
// Split the options up by commas.
size_t p = 0;
size_t q = options.find(',', p);
while (q != string::npos) {
parse_option(options.substr(p, q - p),
flags, password);
p = q + 1;
q = options.find(',', p);
}
parse_option(options.substr(p), flags, password);
_global_ptr->mount(physical_filename, mount_point, flags, password);
}
}
}
@ -668,3 +684,25 @@ found_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
return false;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileSystem::parse_option
// Access: Private, Static
// Description: Parses one of the option flags in the options list on
// the vfs-mount Config.prc line.
////////////////////////////////////////////////////////////////////
void VirtualFileSystem::
parse_option(const string &option, int &flags, string &password) {
if (option == "0" || option.empty()) {
// 0 is the null option.
} else if (option == "ro") {
flags |= MF_read_only;
} else if (option.substr(0, 3) == "pw:") {
password = option.substr(3);
} else {
express_cat.warning()
<< "Invalid option on vfs-mount: \"" << option << "\"\n";
}
}

View File

@ -54,7 +54,8 @@ PUBLISHED:
};
bool mount(Multifile *multifile, const string &mount_point, int flags);
bool mount(const Filename &physical_filename, const string &mount_point, int flags);
bool mount(const Filename &physical_filename, const string &mount_point,
int flags, const string &password = "");
int unmount(Multifile *multifile);
int unmount(const Filename &physical_filename);
int unmount_point(const string &mount_point);
@ -94,6 +95,8 @@ private:
Filename normalize_mount_point(const string &mount_point) const;
bool found_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
VirtualFileMount *mount, const string &local_filename) const;
static void parse_option(const string &option,
int &flags, string &password);
typedef pvector<VirtualFileMount *> Mounts;
Mounts _mounts;