express: Add support for bytes multifile encryption passwords (#1334)

This commit is contained in:
Derzsi Dániel 2022-07-20 23:50:06 +03:00 committed by GitHub
parent 2208cc8bff
commit d79709f004
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 141 additions and 2 deletions

View File

@ -141,6 +141,8 @@ set(P3EXPRESS_IGATEEXT
virtualFileSystem_ext.h
virtualFile_ext.cxx
virtualFile_ext.h
multifile_ext.h
multifile_ext.I
)
composite_sources(p3express P3EXPRESS_SOURCES)

View File

@ -69,8 +69,6 @@ PUBLISHED:
INLINE void set_encryption_flag(bool flag);
INLINE bool get_encryption_flag() const;
INLINE void set_encryption_password(const std::string &encryption_password);
INLINE const std::string &get_encryption_password() const;
INLINE void set_encryption_algorithm(const std::string &encryption_algorithm);
INLINE const std::string &get_encryption_algorithm() const;
@ -86,6 +84,9 @@ PUBLISHED:
std::string update_subfile(const std::string &subfile_name, const Filename &filename,
int compression_level);
EXTENSION(INLINE PyObject *set_encryption_password(PyObject *encryption_password) const);
EXTENSION(INLINE PyObject *get_encryption_password() const);
#ifdef HAVE_OPENSSL
bool add_signature(const Filename &certificate,
const Filename &chain,
@ -143,6 +144,9 @@ PUBLISHED:
INLINE const std::string &get_header_prefix() const;
public:
INLINE void set_encryption_password(const std::string &encryption_password);
INLINE const std::string &get_encryption_password() const;
#ifdef HAVE_OPENSSL
class CertRecord {
public:

View File

@ -0,0 +1,79 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file multifile_ext.I
* @author Derzsi Daniel
* @date 2022-07-20
*/
#include <cstring>
/**
* Specifies the password, either as a Python string or a Python bytes object,
* that will be used to encrypt subfiles subsequently added to the multifile
*/
INLINE PyObject *Extension<Multifile>::
set_encryption_password(PyObject *encryption_password) const {
Py_ssize_t pass_len;
// Have we been passed a string?
if (PyUnicode_Check(encryption_password)) {
const char *pass_str = PyUnicode_AsUTF8AndSize(encryption_password, &pass_len);
_this->set_encryption_password(std::string(pass_str, pass_len));
return Dtool_Return_None();
}
// Have we been passed a bytes object?
if (PyBytes_Check(encryption_password)) {
char *pass_str;
if (PyBytes_AsStringAndSize(encryption_password, &pass_str, &pass_len) < 0) {
PyErr_SetString(PyExc_TypeError, "A valid bytes object is required.");
return NULL;
}
// It is dangerous to use null bytes inside the encryption password.
// OpenSSL will cut off the password prematurely at the first null byte
// encountered.
if (memchr(pass_str, '\0', pass_len) != NULL) {
PyErr_SetString(PyExc_ValueError, "The password must not contain null bytes.");
return NULL;
}
_this->set_encryption_password(std::string(pass_str, pass_len));
return Dtool_Return_None();
}
return Dtool_Raise_BadArgumentsError(
"set_encryption_password(const Multifile self, str encryption_password)\n"
);
}
/**
* Returns the password that will be used to encrypt subfiles subsequently
* added to the multifile, either as a Python string (when possible) or a
* Python bytes object.
*/
INLINE PyObject *Extension<Multifile>::
get_encryption_password() const {
std::string password = _this->get_encryption_password();
const char *pass_str = password.c_str();
Py_ssize_t pass_len = password.length();
// First, attempt to decode it as an UTF-8 string...
PyObject *result = PyUnicode_DecodeUTF8(pass_str, pass_len, NULL);
if (PyErr_Occurred()) {
// This password cannot be decoded as an UTF-8 string, so let's
// return it as a bytes object.
PyErr_Clear();
result = PyBytes_FromStringAndSize(pass_str, pass_len);
}
return result;
}

View File

@ -0,0 +1,40 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file multifile_ext.h
* @author Derzsi Daniel
* @date 2022-07-20
*/
#ifndef MULTIFILE_EXT_H
#define MULTIFILE_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "multifile.h"
#include "py_panda.h"
/**
* This class defines the extension methods for Multifile, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<Multifile> : public ExtensionBase<Multifile> {
public:
INLINE PyObject *set_encryption_password(PyObject *encryption_password) const;
INLINE PyObject *get_encryption_password() const;
};
#include "multifile_ext.I"
#endif // HAVE_PYTHON
#endif // MULTIFILE_EXT_H

View File

@ -3,3 +3,4 @@
#include "stringStream_ext.cxx"
#include "virtualFileSystem_ext.cxx"
#include "virtualFile_ext.cxx"
#include "multifile_ext.h"

View File

@ -10,3 +10,16 @@ def test_multifile_read_empty():
assert m.is_read_valid()
assert m.get_num_subfiles() == 0
m.close()
def test_multifile_password():
m = Multifile()
m.set_encryption_password('Panda3D rocks!')
assert m.get_encryption_password() == 'Panda3D rocks!'
m.set_encryption_password(b'Panda3D is awesome!')
assert m.get_encryption_password() == 'Panda3D is awesome!'
m.set_encryption_password(b'\xc4\x97\xa1\x01\x85\xb6')
assert m.get_encryption_password() == b'\xc4\x97\xa1\x01\x85\xb6'