Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cache_libs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ on:
required: false
type: string
XMLSEC1_VERSION:
default: "1.3.12"
default: "1.3.13"
required: false
type: string
ZLIB_VERSION:
Expand Down
8 changes: 5 additions & 3 deletions build_support/lib_xmlsec_dependency_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def triplet(self) -> str:


class LibXmlsecDependencyBuilder:
WINDOWS_LIBS_DOWNLOAD_RELEASE_URL = 'https://github.com/mxamin/python-xmlsec-win-binaries/releases/download/2026.10.23/'
WINDOWS_LIBS_DOWNLOAD_RELEASE_URL = 'https://github.com/mxamin/python-xmlsec-win-binaries/releases/download/2026.10.28/'
LIB_VERSION_ENV_VARS: ClassVar[dict[str, str]] = {
'libiconv_version': 'PYXMLSEC_LIBICONV_VERSION',
'libxml2_version': 'PYXMLSEC_LIBXML2_VERSION',
Expand All @@ -50,15 +50,15 @@ class LibXmlsecDependencyBuilder:
'libxml2_version': '2.14.6', # Make sure it matches with lxml
'libxslt_version': '1.1.43', # 1.1.44+ requires libxml2 >= 2.15.1
'openssl_version': '3.6.5',
'xmlsec1_version': '1.3.12',
'xmlsec1_version': '1.3.13',
'zlib_version': '1.3.2',
}
WINDOWS_DEFAULT_LIB_VERSIONS: ClassVar[dict[str, str]] = {
'libiconv_version': '1.19-1',
'libxml2_version': '2.11.9-8', # Make sure it matches with lxml
'libxslt_version': '1.1.45',
'openssl_version': '3.5.9',
'xmlsec1_version': '1.3.12',
'xmlsec1_version': '1.3.13',
'zlib_version': '1.3.2',
}

Expand Down Expand Up @@ -390,6 +390,8 @@ def _build_xmlsec1(self, env, prefix_arg, host_arg):
'./configure',
prefix_arg,
'--disable-shared',
'--with-pic',
'--disable-apps',
'--disable-gost',
'--enable-md5',
'--enable-ripemd160',
Expand Down
25 changes: 25 additions & 0 deletions doc/source/modules/constants.rst
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,31 @@ Transforms

Transform could be used for operation.

.. data:: xmlsec.constants.TransformUriTypeNone

No URI types are allowed.

.. data:: xmlsec.constants.TransformUriTypeEmpty

Empty URI (:xml:`URI=""`), i.e. the document containing the signature.

.. data:: xmlsec.constants.TransformUriTypeSameDocument

Same-document URI (:xml:`URI="#id"`).

.. data:: xmlsec.constants.TransformUriTypeLocal

Local file URI (:xml:`URI="file:///path"`).

.. data:: xmlsec.constants.TransformUriTypeRemote

Any other non-empty URI, e.g. :xml:`URI="http://..."`, :xml:`URI="cid:..."`
or a relative path such as :xml:`URI="other.xml"`.

.. data:: xmlsec.constants.TransformUriTypeAny

Any URI type.

.. data:: xmlsec.constants.TransformInclC14N

The regular (inclusive) C14N without comments transform klass.
Expand Down
7 changes: 7 additions & 0 deletions src/constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ int PyXmlSec_ConstantsModule_Init(PyObject* package) {
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUsageEncryptionMethod) < 0) goto ON_FAIL;
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUsageAny) < 0) goto ON_FAIL;

if (PYXMLSEC_ADD_INT_CONSTANT(TransformUriTypeNone) < 0) goto ON_FAIL;
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUriTypeEmpty) < 0) goto ON_FAIL;
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUriTypeSameDocument) < 0) goto ON_FAIL;
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUriTypeLocal) < 0) goto ON_FAIL;
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUriTypeRemote) < 0) goto ON_FAIL;
if (PYXMLSEC_ADD_INT_CONSTANT(TransformUriTypeAny) < 0) goto ON_FAIL;

#undef PYXMLSEC_ADD_INT_CONSTANT

#define PYXMLSEC_DECLARE_NAMESPACE(var, name) \
Expand Down
41 changes: 41 additions & 0 deletions src/ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,40 @@ static PyObject* PyXmlSec_SignatureContextSetEnabledKeyData(PyObject* self, PyOb
return NULL;
}

static const char PyXmlSec_SignatureContextEnabledReferenceUris__doc__[] = \
"Bitmask of :samp:`TransformUriType{XXX}` constants allowed in :xml:`<dsig:Reference/>` URI.\n\n"
".. note:: since xmlsec1 1.3.13 only empty and same-document URIs are enabled by default. "
"Dereferencing local or remote URIs (e.g. through :func:`~xmlsec.register_callbacks`) requires "
"an explicit opt-in.\n";
static PyObject* PyXmlSec_SignatureContextEnabledReferenceUrisGet(PyObject* self, void* closure) {
PyXmlSec_SignatureContext* ctx = (PyXmlSec_SignatureContext*)self;
return PyLong_FromUnsignedLong(ctx->handle->enabledReferenceUris);
}

static int PyXmlSec_SignatureContextEnabledReferenceUrisSet(PyObject* self, PyObject* value, void* closure) {
PyXmlSec_SignatureContext* ctx = (PyXmlSec_SignatureContext*)self;
unsigned long uris;

if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "cannot delete enabled_reference_uris.");
return -1;
}
if (!PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "instance of *int* expected.");
return -1;
}
uris = PyLong_AsUnsignedLong(value);
if (PyErr_Occurred()) {
return -1;
}
if (uris > xmlSecTransformUriTypeAny) {
PyErr_SetString(PyExc_ValueError, "invalid uri type mask.");
return -1;
}
ctx->handle->enabledReferenceUris = (xmlSecTransformUriType)uris;
return 0;
}

static PyGetSetDef PyXmlSec_SignatureContextGetSet[] = {
{
"key",
Expand All @@ -525,6 +559,13 @@ static PyGetSetDef PyXmlSec_SignatureContextGetSet[] = {
(char*)PyXmlSec_SignatureContextKey__doc__,
NULL
},
{
"enabled_reference_uris",
(getter)PyXmlSec_SignatureContextEnabledReferenceUrisGet,
(setter)PyXmlSec_SignatureContextEnabledReferenceUrisSet,
(char*)PyXmlSec_SignatureContextEnabledReferenceUris__doc__,
NULL
},
{NULL} /* Sentinel */
};

Expand Down
11 changes: 11 additions & 0 deletions src/enc.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ static int PyXmlSec_EncryptionContext__init__(PyObject* self, PyObject* args, Py
ctx->handle->keyInfoReadCtx.flags = XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH;
ctx->handle->keyInfoWriteCtx.flags = XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH;

// xmlsec 1.3.13 no longer copies these flags to the read context of the nested
// context used to encrypt <enc:EncryptedKey/>, so the session key encryption key
// lookup becomes strict. Create the nested context upfront (it is reused by
// xmlsec) and enable lax key search on it as well.
if (xmlSecKeyInfoCtxCreateEncCtx(&(ctx->handle->keyInfoWriteCtx)) < 0) {
// ctx->manager is owned by ctx now and released on dealloc
PyXmlSec_SetLastError("failed to create the nested encryption context");
return -1;
}
ctx->handle->keyInfoWriteCtx.encCtx->keyInfoReadCtx.flags = XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH;

return 0;
ON_FAIL:
PYXMLSEC_DEBUGF("%p: init enc context - failed", self);
Expand Down
7 changes: 7 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <xmlsec/xmlsec.h>
#include <xmlsec/crypto.h>
#include <xmlsec/dl.h>
#include <xmlsec/errors.h>
#include <xmlsec/base64.h>
#include <xmlsec/io.h>
Expand Down Expand Up @@ -56,6 +57,12 @@ static void PyXmlSec_Free(int what) {
}

static int PyXmlSec_Init(void) {
// Already initialized (e.g. on module import). Since xmlsec1 1.3.13,
// calling xmlSecInit() again without a shutdown fails.
if (free_mode == _PYXMLSEC_FREE_ALL) {
return 0;
}

if (xmlSecInit() < 0) {
PyXmlSec_SetLastError("cannot initialize xmlsec library.");
PyXmlSec_Free(_PYXMLSEC_FREE_NONE);
Expand Down
1 change: 1 addition & 0 deletions src/xmlsec/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class KeysManager:

class SignatureContext:
key: Key | None
enabled_reference_uris: int
def enable_reference_transform(self, transform: Transform) -> None: ...
def enable_signature_transform(self, transform: Transform) -> None: ...
def register_id(self, node: _Element, id_attr: str = ..., id_ns: str | None = ...) -> None: ...
Expand Down
6 changes: 6 additions & 0 deletions src/xmlsec/constants.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ TransformSha224: Final[__Transform]
TransformSha256: Final[__Transform]
TransformSha384: Final[__Transform]
TransformSha512: Final[__Transform]
TransformUriTypeAny: Final[int]
TransformUriTypeEmpty: Final[int]
TransformUriTypeLocal: Final[int]
TransformUriTypeNone: Final[int]
TransformUriTypeRemote: Final[int]
TransformUriTypeSameDocument: Final[int]
TransformUsageAny: Final[int]
TransformUsageC14NMethod: Final[int]
TransformUsageDSigTransform: Final[int]
Expand Down
17 changes: 17 additions & 0 deletions tests/test_ds.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ def test_set_key_bad_type(self):
with self.assertRaisesRegex(TypeError, r'instance of \*xmlsec.Key\* expected.'):
ctx.key = ''

def test_set_enabled_reference_uris(self):
ctx = xmlsec.SignatureContext()
uris = consts.TransformUriTypeEmpty | consts.TransformUriTypeSameDocument | consts.TransformUriTypeRemote
ctx.enabled_reference_uris = uris
self.assertEqual(ctx.enabled_reference_uris, uris)

def test_set_enabled_reference_uris_bad_args(self):
ctx = xmlsec.SignatureContext()
with self.assertRaisesRegex(TypeError, r'instance of \*int\* expected.'):
ctx.enabled_reference_uris = ''
with self.assertRaises(OverflowError):
ctx.enabled_reference_uris = -1
with self.assertRaisesRegex(ValueError, 'invalid uri type mask'):
ctx.enabled_reference_uris = consts.TransformUriTypeAny + 1
with self.assertRaisesRegex(TypeError, 'cannot delete'):
del ctx.enabled_reference_uris

def test_set_invalid_key(self):
ctx = xmlsec.SignatureContext(manager=xmlsec.KeysManager())
with self.assertRaisesRegex(TypeError, 'empty key.'):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ def _sign_doc(self):
xmlsec.template.add_reference(sign, consts.TransformSha1, uri='cid:123456')

ctx = xmlsec.SignatureContext()
# xmlsec1 >= 1.3.13 only allows empty and same-document reference URIs by default
ctx.enabled_reference_uris = consts.TransformUriTypeAny
ctx.key = xmlsec.Key.from_file(self.path('rsakey.pem'), format=consts.KeyDataFormatPem)
ctx.sign(sign)
return sign
Expand Down
Loading