├── .appveyor.yml ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── linuxbrew.yml │ ├── macosx.yml │ ├── manylinux.yml │ ├── sdist.yml │ └── wheels.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.rst ├── doc ├── Makefile └── source │ ├── api.rst │ ├── conf.py │ ├── examples.rst │ ├── examples │ ├── decrypt.py │ ├── enc1-doc.xml │ ├── enc1-res.xml │ ├── encrypt.py │ ├── rsacert.pem │ ├── rsakey.pem │ ├── rsapub.pem │ ├── sign.py │ ├── sign1-res.xml │ ├── sign1-tmpl.xml │ ├── sign_binary.py │ ├── verify.py │ └── verify_binary.py │ ├── index.rst │ ├── install.rst │ ├── modules │ ├── constants.rst │ ├── template.rst │ ├── tree.rst │ └── xmlsec.rst │ └── requirements.txt ├── pyproject.toml ├── requirements-test.txt ├── requirements.txt ├── setup.cfg ├── setup.py ├── src ├── common.h ├── constants.c ├── constants.h ├── debug.h ├── ds.c ├── enc.c ├── exception.c ├── exception.h ├── keys.c ├── keys.h ├── lxml.c ├── lxml.h ├── main.c ├── platform.h ├── template.c ├── tree.c ├── utils.c ├── utils.h └── xmlsec │ ├── __init__.pyi │ ├── constants.pyi │ ├── py.typed │ ├── template.pyi │ └── tree.pyi └── tests ├── __init__.py ├── base.py ├── conftest.py ├── data ├── deskey.bin ├── doc.xml ├── dsacert.der ├── dsakey.der ├── enc1-in.xml ├── enc1-out.xml ├── enc2-in.xml ├── enc2-out.xml ├── enc3-in.xml ├── enc3-out.xml ├── enc_template.xml ├── rsacert.pem ├── rsakey.pem ├── rsapub.pem ├── sign1-in.xml ├── sign1-out.xml ├── sign2-in.xml ├── sign2-out.xml ├── sign3-in.xml ├── sign3-out.xml ├── sign4-in.xml ├── sign4-out.xml ├── sign5-in.xml ├── sign5-out-xmlsec_1_2_36_to_37.xml ├── sign5-out.xml ├── sign6-in.bin ├── sign6-out.bin └── sign_template.xml ├── softhsm_setup.py ├── test_constants.py ├── test_doc_examples.py ├── test_ds.py ├── test_enc.py ├── test_keys.py ├── test_main.py ├── test_pkcs11.py ├── test_templates.py ├── test_tree.py ├── test_type_stubs.py └── test_xmlsec.py /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - python: 35 4 | - python: 35-x64 5 | - python: 36 6 | - python: 36-x64 7 | - python: 37 8 | - python: 37-x64 9 | - python: 38 10 | - python: 38-x64 11 | - python: 39 12 | python_version: 3.9.13 13 | - python: 39-x64 14 | python_version: 3.9.13 15 | - python: 310 16 | python_version: 3.10.6 17 | - python: 310-x64 18 | python_version: 3.10.6 19 | - python: 311 20 | python_version: 3.11.2 21 | - python: 311-x64 22 | python_version: 3.11.2 23 | 24 | install: 25 | - ps: | 26 | # from https://github.com/appveyor/build-images/blob/27bde614bc60d7ef7a8bc46182f4d7582fa11b56/scripts/Windows/install_python.ps1#L88-L108 27 | function InstallPythonEXE($targetPath, $version) { 28 | $urlPlatform = "" 29 | if ($targetPath -match '-x64$') { 30 | $urlPlatform = "-amd64" 31 | } 32 | Write-Host "Installing Python $version$urlPlatform to $($targetPath)..." -ForegroundColor Cyan 33 | $downloadUrl = "https://www.python.org/ftp/python/$version/python-$version$urlPlatform.exe" 34 | Write-Host "Downloading $($downloadUrl)..." 35 | $exePath = "$env:TEMP\python-$version.exe" 36 | (New-Object Net.WebClient).DownloadFile($downloadUrl, $exePath) 37 | Write-Host "Installing..." 38 | cmd /c start /wait $exePath /quiet TargetDir="$targetPath" Shortcuts=0 Include_launcher=1 InstallLauncherAllUsers=1 Include_debug=1 39 | Remove-Item $exePath 40 | Write-Host "Installed Python $version" -ForegroundColor Green 41 | } 42 | if ( -not ( Test-Path -Path C:\\Python$env:PYTHON -PathType Container ) ) { 43 | InstallPythonEXE C:\\Python$env:PYTHON $env:PYTHON_VERSION 44 | } 45 | - SET PATH=C:\\Python%PYTHON%;c:\\Python%PYTHON%\\scripts;%PATH% 46 | - python -m pip install -U pip wheel setuptools 47 | 48 | build: off 49 | build_script: 50 | - python setup.py bdist_wheel 51 | 52 | test: off 53 | test_script: 54 | - pip install -r requirements-test.txt 55 | - pip install xmlsec --only-binary=xmlsec --no-index --find-links=dist 56 | - pytest -v --color=yes --junitxml=unittests.xml 57 | - ps: Get-ChildItem dist\*.whl | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } 58 | 59 | on_finish: 60 | - ps: | 61 | # archive test results at AppVeyor 62 | $wc = New-Object 'System.Net.WebClient' 63 | $wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\unittests.xml)) 64 | $LastExitCode = 0 65 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/workflows/linuxbrew.yml: -------------------------------------------------------------------------------- 1 | name: linuxbrew 2 | on: [push, pull_request] 3 | jobs: 4 | linuxbrew: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python: ["3.9", "3.10", "3.11", "3.12", "3.13"] 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Install brew 12 | run: | 13 | sudo apt install -y build-essential procps curl file git 14 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 15 | echo "/home/linuxbrew/.linuxbrew/bin" >> $GITHUB_PATH 16 | - name: Install build dependencies 17 | run: | 18 | brew update 19 | brew install python@${{ matrix.python }} gcc libxml2 libxmlsec1 pkg-config 20 | echo "/home/linuxbrew/.linuxbrew/opt/python@${{ matrix.python }}/libexec/bin" >> $GITHUB_PATH 21 | - name: Build wheel 22 | run: | 23 | python3 -m venv build_venv 24 | source build_venv/bin/activate 25 | pip3 install --upgrade setuptools wheel build 26 | export CFLAGS="-I$(brew --prefix)/include" 27 | export LDFLAGS="-L$(brew --prefix)/lib" 28 | python3 -m build 29 | rm -rf build/ 30 | - name: Run tests 31 | run: | 32 | python3 -m venv test_venv 33 | source test_venv/bin/activate 34 | pip3 install --upgrade --no-binary=lxml -r requirements-test.txt 35 | pip3 install xmlsec --only-binary=xmlsec --no-index --find-links=dist/ 36 | pytest -v --color=yes 37 | -------------------------------------------------------------------------------- /.github/workflows/macosx.yml: -------------------------------------------------------------------------------- 1 | name: MacOS 2 | on: [push, pull_request] 3 | jobs: 4 | macosx: 5 | runs-on: macos-latest 6 | strategy: 7 | matrix: 8 | python: ["3.9", "3.10", "3.11", "3.12", "3.13"] 9 | static_deps: ["static", ""] 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Setup Python 13 | uses: actions/setup-python@v4 14 | with: 15 | python-version: ${{ matrix.python }} 16 | - name: Install build dependencies 17 | run: | 18 | pip install --upgrade pip setuptools wheel build 19 | brew install libxml2 libxmlsec1 pkg-config 20 | - name: Build macosx_x86_64 wheel 21 | env: 22 | CC: clang 23 | CFLAGS: "-fprofile-instr-generate -fcoverage-mapping" 24 | LDFLAGS: "-fprofile-instr-generate -fcoverage-mapping" 25 | PYXMLSEC_STATIC_DEPS: ${{ matrix.static_deps }} 26 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: | 28 | export PKG_CONFIG_PATH="$(brew --prefix)/opt/libxml2/lib/pkgconfig" 29 | python -m build 30 | rm -rf build/ 31 | - name: Set environment variables 32 | shell: bash 33 | run: | 34 | echo "PKGVER=$(python setup.py --version)" >> $GITHUB_ENV 35 | echo "LLVM_PROFILE_FILE=pyxmlsec.profraw" >> $GITHUB_ENV 36 | - name: Install test dependencies 37 | run: | 38 | export PKG_CONFIG_PATH="$(brew --prefix)/opt/libxml2/lib/pkgconfig" 39 | pip install coverage --upgrade --no-binary=lxml -r requirements-test.txt 40 | pip install xmlsec --only-binary=xmlsec --no-index --find-links=dist/ 41 | echo "PYXMLSEC_LIBFILE=$(python -c 'import xmlsec; print(xmlsec.__file__)')" >> $GITHUB_ENV 42 | - name: Run tests 43 | run: | 44 | coverage run -m pytest -v --color=yes 45 | - name: Report coverage to codecov 46 | run: | 47 | /Library/Developer/CommandLineTools/usr/bin/llvm-profdata merge -sparse ${{ env.LLVM_PROFILE_FILE }} -output pyxmlsec.profdata 48 | /Library/Developer/CommandLineTools/usr/bin/llvm-cov show ${{ env.PYXMLSEC_LIBFILE }} --arch=$(uname -m) --instr-profile=pyxmlsec.profdata src > coverage.txt 49 | bash <(curl -s https://codecov.io/bash) -f coverage.txt 50 | if: matrix.static_deps != 'static' 51 | -------------------------------------------------------------------------------- /.github/workflows/manylinux.yml: -------------------------------------------------------------------------------- 1 | name: manylinux 2 | on: [push, pull_request] 3 | jobs: 4 | manylinux: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python-abi: [cp36-cp36m, cp37-cp37m, cp38-cp38, cp39-cp39, cp310-cp310, cp311-cp311] 9 | image: 10 | - manylinux2014_x86_64 11 | - manylinux_2_28_x86_64 12 | - musllinux_1_1_x86_64 13 | container: quay.io/pypa/${{ matrix.image }} 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Install python build dependencies 17 | run: | 18 | # https://github.com/actions/runner/issues/2033 19 | chown -R $(id -u):$(id -g) $PWD 20 | /opt/python/${{ matrix.python-abi }}/bin/pip install --upgrade pip setuptools wheel build 21 | - name: Install system build dependencies (manylinux) 22 | run: | 23 | yum install -y perl-core 24 | if: contains(matrix.image, 'manylinux') 25 | - name: Set environment variables 26 | shell: bash 27 | run: | 28 | echo "PKGVER=$(/opt/python/${{ matrix.python-abi }}/bin/python setup.py --version)" >> $GITHUB_ENV 29 | - name: Build linux_x86_64 wheel 30 | env: 31 | PYXMLSEC_STATIC_DEPS: true 32 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | run: | 34 | /opt/python/${{ matrix.python-abi }}/bin/python -m build 35 | - name: Label manylinux wheel 36 | run: | 37 | ls -la dist/ 38 | auditwheel show dist/xmlsec-${{ env.PKGVER }}-${{ matrix.python-abi }}-linux_x86_64.whl 39 | auditwheel repair dist/xmlsec-${{ env.PKGVER }}-${{ matrix.python-abi }}-linux_x86_64.whl 40 | ls -la wheelhouse/ 41 | auditwheel show wheelhouse/xmlsec-${{ env.PKGVER }}-${{ matrix.python-abi }}-*${{ matrix.image }}*.whl 42 | - name: Install test dependencies 43 | run: | 44 | /opt/python/${{ matrix.python-abi }}/bin/pip install --upgrade -r requirements-test.txt 45 | /opt/python/${{ matrix.python-abi }}/bin/pip install xmlsec --only-binary=xmlsec --no-index --find-links=wheelhouse/ 46 | - name: Run tests 47 | run: | 48 | /opt/python/${{ matrix.python-abi }}/bin/pytest -v --color=yes 49 | -------------------------------------------------------------------------------- /.github/workflows/sdist.yml: -------------------------------------------------------------------------------- 1 | name: sdist 2 | on: [push, pull_request] 3 | jobs: 4 | sdist: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python: ["3.9", "3.10", "3.11", "3.12", "3.13"] 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Set up Python 12 | uses: actions/setup-python@v4 13 | with: 14 | python-version: ${{ matrix.python }} 15 | - name: Install build dependencies 16 | run: | 17 | pip install --upgrade pip setuptools wheel 18 | - name: Package source dist 19 | run: | 20 | python setup.py sdist 21 | - name: Install test dependencies 22 | run: | 23 | sudo apt-get update 24 | sudo apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl opensc softhsm2 libengine-pkcs11-openssl 25 | pip install --upgrade -r requirements-test.txt --no-binary lxml 26 | pip install dist/xmlsec-$(python setup.py --version).tar.gz 27 | - name: Run tests 28 | run: | 29 | pytest -v --color=yes 30 | -------------------------------------------------------------------------------- /.github/workflows/wheels.yml: -------------------------------------------------------------------------------- 1 | name: Wheel build 2 | 3 | on: 4 | release: 5 | types: [created] 6 | schedule: 7 | # ┌───────────── minute (0 - 59) 8 | # │ ┌───────────── hour (0 - 23) 9 | # │ │ ┌───────────── day of the month (1 - 31) 10 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 11 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 12 | # │ │ │ │ │ 13 | - cron: "42 3 * * 4" 14 | push: 15 | pull_request: 16 | workflow_dispatch: 17 | 18 | permissions: {} 19 | 20 | jobs: 21 | sdist: 22 | runs-on: ubuntu-latest 23 | 24 | permissions: 25 | contents: write 26 | 27 | steps: 28 | - uses: actions/checkout@v4.1.1 29 | with: 30 | fetch-depth: 0 31 | 32 | - name: Set up Python 33 | uses: actions/setup-python@v5.0.0 34 | with: 35 | python-version: "3.x" 36 | 37 | - name: Install build dependencies 38 | run: | 39 | pip install --upgrade pip setuptools wheel 40 | 41 | - name: Package source dist 42 | run: python setup.py sdist 43 | 44 | - name: Install test dependencies 45 | run: | 46 | sudo apt-get update -y -q 47 | sudo apt-get install -y -q libxml2-dev libxslt1-dev libxmlsec1-dev libxmlsec1-openssl opensc softhsm2 libengine-pkcs11-openssl 48 | pip install --upgrade -r requirements-test.txt --no-binary lxml 49 | pip install dist/xmlsec-$(python setup.py --version).tar.gz 50 | 51 | - name: Run tests 52 | run: pytest -v --color=yes 53 | 54 | - name: Upload sdist 55 | uses: actions/upload-artifact@v4.3.1 56 | with: 57 | name: sdist 58 | path: dist/*.tar.gz 59 | 60 | generate-wheels-matrix: 61 | # Create a matrix of all architectures & versions to build. 62 | # This enables the next step to run cibuildwheel in parallel. 63 | # From https://iscinumpy.dev/post/cibuildwheel-2-10-0/#only-210 64 | name: Generate wheels matrix 65 | runs-on: ubuntu-latest 66 | outputs: 67 | include: ${{ steps.set-matrix.outputs.include }} 68 | steps: 69 | - uses: actions/checkout@v4 70 | - name: Install cibuildwheel 71 | # Nb. keep cibuildwheel version pin consistent with job below 72 | run: pipx install cibuildwheel==2.21.3 73 | - id: set-matrix 74 | # Once we have the windows build figured out, it can be added here 75 | # by updating the matrix to include windows builds as well. 76 | # See example here: 77 | # https://github.com/lxml/lxml/blob/3ccc7d583e325ceb0ebdf8fc295bbb7fc8cd404d/.github/workflows/wheels.yml#L95C1-L106C51 78 | run: | 79 | MATRIX=$( 80 | { 81 | cibuildwheel --print-build-identifiers --platform linux \ 82 | | jq -nRc '{"only": inputs, "os": "ubuntu-latest"}' \ 83 | && cibuildwheel --print-build-identifiers --platform macos \ 84 | | jq -nRc '{"only": inputs, "os": "macos-latest"}' \ 85 | && cibuildwheel --print-build-identifiers --platform windows \ 86 | | jq -nRc '{"only": inputs, "os": "windows-2019"}' 87 | } | jq -sc 88 | ) 89 | echo "include=$MATRIX" 90 | echo "include=$MATRIX" >> $GITHUB_OUTPUT 91 | 92 | build_wheels: 93 | name: Build for ${{ matrix.only }} 94 | needs: generate-wheels-matrix 95 | runs-on: ${{ matrix.os }} 96 | 97 | strategy: 98 | fail-fast: false 99 | matrix: 100 | include: ${{ fromJson(needs.generate-wheels-matrix.outputs.include) }} 101 | 102 | env: 103 | PYXMLSEC_LIBXML2_VERSION: 2.13.5 104 | PYXMLSEC_LIBXSLT_VERSION: 1.1.42 105 | 106 | steps: 107 | - name: Check out the repo 108 | uses: actions/checkout@v4 109 | with: 110 | fetch-depth: 0 111 | 112 | - name: Set up QEMU 113 | if: runner.os == 'Linux' 114 | uses: docker/setup-qemu-action@v3 115 | with: 116 | platforms: all 117 | 118 | - name: Build wheels 119 | uses: pypa/cibuildwheel@v2.21.3 120 | with: 121 | only: ${{ matrix.only }} 122 | env: 123 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 124 | 125 | - uses: actions/upload-artifact@v4.3.1 126 | with: 127 | path: ./wheelhouse/*.whl 128 | name: xmlsec-wheel-${{ matrix.only }} 129 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dot files 2 | .* 3 | !.editorconfig 4 | !.travis* 5 | !.appveyor* 6 | !.git* 7 | !.readthedocs.yaml 8 | !.pre-commit-config.yaml 9 | 10 | # Python 11 | /dist 12 | /build 13 | *.pyc 14 | *.pyo 15 | *.egg* 16 | *.so 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/psf/black 5 | rev: 24.3.0 6 | hooks: 7 | - id: black 8 | types: [] 9 | files: ^.*.pyi?$ 10 | exclude: ^doc/ 11 | - repo: https://github.com/pre-commit/pre-commit-hooks 12 | rev: v4.6.0 13 | hooks: 14 | - id: no-commit-to-branch 15 | - id: trailing-whitespace 16 | - id: end-of-file-fixer 17 | - id: check-yaml 18 | - id: check-added-large-files 19 | - id: check-ast 20 | - id: check-merge-conflict 21 | - id: check-json 22 | - id: detect-private-key 23 | exclude: ^.*/rsakey.pem$ 24 | - id: mixed-line-ending 25 | - id: pretty-format-json 26 | args: [--autofix] 27 | - repo: https://github.com/PyCQA/flake8 28 | rev: 7.0.0 29 | hooks: 30 | - id: flake8 31 | exclude: ^setup.py$ 32 | additional_dependencies: [flake8-docstrings, flake8-bugbear, flake8-logging-format, flake8-builtins, flake8-eradicate, flake8-fixme, pep8-naming, flake8-pep3101, flake8-annotations-complexity,flake8-pyi] 33 | - repo: https://github.com/PyCQA/isort 34 | rev: 5.13.2 35 | hooks: 36 | - id: isort 37 | - repo: https://github.com/pre-commit/mirrors-mypy 38 | rev: v1.9.0 39 | hooks: 40 | - id: mypy 41 | exclude: (setup.py|tests/.*.py|doc/.*) 42 | types: [] 43 | files: ^.*.pyi?$ 44 | additional_dependencies: [lxml-stubs,types-docutils] 45 | - repo: https://github.com/pre-commit/pygrep-hooks 46 | rev: v1.10.0 47 | hooks: 48 | - id: rst-backticks 49 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-20.04 5 | tools: 6 | python: '3.9' 7 | 8 | sphinx: 9 | configuration: doc/source/conf.py 10 | 11 | python: 12 | install: 13 | - method: pip 14 | path: . 15 | - requirements: doc/source/requirements.txt 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: python 4 | notifications: 5 | email: false 6 | matrix: 7 | include: 8 | - python: 3.5 9 | - python: 3.6 10 | - python: 3.7 11 | dist: xenial 12 | sudo: required 13 | - python: 3.8 14 | dist: xenial 15 | sudo: required 16 | - python: 3.9 17 | dist: xenial 18 | sudo: required 19 | - python: 3.11 20 | dist: xenial 21 | sudo: required 22 | env: 23 | global: 24 | - CFLAGS=-coverage 25 | - LDFLAGS=-coverage -lgcov 26 | - PYXMLSEC_TEST_ITERATIONS=50 27 | 28 | addons: 29 | apt: 30 | packages: 31 | - libssl-dev 32 | - libxmlsec1 33 | - libxmlsec1-dev 34 | - libxmlsec1-openssl 35 | - libxslt1-dev 36 | - pkg-config 37 | - lcov 38 | install: 39 | - travis_retry pip install --upgrade pip setuptools wheel 40 | - travis_retry pip install coverage -r requirements-test.txt --upgrade --force-reinstall 41 | - python setup.py bdist_wheel 42 | - pip install xmlsec --only-binary=xmlsec --no-index --find-links=dist/ 43 | script: coverage run -m pytest -v tests --color=yes 44 | after_success: 45 | - lcov --capture --no-external --directory . --output-file coverage.info 46 | - lcov --list coverage.info 47 | - bash <(curl -s https://codecov.io/bash) -f coverage.info 48 | before_deploy: 49 | - travis_retry pip install Sphinx -r doc/source/requirements.txt 50 | - git apply --verbose --no-index --unsafe-paths --directory=$(python -c "import site; print(site.getsitepackages()[0])") doc/source/sphinx-pr-6916.diff 51 | - sphinx-build -EWanb html doc/source build/sphinx 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ryan Leckey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src * 2 | recursive-include tests * 3 | prune */__pycache__ 4 | prune .github 5 | prune doc 6 | exclude .appveyor.yml 7 | exclude .editorconfig 8 | exclude .travis.yml 9 | exclude .gitattributes 10 | exclude .gitignore 11 | exclude requirements-test.txt 12 | exclude requirements.txt 13 | exclude xmlsec_extra.py 14 | exclude xmlsec_setupinfo.py 15 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | python-xmlsec 2 | ============= 3 | 4 | .. image:: https://img.shields.io/pypi/v/xmlsec.svg?logo=python&logoColor=white 5 | :target: https://pypi.python.org/pypi/xmlsec 6 | .. image:: https://results.pre-commit.ci/badge/github/xmlsec/python-xmlsec/master.svg 7 | :target: https://results.pre-commit.ci/latest/github/xmlsec/python-xmlsec/master 8 | :alt: pre-commit.ci status 9 | .. image:: https://img.shields.io/appveyor/ci/hoefling/xmlsec/master.svg?logo=appveyor&logoColor=white&label=AppVeyor 10 | :target: https://ci.appveyor.com/project/hoefling/xmlsec 11 | .. image:: https://github.com/mehcode/python-xmlsec/actions/workflows/manylinux.yml/badge.svg 12 | :target: https://github.com/mehcode/python-xmlsec/actions/workflows/manylinux.yml 13 | .. image:: https://github.com/mehcode/python-xmlsec/actions/workflows/macosx.yml/badge.svg 14 | :target: https://github.com/mehcode/python-xmlsec/actions/workflows/macosx.yml 15 | .. image:: https://github.com/mehcode/python-xmlsec/actions/workflows/linuxbrew.yml/badge.svg 16 | :target: https://github.com/mehcode/python-xmlsec/actions/workflows/linuxbrew.yml 17 | .. image:: https://github.com/mehcode/python-xmlsec/actions/workflows/opensuse-tumbleweed.yml/badge.svg 18 | :target: https://github.com/mehcode/python-xmlsec/actions/workflows/opensuse-tumbleweed.yml 19 | .. image:: https://codecov.io/gh/xmlsec/python-xmlsec/branch/master/graph/badge.svg 20 | :target: https://codecov.io/gh/xmlsec/python-xmlsec 21 | .. image:: https://img.shields.io/readthedocs/xmlsec/latest?logo=read-the-docs 22 | :target: https://xmlsec.readthedocs.io/en/latest/?badge=latest 23 | :alt: Documentation Status 24 | 25 | Python bindings for the `XML Security Library `_. 26 | 27 | Documentation 28 | ************* 29 | 30 | A documentation for ``xmlsec`` can be found at `xmlsec.readthedocs.io `_. 31 | 32 | Usage 33 | ***** 34 | 35 | Check the `examples `_ section in the documentation to see various examples of signing and verifying using the library. 36 | 37 | Requirements 38 | ************ 39 | - ``libxml2 >= 2.9.1`` 40 | - ``libxmlsec1 >= 1.2.33`` 41 | 42 | Install 43 | ******* 44 | 45 | ``xmlsec`` is available on PyPI: 46 | 47 | .. code-block:: bash 48 | 49 | pip install xmlsec 50 | 51 | Depending on your OS, you may need to install the required native 52 | libraries first: 53 | 54 | Linux (Debian) 55 | ^^^^^^^^^^^^^^ 56 | 57 | .. code-block:: bash 58 | 59 | apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl 60 | 61 | 62 | Note: There is no required version of LibXML2 for Ubuntu Precise, 63 | so you need to download and install it manually. 64 | 65 | .. code-block:: bash 66 | 67 | wget http://xmlsoft.org/sources/libxml2-2.9.1.tar.gz 68 | tar -xvf libxml2-2.9.1.tar.gz 69 | cd libxml2-2.9.1 70 | ./configure && make && make install 71 | 72 | 73 | Linux (CentOS) 74 | ^^^^^^^^^^^^^^ 75 | 76 | .. code-block:: bash 77 | 78 | yum install libxml2-devel xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel 79 | 80 | 81 | Linux (Fedora) 82 | ^^^^^^^^^^^^^^ 83 | 84 | .. code-block:: bash 85 | 86 | dnf install libxml2-devel xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel 87 | 88 | 89 | Mac 90 | ^^^ 91 | 92 | .. code-block:: bash 93 | 94 | brew install libxml2 libxmlsec1 pkg-config 95 | 96 | or 97 | 98 | .. code-block:: bash 99 | 100 | port install libxml2 xmlsec pkgconfig 101 | 102 | 103 | Alpine 104 | ^^^^^^ 105 | 106 | .. code-block:: bash 107 | 108 | apk add build-base libressl libffi-dev libressl-dev libxslt-dev libxml2-dev xmlsec-dev xmlsec 109 | 110 | 111 | Troubleshooting 112 | *************** 113 | 114 | Mac 115 | ^^^ 116 | 117 | If you get any fatal errors about missing ``.h`` files, update your 118 | ``C_INCLUDE_PATH`` environment variable to include the appropriate 119 | files from the ``libxml2`` and ``libxmlsec1`` libraries. 120 | 121 | 122 | Windows 123 | ^^^^^^^ 124 | 125 | Starting with 1.3.7, prebuilt wheels are available for Windows, 126 | so running ``pip install xmlsec`` should suffice. If you want 127 | to build from source: 128 | 129 | #. Configure build environment, see `wiki.python.org `_ for more details. 130 | 131 | #. Install from source dist: 132 | 133 | .. code-block:: bash 134 | 135 | pip install xmlsec --no-binary=xmlsec 136 | 137 | 138 | Building from source 139 | ******************** 140 | 141 | #. Clone the ``xmlsec`` source code repository to your local computer. 142 | 143 | .. code-block:: bash 144 | 145 | git clone https://github.com/xmlsec/python-xmlsec.git 146 | 147 | #. Change into the ``python-xmlsec`` root directory. 148 | 149 | .. code-block:: bash 150 | 151 | cd /path/to/xmlsec 152 | 153 | 154 | #. Install the project and all its dependencies using ``pip``. 155 | 156 | .. code-block:: bash 157 | 158 | pip install . 159 | 160 | 161 | Contributing 162 | ************ 163 | 164 | Setting up your environment 165 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 166 | 167 | #. Follow steps 1 and 2 of the `manual installation instructions <#building-from-source>`_. 168 | 169 | 170 | #. Initialize a virtual environment to develop in. 171 | This is done so as to ensure every contributor is working with 172 | close-to-identicial versions of packages. 173 | 174 | .. code-block:: bash 175 | 176 | mkvirtualenv xmlsec 177 | 178 | The ``mkvirtualenv`` command is available from ``virtualenvwrapper`` package which can be installed by following `link `_. 179 | 180 | #. Activate the created virtual environment: 181 | 182 | .. code-block:: bash 183 | 184 | workon xmlsec 185 | 186 | #. Install ``xmlsec`` in development mode with testing enabled. 187 | This will download all dependencies required for running the unit tests. 188 | 189 | .. code-block:: bash 190 | 191 | pip install -r requirements-test.txt 192 | pip install -e "." 193 | 194 | 195 | Running the test suite 196 | ^^^^^^^^^^^^^^^^^^^^^^ 197 | 198 | #. `Set up your environment <#setting-up-your-environment>`_. 199 | 200 | #. Run the unit tests. 201 | 202 | .. code-block:: bash 203 | 204 | pytest tests 205 | 206 | #. Tests configuration 207 | 208 | Env variable ``PYXMLSEC_TEST_ITERATIONS`` specifies number of 209 | test iterations to detect memory leaks. 210 | 211 | Reporting an issue 212 | ^^^^^^^^^^^^^^^^^^ 213 | 214 | Please attach the output of following information: 215 | 216 | * version of ``xmlsec`` 217 | * version of ``libxmlsec1`` 218 | * version of ``libxml2`` 219 | * output from the command 220 | 221 | .. code-block:: bash 222 | 223 | pkg-config --cflags xmlsec1 224 | 225 | License 226 | ******* 227 | 228 | Unless otherwise noted, all files contained within this project are licensed under the MIT opensource license. 229 | See the included ``LICENSE`` file or visit `opensource.org `_ for more information. 230 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = python-xmlsec 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/source/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | modules/xmlsec 8 | modules/constants 9 | modules/template 10 | modules/tree 11 | 12 | 13 | :ref:`contents` 14 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import importlib.metadata 4 | import urllib.request 5 | 6 | import lxml 7 | from docutils.nodes import Text, reference 8 | from packaging.version import Version, parse 9 | from sphinx.addnodes import pending_xref 10 | from sphinx.application import Sphinx 11 | from sphinx.environment import BuildEnvironment 12 | from sphinx.errors import ExtensionError 13 | 14 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx'] 15 | 16 | intersphinx_mapping = {'python': ('https://docs.python.org/3/', None)} 17 | 18 | templates_path = ['_templates'] 19 | source_suffix = '.rst' 20 | master_doc = 'index' 21 | 22 | project = u'python-xmlsec' 23 | copyright = u'2020, Oleg Hoefling ' # noqa: A001 24 | author = u'Bulat Gaifullin ' 25 | release = importlib.metadata.version('xmlsec') 26 | parsed: Version = parse(release) 27 | version = '{}.{}'.format(parsed.major, parsed.minor) 28 | 29 | exclude_patterns: list[str] = [] 30 | pygments_style = 'sphinx' 31 | todo_include_todos = False 32 | 33 | html_theme = 'furo' 34 | html_static_path: list[str] = [] 35 | htmlhelp_basename = 'python-xmlsecdoc' 36 | 37 | latex_elements: dict[str, str] = {} 38 | latex_documents = [ 39 | ( 40 | master_doc, 41 | 'python-xmlsec.tex', 42 | u'python-xmlsec Documentation', 43 | u'Bulat Gaifullin \\textless{}gaifullinbf@gmail.com\\textgreater{}', 44 | 'manual', 45 | ) 46 | ] 47 | 48 | man_pages = [(master_doc, 'python-xmlsec', u'python-xmlsec Documentation', [author], 1)] 49 | 50 | texinfo_documents = [ 51 | ( 52 | master_doc, 53 | 'python-xmlsec', 54 | u'python-xmlsec Documentation', 55 | author, 56 | 'python-xmlsec', 57 | 'One line description of project.', 58 | 'Miscellaneous', 59 | ) 60 | ] 61 | 62 | autodoc_member_order = 'groupwise' 63 | autodoc_docstring_signature = True 64 | 65 | 66 | rst_prolog = ''' 67 | .. role:: xml(code) 68 | :language: xml 69 | ''' 70 | 71 | # LXML crossref'ing stuff: 72 | # LXML doesn't have an intersphinx docs, 73 | # so we link to lxml.etree._Element explicitly 74 | lxml_element_cls_doc_uri = 'https://lxml.de/api/lxml.etree._Element-class.html' 75 | 76 | 77 | def lxml_element_doc_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref, contnode: Text) -> reference: 78 | """ 79 | Handle a missing reference only if it is a ``lxml.etree._Element`` ref. 80 | 81 | We handle only :class:`lxml.etree._Element` and :class:`~lxml.etree._Element` nodes. 82 | """ 83 | if ( 84 | node.get('reftype', None) == 'class' 85 | and node.get('reftarget', None) == 'lxml.etree._Element' 86 | and contnode.astext() in ('lxml.etree._Element', '_Element') 87 | ): 88 | reftitle = '(in lxml v{})'.format(lxml.__version__) # type: ignore[attr-defined] 89 | newnode = reference('', '', internal=False, refuri=lxml_element_cls_doc_uri, reftitle=reftitle) 90 | newnode.append(contnode) 91 | return newnode 92 | 93 | 94 | def setup(app: Sphinx) -> None: 95 | # first, check whether the doc URL is still valid 96 | if urllib.request.urlopen(lxml_element_cls_doc_uri).getcode() != 200: 97 | raise ExtensionError('URL to `lxml.etree._Element` docs is not accesible.') 98 | app.connect('missing-reference', lxml_element_doc_reference) 99 | -------------------------------------------------------------------------------- /doc/source/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | Decrypt 5 | ------- 6 | 7 | .. literalinclude:: examples/decrypt.py 8 | 9 | Encrypt 10 | ------- 11 | 12 | .. literalinclude:: examples/encrypt.py 13 | 14 | 15 | Sign 16 | ---- 17 | 18 | .. literalinclude:: examples/sign.py 19 | 20 | Sign-Binary 21 | ----------- 22 | 23 | .. literalinclude:: examples/sign_binary.py 24 | 25 | 26 | Verify 27 | ------ 28 | 29 | .. literalinclude:: examples/verify.py 30 | 31 | 32 | Verify-Binary 33 | ------------- 34 | 35 | .. literalinclude:: examples/verify_binary.py 36 | -------------------------------------------------------------------------------- /doc/source/examples/decrypt.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | 3 | import xmlsec 4 | 5 | manager = xmlsec.KeysManager() 6 | key = xmlsec.Key.from_file('rsakey.pem', xmlsec.constants.KeyDataFormatPem) 7 | manager.add_key(key) 8 | enc_ctx = xmlsec.EncryptionContext(manager) 9 | root = etree.parse("enc1-res.xml").getroot() 10 | enc_data = xmlsec.tree.find_child(root, "EncryptedData", xmlsec.constants.EncNs) 11 | decrypted = enc_ctx.decrypt(enc_data) 12 | print(etree.tostring(decrypted)) 13 | -------------------------------------------------------------------------------- /doc/source/examples/enc1-doc.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Hello, World! 7 | 8 | -------------------------------------------------------------------------------- /doc/source/examples/enc1-res.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | UrTgE0UxQa8xevs4SyRA0rsibEz/ZFDjCBD+t4pKSdajB/cefYObZzqq2l41Q6R/ 10 | tqYLht5hEBh26AHfjmQSJAL+eChXOt/EaOf63zzJedO90HGqIQyzOeOPURAl3Li8 11 | ivPyLVyocJDeVNeh7W+7kYwpFQ6PLuQxWsFFQXVoRAWbXHpZkSzVheR+5RpYJRTb 12 | 1UYXKxu8jg4NqbjucVMDIxUOzsVCDRyk8R8sQrM7D/H/N0y7DAY8oX/WZ45xLwUy 13 | DY/U86tTpTn95NwHD10SLyrL6rpXdbEuoIQHhWLwV9uQxnJA/Pn1KZ+xXK/fePfP 14 | 26PBo/hUrN5pm5U8ycc4iw== 15 | 16 | 17 | 18 | 19 | 2pb5Mxd0f+AW56Cs3MfQ9HJkUVeliSi1hVCNCVHTKeMyC2VL6lPhQ9+L01aSeTSY 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /doc/source/examples/encrypt.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | 3 | import xmlsec 4 | 5 | with open('enc1-doc.xml') as fp: 6 | template = etree.parse(fp).getroot() 7 | 8 | enc_data = xmlsec.template.encrypted_data_create( 9 | template, 10 | xmlsec.constants.TransformAes128Cbc, 11 | type=xmlsec.constants.TypeEncElement, 12 | ns="xenc", 13 | ) 14 | 15 | xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) 16 | key_info = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") 17 | enc_key = xmlsec.template.add_encrypted_key(key_info, xmlsec.constants.TransformRsaOaep) 18 | xmlsec.template.encrypted_data_ensure_cipher_value(enc_key) 19 | data = template.find('./Data') 20 | 21 | # Encryption 22 | manager = xmlsec.KeysManager() 23 | key = xmlsec.Key.from_file('rsacert.pem', xmlsec.constants.KeyDataFormatCertPem, None) 24 | manager.add_key(key) 25 | 26 | enc_ctx = xmlsec.EncryptionContext(manager) 27 | enc_ctx.key = xmlsec.Key.generate( 28 | xmlsec.constants.KeyDataAes, 128, xmlsec.constants.KeyDataTypeSession 29 | ) 30 | enc_data = enc_ctx.encrypt_xml(enc_data, data) 31 | enc_method = xmlsec.tree.find_child( 32 | enc_data, xmlsec.constants.NodeEncryptionMethod, xmlsec.constants.EncNs 33 | ) 34 | key_info = xmlsec.tree.find_child( 35 | enc_data, xmlsec.constants.NodeKeyInfo, xmlsec.constants.DSigNs 36 | ) 37 | enc_method = xmlsec.tree.find_node( 38 | key_info, xmlsec.constants.NodeEncryptionMethod, xmlsec.constants.EncNs 39 | ) 40 | cipher_value = xmlsec.tree.find_node( 41 | key_info, xmlsec.constants.NodeCipherValue, xmlsec.constants.EncNs 42 | ) 43 | print(etree.tostring(cipher_value)) 44 | -------------------------------------------------------------------------------- /doc/source/examples/rsacert.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: md5WithRSAEncryption 6 | Issuer: C=US, ST=California, L=Sunnyvale, O=XML Security Library (http://www.aleksey.com/xmlsec), OU=Root Certificate, CN=Aleksey Sanin/emailAddress=xmlsec@aleksey.com 7 | Validity 8 | Not Before: Mar 31 04:02:22 2003 GMT 9 | Not After : Mar 28 04:02:22 2013 GMT 10 | Subject: C=US, ST=California, O=XML Security Library (http://www.aleksey.com/xmlsec), OU=Examples RSA Certificate, CN=Aleksey Sanin/emailAddress=xmlsec@aleksey.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (2048 bit) 14 | Modulus (2048 bit): 15 | 00:97:b8:fe:b4:3f:83:35:78:16:89:04:ec:2b:61: 16 | 8c:bf:c4:5f:00:81:4a:45:e6:d9:cd:e9:e2:3c:97: 17 | 3b:45:ad:aa:e6:8d:0b:77:71:07:01:4f:7c:f9:7d: 18 | e2:19:aa:dd:91:59:f4:f1:cf:3d:ba:78:46:96:11: 19 | 9c:b6:5b:46:39:73:55:23:aa:f7:9e:00:5c:e5:e9: 20 | 49:ec:3b:9c:3f:84:99:3a:90:ad:df:7e:64:86:c6: 21 | 26:72:ce:31:08:79:7e:13:15:b8:e5:bf:d6:56:02: 22 | 8d:60:21:4c:27:18:64:fb:fb:55:70:f6:33:bd:2f: 23 | 55:70:d5:5e:7e:99:ae:a4:e0:aa:45:47:13:a8:30: 24 | d5:a0:8a:9d:cc:20:ec:e4:8e:51:c9:54:c5:7f:3e: 25 | 66:2d:74:bf:a3:7a:f8:f3:ec:94:57:39:b4:ac:00: 26 | 75:62:61:54:b4:d0:e0:52:86:f8:5e:77:ec:50:43: 27 | 9c:d2:ba:a7:8c:62:5a:bc:b2:fe:f3:cc:62:7e:23: 28 | 60:6b:c7:51:49:37:78:7e:25:15:30:ab:fa:b4:ae: 29 | 25:8f:22:fc:a3:48:7f:f2:0a:8a:6e:e0:fe:8d:f0: 30 | 01:ed:c6:33:cc:6b:a1:fd:a6:80:ef:06:8c:af:f6: 31 | 40:3a:8e:42:14:20:61:12:1f:e3:fc:05:b1:05:d5: 32 | 65:c3 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Basic Constraints: 36 | CA:FALSE 37 | Netscape Comment: 38 | OpenSSL Generated Certificate 39 | X509v3 Subject Key Identifier: 40 | 24:84:2C:F2:D4:59:20:62:8B:2E:5C:86:90:A3:AA:30:BA:27:1A:9C 41 | X509v3 Authority Key Identifier: 42 | keyid:B4:B9:EF:9A:E6:97:0E:68:65:1E:98:CE:FA:55:0D:89:06:DB:4C:7C 43 | DirName:/C=US/ST=California/L=Sunnyvale/O=XML Security Library (http://www.aleksey.com/xmlsec)/OU=Root Certificate/CN=Aleksey Sanin/emailAddress=xmlsec@aleksey.com 44 | serial:00 45 | 46 | Signature Algorithm: md5WithRSAEncryption 47 | b5:3f:9b:32:31:4a:ff:2f:84:3b:a8:9b:11:5c:a6:5c:f0:76: 48 | 52:d9:6e:f4:90:ad:fa:0d:90:c1:98:d5:4a:12:dd:82:6b:37: 49 | e8:d9:2d:62:92:c9:61:37:98:86:8f:a4:49:6a:5e:25:d0:18: 50 | 69:30:0f:98:8f:43:58:89:31:b2:3b:05:e2:ef:c7:a6:71:5f: 51 | f7:fe:73:c5:a7:b2:cd:2e:73:53:71:7d:a8:4c:68:1a:32:1b: 52 | 5e:48:2f:8f:9b:7a:a3:b5:f3:67:e8:b1:a2:89:4e:b2:4d:1b: 53 | 79:9c:ff:f0:0d:19:4f:4e:b1:03:3d:99:f0:44:b7:8a:0b:34: 54 | 9d:83 55 | -----BEGIN CERTIFICATE----- 56 | MIIE3zCCBEigAwIBAgIBBTANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCVVMx 57 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 58 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 59 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 60 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMB4X 61 | DTAzMDMzMTA0MDIyMloXDTEzMDMyODA0MDIyMlowgb8xCzAJBgNVBAYTAlVTMRMw 62 | EQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFy 63 | eSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMSEwHwYDVQQLExhFeGFt 64 | cGxlcyBSU0EgQ2VydGlmaWNhdGUxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf 65 | BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCCASIwDQYJKoZIhvcNAQEB 66 | BQADggEPADCCAQoCggEBAJe4/rQ/gzV4FokE7CthjL/EXwCBSkXm2c3p4jyXO0Wt 67 | quaNC3dxBwFPfPl94hmq3ZFZ9PHPPbp4RpYRnLZbRjlzVSOq954AXOXpSew7nD+E 68 | mTqQrd9+ZIbGJnLOMQh5fhMVuOW/1lYCjWAhTCcYZPv7VXD2M70vVXDVXn6ZrqTg 69 | qkVHE6gw1aCKncwg7OSOUclUxX8+Zi10v6N6+PPslFc5tKwAdWJhVLTQ4FKG+F53 70 | 7FBDnNK6p4xiWryy/vPMYn4jYGvHUUk3eH4lFTCr+rSuJY8i/KNIf/IKim7g/o3w 71 | Ae3GM8xrof2mgO8GjK/2QDqOQhQgYRIf4/wFsQXVZcMCAwEAAaOCAVcwggFTMAkG 72 | A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp 73 | ZmljYXRlMB0GA1UdDgQWBBQkhCzy1FkgYosuXIaQo6owuicanDCB+AYDVR0jBIHw 74 | MIHtgBS0ue+a5pcOaGUemM76VQ2JBttMfKGB0aSBzjCByzELMAkGA1UEBhMCVVMx 75 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 76 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 77 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 78 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggEA 79 | MA0GCSqGSIb3DQEBBAUAA4GBALU/mzIxSv8vhDuomxFcplzwdlLZbvSQrfoNkMGY 80 | 1UoS3YJrN+jZLWKSyWE3mIaPpElqXiXQGGkwD5iPQ1iJMbI7BeLvx6ZxX/f+c8Wn 81 | ss0uc1NxfahMaBoyG15IL4+beqO182fosaKJTrJNG3mc//ANGU9OsQM9mfBEt4oL 82 | NJ2D 83 | -----END CERTIFICATE----- 84 | -------------------------------------------------------------------------------- /doc/source/examples/rsakey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAl7j+tD+DNXgWiQTsK2GMv8RfAIFKRebZzeniPJc7Ra2q5o0L 3 | d3EHAU98+X3iGardkVn08c89unhGlhGctltGOXNVI6r3ngBc5elJ7DucP4SZOpCt 4 | 335khsYmcs4xCHl+ExW45b/WVgKNYCFMJxhk+/tVcPYzvS9VcNVefpmupOCqRUcT 5 | qDDVoIqdzCDs5I5RyVTFfz5mLXS/o3r48+yUVzm0rAB1YmFUtNDgUob4XnfsUEOc 6 | 0rqnjGJavLL+88xifiNga8dRSTd4fiUVMKv6tK4ljyL8o0h/8gqKbuD+jfAB7cYz 7 | zGuh/aaA7waMr/ZAOo5CFCBhEh/j/AWxBdVlwwIDAQABAoIBAQCAvt6DnZF9gdW9 8 | l4vAlBqXb88d4phgELCp5tmviLUnP2NSGEWuqR7Eoeru2z9NgIxblvYfazh6Ty22 9 | kmNk6rcAcTnB9oYAcVZjUj8EUuEXlTFhXPvuNpafNu3RZd59znqJP1mSu+LpQWku 10 | NZMlabHnkTLDlGf7FXtvL9/rlgV4qk3QcDVF793JFszWrtK3mnld3KHQ6cuo9iSm 11 | 0rQKtkDjeHsRell8qTQvfBsgG1q2bv8QWT45/eQrra9mMbGTr3DbnXvoeJmTj1VN 12 | XJV7tBNllxxPahlYMByJaf/Tuva5j6HWUEIfYky5ihr2z1P/fNQ2OSCM6SQHpkiG 13 | EXQDueXBAoGBAMfW7KcmToEQEcTiqfey6C1LOLoemcX0/ROUktPq/5JQJRRrT4t7 14 | XevLX0ed8sLyR5T29XQtdnuV0DJfvcJD+6ZwfOcQ+f6ZzCaNXJP97JtEt5kSWY01 15 | Ei+nphZ0RFvPb04V3qDU9dElU26GR36CRBYJyM2WQPx4v+/YyDSZH9kLAoGBAMJc 16 | ZBU8pRbIia/FFOHUlS3v5P18nVmXyOd0fvRq0ZelaQCebTZ4K9wjnCfw//yzkb2Z 17 | 0vZFNB+xVBKB0Pt6nVvnSNzxdQ8EAXVFwHtXa25FUyP2RERQgTvmajqmgWjZsDYp 18 | 6GHcK3ZhmdmscQHF/Q2Uo4scvBcheahm9IXiNskpAoGAXelEgTBhSAmTMCEMmti6 19 | fz6QQ/bJcNu2apMxhOE0hT+gjT34vaWV9481EWTKho5w0TJVGumaem1mz6VqeXaV 20 | Nhw6tiOmN91ysNNRpEJ6BGWAmjCjYNaF21s/k+HDlhmfRuTEIHSzqDuQP6pewrbY 21 | 5Dpo4SQxGfRsznvjacRj0Q0CgYBN247oBvQnDUxCkhNMZ8kersOvW5T4x9neBge5 22 | R3UQZ12Jtu0O7dK8C7PJODyDcTeHmTAuIQjBTVrdUw1xP+v7XcoNX9hBnJws6zUw 23 | 85MAiFrGxCcSqqEqaqHRPtQGOXXiLKV/ViA++tgTn4VhbXtyTkG5P1iFd45xjFSV 24 | sUm7CQKBgDn92tHxzePly1L1mK584TkVryx4cP9RFHpebnmNduGwwjnRuYipoj8y 25 | pPPAkVbbaA3f9OB2go48rN0Ft9nHdlqgh9BpIKCVtkIb1XN0K3Oa/8BW8W/GAiNG 26 | HJcsrOtIrGVRdlyJG6bDaN8T49DnhOcsqMbf+IkIvfh50VeE9L/e 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /doc/source/examples/rsapub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl7j+tD+DNXgWiQTsK2GM 3 | v8RfAIFKRebZzeniPJc7Ra2q5o0Ld3EHAU98+X3iGardkVn08c89unhGlhGctltG 4 | OXNVI6r3ngBc5elJ7DucP4SZOpCt335khsYmcs4xCHl+ExW45b/WVgKNYCFMJxhk 5 | +/tVcPYzvS9VcNVefpmupOCqRUcTqDDVoIqdzCDs5I5RyVTFfz5mLXS/o3r48+yU 6 | Vzm0rAB1YmFUtNDgUob4XnfsUEOc0rqnjGJavLL+88xifiNga8dRSTd4fiUVMKv6 7 | tK4ljyL8o0h/8gqKbuD+jfAB7cYzzGuh/aaA7waMr/ZAOo5CFCBhEh/j/AWxBdVl 8 | wwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /doc/source/examples/sign.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | 3 | import xmlsec 4 | 5 | with open('sign1-tmpl.xml') as fp: 6 | template = etree.parse(fp).getroot() 7 | 8 | signature_node = xmlsec.tree.find_node(template, xmlsec.constants.NodeSignature) 9 | ctx = xmlsec.SignatureContext() 10 | key = xmlsec.Key.from_file('rsakey.pem', xmlsec.constants.KeyDataFormatPem) 11 | ctx.key = key 12 | ctx.sign(signature_node) 13 | print(etree.tostring(template)) 14 | -------------------------------------------------------------------------------- /doc/source/examples/sign1-res.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 9H/rQr2Axe9hYTV2n/tCp+3UIQQ= 19 | 20 | 21 | Mx4psIy9/UY+u8QBJRDrwQWKRaCGz0WOVftyDzAe6WHAFSjMNr7qb2ojq9kdipT8 22 | Oub5q2OQ7mzdSLiiejkrO1VeqM/90yEIGI4En6KEB6ArEzw+iq4N1wm6EptcyxXx 23 | M9StAOOa9ilWYqR9Tfx3SW1urUIuKYgUitxsONiUHBVaW6HeX51bsXoTF++4ZI+D 24 | jiPBjN4HHmr0cbJ6BXk91S27ffZIfp1Qj5nL9onFLUGbR6EFgu2luiRzQbPuM2tP 25 | XxyI7GZ8AfHnRJK28ARvBC9oi+O1ej20S79CIV7gdBxbLbFprozBHAwOEC57YgJc 26 | x+YEjSjcO7SBIR1FiUA7pw== 27 | 28 | rsakey.pem 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /doc/source/examples/sign1-tmpl.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /doc/source/examples/sign_binary.py: -------------------------------------------------------------------------------- 1 | import xmlsec 2 | 3 | ctx = xmlsec.SignatureContext() 4 | key = xmlsec.Key.from_file('rsakey.pem', xmlsec.constants.KeyDataFormatPem) 5 | ctx.key = key 6 | data = b'\xa8f4dP\x82\x02\xd3\xf5.\x02\xc1\x03\xef\xc4\x86\xabC\xec\xb7>\x8e\x1f\xa3\xa3\xc5\xb9qc\xc2\x81\xb1-\xa4B\xdf\x03>\xba\xd1' 7 | sign = ctx.sign_binary(data, xmlsec.constants.TransformRsaSha1) 8 | print(sign) 9 | -------------------------------------------------------------------------------- /doc/source/examples/verify.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | 3 | import xmlsec 4 | 5 | with open('sign1-res.xml') as fp: 6 | template = etree.parse(fp).getroot() 7 | 8 | xmlsec.tree.add_ids(template, ["ID"]) 9 | signature_node = xmlsec.tree.find_node(template, xmlsec.constants.NodeSignature) 10 | # Create a digital signature context (no key manager is needed). 11 | ctx = xmlsec.SignatureContext() 12 | key = xmlsec.Key.from_file('rsapub.pem', xmlsec.constants.KeyDataFormatPem) 13 | # Set the key on the context. 14 | ctx.key = key 15 | ctx.verify(signature_node) 16 | -------------------------------------------------------------------------------- /doc/source/examples/verify_binary.py: -------------------------------------------------------------------------------- 1 | import xmlsec 2 | 3 | ctx = xmlsec.SignatureContext() 4 | key = xmlsec.Key.from_file('rsakey.pem', xmlsec.constants.KeyDataFormatPem) 5 | ctx.key = key 6 | 7 | data = b'\xa8f4dP\x82\x02\xd3\xf5.\x02\xc1\x03\xef\xc4\x86\xabC\xec\xb7>\x8e\x1f\xa3\xa3\xc5\xb9qc\xc2\x81\xb1-\xa4B\xdf\x03>\xba\xd1' 8 | sign = b"h\xcb\xb1\x82\xfa`e\x89x\xe5\xc5ir\xd6\xd1Q\x9a\x0b\xeaU_G\xcc'\xa4c\xa3>\x9b27\xbf^`\xa7p\xfb\x98\xcb\x81\xd2\xb1\x0c'\x9d\xe2\n\xec\xb2<\xcf@\x98=\xe0}O8}fy\xc2\xc4\xe9\xec\x87\xf6\xc1\xde\xfd\x96*o\xab\xae\x12\xc9{\xcc\x0e\x93y\x9a\x16\x80o\x92\xeb\x02^h|\xa0\x9b<\x99_\x97\xcb\xe27\xe9u\xc3\xfa_\xcct/sTb\xa0\t\xd3\x93'\xb4\xa4\x0ez\xcbL\x14D\xdb\xe3\x84\x886\xe9J[\xe7\xce\xc0\xb1\x99\x07\x17{\xc6:\xff\x1dt\xfd\xab^2\xf7\x9e\xa4\xccT\x8e~b\xdb\x9a\x04\x04\xbaM\xfa\xbd\xec)z\xbb\x89\xd7\xb2Q\xac\xaf\x13\xdcD\xcd\n6\x92\xfao\xb9\xd9\x96$\xce\xa6\xcf\xf8\xe4Bb60\xf5\xd2a\xb1o\x8c\x0f\x8bl\x88vh\xb5h\xfa\xfa\xb66\xedQ\x10\xc4\xef\xfa\x81\xf0\xc9.^\x98\x1ePQS\x9e\xafAy\x90\xe4\x95\x03V\xc2\xa0\x18\xa5d\xc2\x15*\xb6\xd7$\xc0\t2\xa1" 9 | ctx.verify_binary(data, xmlsec.constants.TransformRsaSha1, sign) 10 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. python-xmlsec documentation master file, created by 2 | sphinx-quickstart on Fri Mar 17 10:30:14 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root ``toctree`` directive. 5 | 6 | Welcome to python-xmlsec's documentation! 7 | ========================================= 8 | 9 | Python bindings for the XML Security Library. 10 | 11 | .. _contents: 12 | 13 | Table of contents 14 | ================= 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | 19 | install 20 | api 21 | examples 22 | 23 | Indices and tables 24 | ================== 25 | 26 | * :ref:`genindex` 27 | * :ref:`modindex` 28 | * :ref:`search` 29 | -------------------------------------------------------------------------------- /doc/source/install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | ``xmlsec`` is available on PyPI: 5 | 6 | .. code-block:: bash 7 | 8 | pip install xmlsec 9 | 10 | Depending on your OS, you may need to install the required native 11 | libraries first: 12 | 13 | Linux (Debian) 14 | -------------- 15 | 16 | .. code-block:: bash 17 | 18 | apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl 19 | 20 | .. note:: There is no required version of LibXML2 for Ubuntu Precise, 21 | so you need to download and install it manually: 22 | 23 | .. code-block:: bash 24 | 25 | wget http://xmlsoft.org/sources/libxml2-2.9.1.tar.gz 26 | tar -xvf libxml2-2.9.1.tar.gz 27 | cd libxml2-2.9.1 28 | ./configure && make && make install 29 | 30 | 31 | Linux (CentOS) 32 | -------------- 33 | 34 | .. code-block:: bash 35 | 36 | yum install libxml2-devel xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel 37 | 38 | 39 | Linux (Fedora) 40 | -------------- 41 | 42 | .. code-block:: bash 43 | 44 | dnf install libxml2-devel xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel 45 | 46 | 47 | Mac 48 | --- 49 | 50 | .. code-block:: bash 51 | 52 | xcode-select --install 53 | brew upgrade 54 | brew install libxml2 libxmlsec1 pkg-config 55 | 56 | 57 | Alpine 58 | ------ 59 | 60 | .. code-block:: bash 61 | 62 | apk add build-base libressl libffi-dev libressl-dev libxslt-dev libxml2-dev xmlsec-dev xmlsec 63 | 64 | 65 | Troubleshooting 66 | *************** 67 | 68 | Mac 69 | --- 70 | 71 | If you get any fatal errors about missing ``.h`` files, update your 72 | ``C_INCLUDE_PATH`` environment variable to include the appropriate 73 | files from the ``libxml2`` and ``libxmlsec1`` libraries. 74 | 75 | 76 | Windows 77 | ------- 78 | 79 | Starting with 1.3.7, prebuilt wheels are available for Windows, 80 | so running ``pip install xmlsec`` should suffice. If you want 81 | to build from source: 82 | 83 | #. Configure build environment, see `wiki.python.org `_ for more details. 84 | 85 | #. Install from source dist: 86 | 87 | .. code-block:: bash 88 | 89 | pip install xmlsec --no-binary=xmlsec 90 | -------------------------------------------------------------------------------- /doc/source/modules/constants.rst: -------------------------------------------------------------------------------- 1 | ``xmlsec.constants`` 2 | -------------------- 3 | 4 | Various constants used by the library. 5 | 6 | EncryptionType 7 | ************** 8 | 9 | .. data:: xmlsec.constants.TypeEncContent 10 | :annotation: = 'http://www.w3.org/2001/04/xmlenc#Content' 11 | 12 | .. data:: xmlsec.constants.TypeEncElement 13 | :annotation: = 'http://www.w3.org/2001/04/xmlenc#Element' 14 | 15 | KeyData 16 | ******* 17 | 18 | .. class:: __KeyData 19 | 20 | Base type for all :samp:`KeyData{XXX}` constants. 21 | 22 | .. data:: xmlsec.constants.KeyDataName 23 | 24 | The :xml:`` processing class. 25 | 26 | .. data:: xmlsec.constants.KeyDataValue 27 | 28 | The :xml:`` processing class. 29 | 30 | .. data:: xmlsec.constants.KeyDataRetrievalMethod 31 | 32 | The :xml:`` processing class. 33 | 34 | .. data:: xmlsec.constants.KeyDataEncryptedKey 35 | 36 | The :xml:`` processing class. 37 | 38 | .. data:: xmlsec.constants.KeyDataAes 39 | 40 | The AES key klass. 41 | 42 | .. data:: xmlsec.constants.KeyDataDes 43 | 44 | The DES key klass. 45 | 46 | .. data:: xmlsec.constants.KeyDataDsa 47 | 48 | The DSA key klass. 49 | 50 | .. data:: xmlsec.constants.KeyDataEcdsa 51 | 52 | (Deprecated. The EC key klass) The ECDSA key klass. 53 | 54 | .. data:: xmlsec.constants.KeyDataEc 55 | 56 | The EC key klass. 57 | 58 | .. data:: xmlsec.constants.KeyDataHmac 59 | 60 | The DHMAC key klass. 61 | 62 | .. data:: xmlsec.constants.KeyDataRsa 63 | 64 | The RSA key klass. 65 | 66 | .. data:: xmlsec.constants.KeyDataX509 67 | 68 | The X509 data klass. 69 | 70 | .. data:: xmlsec.constants.KeyDataRawX509Cert 71 | 72 | The raw X509 certificate klass. 73 | 74 | KeyDataFormat 75 | ************* 76 | 77 | .. data:: xmlsec.constants.KeyDataFormatUnknown 78 | 79 | the key data format is unknown. 80 | 81 | .. data:: xmlsec.constants.KeyDataFormatBinary 82 | 83 | the binary key data. 84 | 85 | .. data:: xmlsec.constants.KeyDataFormatPem 86 | 87 | the PEM key data (cert or public/private key). 88 | 89 | .. data:: xmlsec.constants.KeyDataFormatDer 90 | 91 | the DER key data (cert or public/private key). 92 | 93 | .. data:: xmlsec.constants.KeyDataFormatPkcs8Pem 94 | 95 | the PKCS8 PEM private key. 96 | 97 | .. data:: xmlsec.constants.KeyDataFormatPkcs8Der 98 | 99 | the PKCS8 DER private key. 100 | 101 | .. data:: xmlsec.constants.KeyDataFormatPkcs12 102 | 103 | the PKCS12 format (bag of keys and certs) 104 | 105 | .. data:: xmlsec.constants.KeyDataFormatCertPem 106 | 107 | the PEM cert. 108 | 109 | .. data:: xmlsec.constants.KeyDataFormatCertDer 110 | 111 | the DER cert. 112 | 113 | KeyDataType 114 | *********** 115 | 116 | .. data:: xmlsec.constants.KeyDataTypeUnknown 117 | 118 | The key data type is unknown 119 | 120 | .. data:: xmlsec.constants.KeyDataTypeNone 121 | 122 | The key data type is unknown 123 | 124 | .. data:: xmlsec.constants.KeyDataTypePublic 125 | 126 | The key data contain a public key. 127 | 128 | .. data:: xmlsec.constants.KeyDataTypePrivate 129 | 130 | The key data contain a private key. 131 | 132 | .. data:: xmlsec.constants.KeyDataTypeSymmetric 133 | 134 | The key data contain a symmetric key. 135 | 136 | .. data:: xmlsec.constants.KeyDataTypeSession 137 | 138 | The key data contain session key (one time key, not stored in keys manager). 139 | 140 | .. data:: xmlsec.constants.KeyDataTypePermanent 141 | 142 | The key data contain permanent key (stored in keys manager). 143 | 144 | .. data:: xmlsec.constants.KeyDataTypeTrusted 145 | 146 | The key data is trusted. 147 | 148 | .. data:: xmlsec.constants.KeyDataTypeAny 149 | 150 | The key data is trusted. 151 | 152 | Namespaces 153 | ********** 154 | 155 | .. data:: xmlsec.constants.Ns 156 | :annotation: = 'http://www.aleksey.com/xmlsec/2002' 157 | 158 | .. data:: xmlsec.constants.DSigNs 159 | :annotation: = 'http://www.w3.org/2000/09/xmldsig#' 160 | 161 | .. data:: xmlsec.constants.EncNs 162 | :annotation: = 'http://www.w3.org/2001/04/xmlenc#' 163 | 164 | .. data:: xmlsec.constants.XPathNs 165 | :annotation: = 'http://www.w3.org/TR/1999/REC-xpath-19991116' 166 | 167 | .. data:: xmlsec.constants.XPath2Ns 168 | :annotation: = 'http://www.w3.org/2002/06/xmldsig-filter2' 169 | 170 | .. data:: xmlsec.constants.XPointerNs 171 | :annotation: = 'http://www.w3.org/2001/04/xmldsig-more/xptr' 172 | 173 | .. data:: xmlsec.constants.NsExcC14N 174 | :annotation: = 'http://www.w3.org/2001/10/xml-exc-c14n#' 175 | 176 | .. data:: xmlsec.constants.NsExcC14NWithComments 177 | :annotation: = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments' 178 | 179 | Nodes 180 | ***** 181 | 182 | .. data:: xmlsec.constants.NodeSignature 183 | :annotation: = 'Signature' 184 | 185 | .. data:: xmlsec.constants.NodeSignedInfo 186 | :annotation: = 'SignedInfo' 187 | 188 | .. data:: xmlsec.constants.NodeCanonicalizationMethod 189 | :annotation: = 'CanonicalizationMethod' 190 | 191 | .. data:: xmlsec.constants.NodeSignatureMethod 192 | :annotation: = 'SignatureMethod' 193 | 194 | .. data:: xmlsec.constants.NodeSignatureValue 195 | :annotation: = 'SignatureValue' 196 | 197 | .. data:: xmlsec.constants.NodeSignatureProperties 198 | :annotation: = 'SignatureProperties' 199 | 200 | .. data:: xmlsec.constants.NodeDigestMethod 201 | :annotation: = 'DigestMethod' 202 | 203 | .. data:: xmlsec.constants.NodeDigestValue 204 | :annotation: = 'DigestValue' 205 | 206 | .. data:: xmlsec.constants.NodeObject 207 | :annotation: = 'Object' 208 | 209 | .. data:: xmlsec.constants.NodeManifest 210 | :annotation: = 'Manifest' 211 | 212 | .. data:: xmlsec.constants.NodeEncryptedData 213 | :annotation: = 'EncryptedData' 214 | 215 | .. data:: xmlsec.constants.NodeEncryptedKey 216 | :annotation: = 'EncryptedKey' 217 | 218 | .. data:: xmlsec.constants.NodeEncryptionMethod 219 | :annotation: = 'EncryptionMethod' 220 | 221 | .. data:: xmlsec.constants.NodeEncryptionProperties 222 | :annotation: = 'EncryptionProperties' 223 | 224 | .. data:: xmlsec.constants.NodeEncryptionProperty 225 | :annotation: = 'EncryptionProperty' 226 | 227 | .. data:: xmlsec.constants.NodeCipherData 228 | :annotation: = 'CipherData' 229 | 230 | .. data:: xmlsec.constants.NodeCipherValue 231 | :annotation: = 'CipherValue' 232 | 233 | .. data:: xmlsec.constants.NodeCipherReference 234 | :annotation: = 'CipherReference' 235 | 236 | .. data:: xmlsec.constants.NodeReference 237 | :annotation: = 'Reference' 238 | 239 | .. data:: xmlsec.constants.NodeReferenceList 240 | :annotation: = 'ReferenceList' 241 | 242 | .. data:: xmlsec.constants.NodeDataReference 243 | :annotation: = 'DataReference' 244 | 245 | .. data:: xmlsec.constants.NodeKeyReference 246 | :annotation: = 'KeyReference' 247 | 248 | .. data:: xmlsec.constants.NodeKeyInfo 249 | :annotation: = 'KeyInfo' 250 | 251 | .. data:: xmlsec.constants.NodeKeyName 252 | :annotation: = 'KeyName' 253 | 254 | .. data:: xmlsec.constants.NodeKeyValue 255 | :annotation: = 'KeyValue' 256 | 257 | .. data:: xmlsec.constants.NodeX509Data 258 | :annotation: = 'X509Data' 259 | 260 | Transforms 261 | ********** 262 | 263 | .. class:: __Transform 264 | 265 | Base type for all :samp:`Transform{XXX}` constants. 266 | 267 | .. data:: xmlsec.constants.TransformUsageUnknown 268 | 269 | Transforms usage is unknown or undefined. 270 | 271 | .. data:: xmlsec.constants.TransformUsageDSigTransform 272 | 273 | Transform could be used in :xml:``. 274 | 275 | .. data:: xmlsec.constants.TransformUsageC14NMethod 276 | 277 | Transform could be used in :xml:``. 278 | 279 | .. data:: xmlsec.constants.TransformUsageDigestMethod 280 | 281 | Transform could be used in :xml:``. 282 | 283 | .. data:: xmlsec.constants.TransformUsageSignatureMethod 284 | 285 | Transform could be used in :xml:``. 286 | 287 | .. data:: xmlsec.constants.TransformUsageEncryptionMethod 288 | 289 | Transform could be used in :xml:``. 290 | 291 | .. data:: xmlsec.constants.TransformUsageAny 292 | 293 | Transform could be used for operation. 294 | 295 | .. data:: xmlsec.constants.TransformInclC14N 296 | 297 | The regular (inclusive) C14N without comments transform klass. 298 | 299 | .. data:: xmlsec.constants.TransformInclC14NWithComments 300 | 301 | The regular (inclusive) C14N with comments transform klass. 302 | 303 | .. data:: xmlsec.constants.TransformInclC14N11 304 | 305 | The regular (inclusive) C14N 1.1 without comments transform klass. 306 | 307 | .. data:: xmlsec.constants.TransformInclC14N11WithComments 308 | 309 | The regular (inclusive) C14N 1.1 with comments transform klass. 310 | 311 | .. data:: xmlsec.constants.TransformExclC14N 312 | 313 | The exclusive C14N without comments transform klass. 314 | 315 | .. data:: xmlsec.constants.TransformExclC14NWithComments 316 | 317 | The exclusive C14N with comments transform klass. 318 | 319 | .. data:: xmlsec.constants.TransformEnveloped 320 | 321 | The "enveloped" transform klass. 322 | 323 | .. data:: xmlsec.constants.TransformXPath 324 | 325 | The XPath transform klass. 326 | 327 | .. data:: xmlsec.constants.TransformXPath2 328 | 329 | The XPath2 transform klass. 330 | 331 | .. data:: xmlsec.constants.TransformXPointer 332 | 333 | The XPointer transform klass. 334 | 335 | .. data:: xmlsec.constants.TransformXslt 336 | 337 | The XSLT transform klass. 338 | 339 | .. data:: xmlsec.constants.TransformRemoveXmlTagsC14N 340 | 341 | The "remove all xml tags" transform klass (used before base64 transforms). 342 | 343 | .. data:: xmlsec.constants.TransformVisa3DHack 344 | 345 | Selects node subtree by given node id string. The only reason why we need this is Visa3D protocol. It doesn't follow XML/XPointer/XMLDSig specs and allows invalid XPointer expressions in the URI attribute. Since we couldn't evaluate such expressions thru XPath/XPointer engine, we need to have this hack here. 346 | 347 | .. data:: xmlsec.constants.TransformAes128Cbc 348 | 349 | The AES128 CBC cipher transform klass. 350 | 351 | .. data:: xmlsec.constants.TransformAes192Cbc 352 | 353 | The AES192 CBC cipher transform klass. 354 | 355 | .. data:: xmlsec.constants.TransformAes256Cbc 356 | 357 | The AES256 CBC cipher transform klass. 358 | 359 | .. data:: xmlsec.constants.TransformKWAes128 360 | 361 | The AES 128 key wrap transform klass. 362 | 363 | .. data:: xmlsec.constants.TransformKWAes192 364 | 365 | The AES 192 key wrap transform klass. 366 | 367 | .. data:: xmlsec.constants.TransformKWAes256 368 | 369 | The AES 256 key wrap transform klass. 370 | 371 | .. data:: xmlsec.constants.TransformDes3Cbc 372 | 373 | The DES3 CBC cipher transform klass. 374 | 375 | .. data:: xmlsec.constants.TransformKWDes3 376 | 377 | The DES3 key wrap transform klass. 378 | 379 | .. data:: xmlsec.constants.TransformDsaSha1 380 | 381 | The DSA-SHA1 signature transform klass. 382 | 383 | .. data:: xmlsec.constants.TransformEcdsaSha1 384 | 385 | The ECDSA-SHA1 signature transform klass. 386 | 387 | .. data:: xmlsec.constants.TransformEcdsaSha224 388 | 389 | The ECDSA-SHA224 signature transform klass. 390 | 391 | .. data:: xmlsec.constants.TransformEcdsaSha256 392 | 393 | The ECDSA-SHA256 signature transform klass. 394 | 395 | .. data:: xmlsec.constants.TransformEcdsaSha384 396 | 397 | The ECDS-SHA384 signature transform klass. 398 | 399 | .. data:: xmlsec.constants.TransformEcdsaSha512 400 | 401 | The ECDSA-SHA512 signature transform klass. 402 | 403 | .. data:: xmlsec.constants.TransformHmacMd5 404 | 405 | The HMAC with MD5 signature transform klass. 406 | 407 | .. data:: xmlsec.constants.TransformHmacRipemd160 408 | 409 | The HMAC with RipeMD160 signature transform klass. 410 | 411 | .. data:: xmlsec.constants.TransformHmacSha1 412 | 413 | The HMAC with SHA1 signature transform klass. 414 | 415 | .. data:: xmlsec.constants.TransformHmacSha224 416 | 417 | The HMAC with SHA224 signature transform klass. 418 | 419 | .. data:: xmlsec.constants.TransformHmacSha256 420 | 421 | The HMAC with SHA256 signature transform klass. 422 | 423 | .. data:: xmlsec.constants.TransformHmacSha384 424 | 425 | The HMAC with SHA384 signature transform klass. 426 | 427 | .. data:: xmlsec.constants.TransformHmacSha512 428 | 429 | The HMAC with SHA512 signature transform klass. 430 | 431 | .. data:: xmlsec.constants.TransformRsaMd5 432 | 433 | The RSA-MD5 signature transform klass. 434 | 435 | .. data:: xmlsec.constants.TransformRsaRipemd160 436 | 437 | The RSA-RIPEMD160 signature transform klass. 438 | 439 | .. data:: xmlsec.constants.TransformRsaSha1 440 | 441 | The RSA-SHA1 signature transform klass. 442 | 443 | .. data:: xmlsec.constants.TransformRsaSha224 444 | 445 | The RSA-SHA224 signature transform klass. 446 | 447 | .. data:: xmlsec.constants.TransformRsaSha256 448 | 449 | The RSA-SHA256 signature transform klass. 450 | 451 | .. data:: xmlsec.constants.TransformRsaSha384 452 | 453 | The RSA-SHA384 signature transform klass. 454 | 455 | .. data:: xmlsec.constants.TransformRsaSha512 456 | 457 | The RSA-SHA512 signature transform klass. 458 | 459 | .. data:: xmlsec.constants.TransformRsaPkcs1 460 | 461 | The RSA PKCS1 key transport transform klass. 462 | 463 | .. data:: xmlsec.constants.TransformRsaOaep 464 | 465 | The RSA OAEP key transport transform klass. 466 | 467 | .. data:: xmlsec.constants.TransformMd5 468 | 469 | The MD5 digest transform klass. 470 | 471 | .. data:: xmlsec.constants.TransformRipemd160 472 | 473 | The RIPEMD160 digest transform klass. 474 | 475 | .. data:: xmlsec.constants.TransformSha1 476 | 477 | The SHA1 digest transform klass. 478 | 479 | .. data:: xmlsec.constants.TransformSha224 480 | 481 | The SHA224 digest transform klass. 482 | 483 | .. data:: xmlsec.constants.TransformSha256 484 | 485 | The SHA256 digest transform klass. 486 | 487 | .. data:: xmlsec.constants.TransformSha384 488 | 489 | The SHA384 digest transform klass. 490 | 491 | .. data:: xmlsec.constants.TransformSha512 492 | 493 | The SHA512 digest transform klass. 494 | 495 | :ref:`contents` 496 | -------------------------------------------------------------------------------- /doc/source/modules/template.rst: -------------------------------------------------------------------------------- 1 | ``xmlsec.template`` 2 | ------------------- 3 | 4 | .. automodule:: xmlsec.template 5 | :members: 6 | :undoc-members: 7 | 8 | 9 | :ref:`contents` 10 | -------------------------------------------------------------------------------- /doc/source/modules/tree.rst: -------------------------------------------------------------------------------- 1 | ``xmlsec.tree`` 2 | --------------- 3 | 4 | .. automodule:: xmlsec.tree 5 | :members: 6 | :undoc-members: 7 | 8 | 9 | :ref:`contents` 10 | -------------------------------------------------------------------------------- /doc/source/modules/xmlsec.rst: -------------------------------------------------------------------------------- 1 | ``xmlsec`` 2 | ---------- 3 | 4 | .. automodule:: xmlsec 5 | :members: 6 | :undoc-members: 7 | 8 | 9 | :ref:`contents` 10 | -------------------------------------------------------------------------------- /doc/source/requirements.txt: -------------------------------------------------------------------------------- 1 | lxml>=3.8 2 | importlib_metadata;python_version < '3.8' 3 | packaging 4 | Sphinx>=3 5 | furo>=2021.4.11b34 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.mypy] 2 | files = ['src'] 3 | ignore_missing_imports = false 4 | warn_unused_configs = true 5 | disallow_subclassing_any = true 6 | disallow_any_generics = true 7 | disallow_untyped_calls = true 8 | disallow_untyped_defs = true 9 | disallow_incomplete_defs = true 10 | check_untyped_defs = true 11 | disallow_untyped_decorators = true 12 | disallow_any_unimported = true 13 | strict_optional = true 14 | no_implicit_optional = true 15 | warn_redundant_casts = true 16 | warn_unused_ignores = true 17 | warn_return_any = true 18 | warn_no_return = true 19 | no_implicit_reexport = true 20 | show_error_codes = true 21 | 22 | [tool.black] 23 | line_length = 130 24 | skip-string-normalization = true 25 | target_version = ['py39'] 26 | include = '\.pyi?$' 27 | exclude = ''' 28 | 29 | ( 30 | /( 31 | \.eggs # exclude a few common directories in the 32 | | \.git # root of the project 33 | | \.mypy_cache 34 | | \.tox 35 | | build 36 | | dist 37 | )/ 38 | ) 39 | ''' 40 | 41 | [tool.isort] 42 | profile = 'black' 43 | known_first_party = ['xmlsec'] 44 | known_third_party = ['lxml', 'pytest', '_pytest', 'hypothesis'] 45 | 46 | [build-system] 47 | requires = ['setuptools>=42', 'wheel', 'setuptools_scm[toml]>=3.4', "pkgconfig>=1.5.1", "lxml>=3.8, !=4.7.0"] 48 | 49 | [tool.cibuildwheel] 50 | build-verbosity = 1 51 | build-frontend = "build" 52 | skip = [ 53 | "pp*", 54 | "*-musllinux_i686", 55 | # LXML doesn't publish wheels for these platforms, which makes it 56 | # difficult for us to build wheels, so we exclude them. 57 | "cp36-manylinux_aarch64", 58 | "cp37-manylinux_aarch64", 59 | "cp36-musllinux_aarch64", 60 | "cp37-musllinux_aarch64", 61 | "cp36-macosx*", 62 | "cp37-macosx*", 63 | "cp38-macosx*", 64 | ] 65 | test-command = "pytest -v --color=yes {package}/tests" 66 | before-test = "pip install -r requirements-test.txt" 67 | test-skip = "*-macosx_arm64" 68 | 69 | [tool.cibuildwheel.environment] 70 | PYXMLSEC_STATIC_DEPS = "true" 71 | 72 | [tool.cibuildwheel.linux] 73 | archs = ["x86_64", "aarch64", "i686"] 74 | environment-pass = [ 75 | "PYXMLSEC_LIBXML2_VERSION", 76 | "PYXMLSEC_LIBXSLT_VERSION", 77 | "PYXMLSEC_STATIC_DEPS", 78 | "GH_TOKEN" 79 | ] 80 | 81 | [tool.cibuildwheel.macos] 82 | archs = ["x86_64", "arm64"] 83 | before-all = "brew install perl" 84 | 85 | [tool.cibuildwheel.windows] 86 | archs = ["AMD64", "x86"] 87 | 88 | [[tool.cibuildwheel.overrides]] 89 | select = "*-manylinux*" 90 | before-all = "yum install -y perl-core" 91 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | pytest>=4.6.9 3 | lxml-stubs 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | lxml >= 3.8.0, !=4.7.0 --no-binary=lxml 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file = README.rst 3 | 4 | [bdist_rpm] 5 | release = 1 6 | build_requires = pkg-config xmlsec1-devel libxml2-devel xmlsec1-openssl-devel 7 | group = Development/Libraries 8 | requires = xmlsec1 xmlsec1-openssl 9 | 10 | [build_sphinx] 11 | source-dir = doc/source 12 | build-dir = doc/build 13 | all_files = 1 14 | 15 | [upload_docs] 16 | upload_dir = doc/build/html 17 | 18 | [flake8] 19 | per-file-ignores = 20 | *.pyi: E301, E302, E305, E501, E701, F401, F822 21 | doc/source/conf.py: D1 22 | doc/source/examples/*.py: D1, E501 23 | tests/*.py: D1 24 | exclude = .venv*,.git,*_pb2.pyi,build,dist,libs,.eggs,.direnv* 25 | max-line-length = 130 26 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_COMMON_H__ 11 | #define __PYXMLSEC_COMMON_H__ 12 | 13 | #include "debug.h" 14 | 15 | #ifndef MODULE_NAME 16 | #define MODULE_NAME xmlsec 17 | #endif 18 | 19 | #define JOIN(X,Y) DO_JOIN1(X,Y) 20 | #define DO_JOIN1(X,Y) DO_JOIN2(X,Y) 21 | #define DO_JOIN2(X,Y) X##Y 22 | 23 | #define DO_STRINGIFY(x) #x 24 | #define STRINGIFY(x) DO_STRINGIFY(x) 25 | 26 | #endif //__PYXMLSEC_COMMON_H__ 27 | -------------------------------------------------------------------------------- /src/constants.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_CONSTANTS_H__ 11 | #define __PYXMLSEC_CONSTANTS_H__ 12 | 13 | #include "platform.h" 14 | 15 | #include 16 | #include 17 | 18 | typedef struct { 19 | PyObject_HEAD 20 | xmlSecTransformId id; 21 | } PyXmlSec_Transform; 22 | 23 | typedef struct { 24 | PyObject_HEAD 25 | xmlSecKeyDataId id; 26 | } PyXmlSec_KeyData; 27 | 28 | extern PyTypeObject* PyXmlSec_TransformType; 29 | extern PyTypeObject* PyXmlSec_KeyDataType; 30 | 31 | #endif //__PYXMLSEC_CONSTANTS_H__ 32 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_DEBUG_H__ 11 | #define __PYXMLSEC_DEBUG_H__ 12 | 13 | #ifdef PYXMLSEC_ENABLE_DEBUG 14 | 15 | #include 16 | #define PYXMLSEC_DEBUG(fmt) fprintf(stderr, "[%s:%d %s] " fmt "\n", __FILE__, __LINE__, __FUNCTION__) 17 | #define PYXMLSEC_DEBUGF(fmt, ...) fprintf(stderr, "[%s:%d %s] " fmt "\n", __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 18 | #define PYXMLSEC_DUMP(method, obj) method(obj, stderr) 19 | #else 20 | #define PYXMLSEC_DEBUG(...) 21 | #define PYXMLSEC_DEBUGF(...) 22 | #define PYXMLSEC_DUMP(method, obj) 23 | #endif // PYXMLSEC_ENABLE_DEBUG 24 | 25 | #endif // __PYXMLSEC_DEBUG_H__ 26 | -------------------------------------------------------------------------------- /src/exception.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #include "common.h" 11 | #include "exception.h" 12 | #include "utils.h" 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | 21 | // default error class 22 | PyObject* PyXmlSec_Error; 23 | PyObject* PyXmlSec_InternalError; 24 | PyObject* PyXmlSec_VerificationError; 25 | 26 | #if PY_MINOR_VERSION >= 7 27 | static Py_tss_t PyXmlSec_LastErrorKey; 28 | #else 29 | static int PyXmlSec_LastErrorKey = 0; 30 | #endif 31 | 32 | static int PyXmlSec_PrintErrorMessage = 0; 33 | 34 | typedef struct { 35 | const xmlChar* file; 36 | const xmlChar* func; 37 | const xmlChar* object; 38 | const xmlChar* subject; 39 | const xmlChar* msg; 40 | int line; 41 | int reason; 42 | } PyXmlSec_ErrorHolder; 43 | 44 | PyXmlSec_ErrorHolder* PyXmlSec_ErrorHolderCreate(const char* file, int line, const char* func, const char* object, const char* subject, int reason, const char* msg) { 45 | PyXmlSec_ErrorHolder* h = (PyXmlSec_ErrorHolder*)xmlMalloc(sizeof(PyXmlSec_ErrorHolder)); 46 | 47 | // file and func is __FILE__ and __FUNCTION__ macro, so it can be stored as is. 48 | h->file = XSTR(file); 49 | h->line = line; 50 | h->func = XSTR(func); 51 | h->reason = reason; 52 | // there is no guarantee that object and subject will not be deallocate after exit from function, 53 | // so make a copy 54 | // xmlCharStrdup returns NULL if arg is NULL 55 | h->object = xmlCharStrdup(object); 56 | h->subject = xmlCharStrdup(subject); 57 | h->msg = xmlCharStrdup(msg); 58 | 59 | PYXMLSEC_DEBUGF("new error %p", h); 60 | return h; 61 | } 62 | 63 | void PyXmlSec_ErrorHolderFree(PyXmlSec_ErrorHolder* h) { 64 | if (h != NULL) { 65 | PYXMLSEC_DEBUGF("free error %p", h); 66 | xmlFree((void*)(h->object)); 67 | xmlFree((void*)(h->subject)); 68 | xmlFree((void*)(h->msg)); 69 | xmlFree((void*)(h)); 70 | } 71 | } 72 | 73 | // saves new error in TLS and returns previous 74 | static PyXmlSec_ErrorHolder* PyXmlSec_ExchangeLastError(PyXmlSec_ErrorHolder* e) { 75 | PyXmlSec_ErrorHolder* v; 76 | int r; 77 | 78 | #if PY_MINOR_VERSION >= 7 79 | if (PyThread_tss_is_created(&PyXmlSec_LastErrorKey) == 0) { 80 | #else 81 | if (PyXmlSec_LastErrorKey == 0) { 82 | #endif 83 | PYXMLSEC_DEBUG("WARNING: There is no error key."); 84 | PyXmlSec_ErrorHolderFree(e); 85 | return NULL; 86 | } 87 | 88 | // get_key_value and set_key_value are gil free 89 | #if PY_MINOR_VERSION >= 7 90 | v = (PyXmlSec_ErrorHolder*)PyThread_tss_get(&PyXmlSec_LastErrorKey); 91 | //PyThread_tss_delete(&PyXmlSec_LastErrorKey); 92 | r = PyThread_tss_set(&PyXmlSec_LastErrorKey, (void*)e); 93 | #else 94 | v = (PyXmlSec_ErrorHolder*)PyThread_get_key_value(PyXmlSec_LastErrorKey); 95 | PyThread_delete_key_value(PyXmlSec_LastErrorKey); 96 | r = PyThread_set_key_value(PyXmlSec_LastErrorKey, (void*)e); 97 | #endif 98 | PYXMLSEC_DEBUGF("set_key_value returns %d", r); 99 | return v; 100 | } 101 | 102 | // xmlsec library error callback 103 | static void PyXmlSec_ErrorCallback(const char* file, int line, const char* func, const char* object, const char* subject, int reason, const char* msg) { 104 | // TODO do not allocate error object each time. 105 | PyXmlSec_ErrorHolderFree(PyXmlSec_ExchangeLastError(PyXmlSec_ErrorHolderCreate(file, line, func, object, subject, reason, msg))); 106 | 107 | if (PyXmlSec_PrintErrorMessage) { 108 | const char* error_msg = NULL; 109 | xmlSecSize i; 110 | for (i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) && (xmlSecErrorsGetMsg(i) != NULL); ++i) { 111 | if(xmlSecErrorsGetCode(i) == reason) { 112 | error_msg = xmlSecErrorsGetMsg(i); 113 | break; 114 | } 115 | } 116 | 117 | fprintf(stderr, 118 | "func=%s:file=%s:line=%d:obj=%s:subj=%s:error=%d:%s:%s\n", 119 | (func != NULL) ? func : "unknown", 120 | (file != NULL) ? file : "unknown", 121 | line, 122 | (object != NULL) ? object : "unknown", 123 | (subject != NULL) ? subject : "unknown", 124 | reason, 125 | (error_msg != NULL) ? error_msg : "", 126 | (msg != NULL) ? msg : ""); 127 | } 128 | } 129 | 130 | // pops the last error which was occurred in current thread 131 | // the gil should be acquired 132 | static PyObject* PyXmlSec_GetLastError(PyObject* type, const char* msg) { 133 | PyXmlSec_ErrorHolder* h = PyXmlSec_ExchangeLastError(NULL); 134 | PyObject* exc; 135 | 136 | if (h == NULL) { 137 | return NULL; 138 | } 139 | 140 | exc = PyObject_CallFunction(type, "is", h->reason, msg); 141 | if (exc == NULL) goto ON_FAIL; 142 | 143 | PyXmlSec_SetLongAttr(exc, "code", h->reason); 144 | PyXmlSec_SetStringAttr(exc, "message", msg); 145 | PyXmlSec_SetStringAttr(exc, "details", (const char*)xmlSecErrorsSafeString(h->msg)); 146 | PyXmlSec_SetStringAttr(exc, "file", (const char*)xmlSecErrorsSafeString(h->file)); 147 | PyXmlSec_SetLongAttr(exc, "line", h->line); 148 | PyXmlSec_SetStringAttr(exc, "func", (const char*)xmlSecErrorsSafeString(h->func)); 149 | PyXmlSec_SetStringAttr(exc, "object", (const char*)xmlSecErrorsSafeString(h->object)); 150 | PyXmlSec_SetStringAttr(exc, "subject", (const char*)xmlSecErrorsSafeString(h->subject)); 151 | 152 | ON_FAIL: 153 | PyXmlSec_ErrorHolderFree(h); 154 | return exc; 155 | } 156 | 157 | void PyXmlSec_SetLastError2(PyObject* type, const char* msg) { 158 | PyObject* last = PyXmlSec_GetLastError(type, msg); 159 | if (last == NULL) { 160 | PYXMLSEC_DEBUG("WARNING: no xmlsec error"); 161 | last = PyObject_CallFunction(PyXmlSec_InternalError, "is", (int)-1, msg); 162 | if (last == NULL) { 163 | return; 164 | } 165 | } 166 | PyErr_SetObject(type, last); 167 | Py_DECREF(last); 168 | } 169 | 170 | void PyXmlSec_SetLastError(const char* msg) { 171 | PyXmlSec_SetLastError2(PyXmlSec_Error, msg); 172 | } 173 | 174 | void PyXmlSec_ClearError(void) { 175 | PyXmlSec_ErrorHolderFree(PyXmlSec_ExchangeLastError(NULL)); 176 | } 177 | 178 | void PyXmlSecEnableDebugTrace(int v) { 179 | PyXmlSec_PrintErrorMessage = v; 180 | } 181 | 182 | void PyXmlSec_InstallErrorCallback() { 183 | #if PY_MINOR_VERSION >= 7 184 | if (PyThread_tss_is_created(&PyXmlSec_LastErrorKey) != 0) { 185 | #else 186 | if (PyXmlSec_LastErrorKey != 0) { 187 | #endif 188 | xmlSecErrorsSetCallback(PyXmlSec_ErrorCallback); 189 | } 190 | } 191 | 192 | // initializes errors module 193 | int PyXmlSec_ExceptionsModule_Init(PyObject* package) { 194 | PyXmlSec_Error = NULL; 195 | PyXmlSec_InternalError = NULL; 196 | PyXmlSec_VerificationError = NULL; 197 | 198 | if ((PyXmlSec_Error = PyErr_NewExceptionWithDoc( 199 | STRINGIFY(MODULE_NAME) ".Error", "The common exception class.", PyExc_Exception, 0)) == NULL) goto ON_FAIL; 200 | 201 | if ((PyXmlSec_InternalError = PyErr_NewExceptionWithDoc( 202 | STRINGIFY(MODULE_NAME) ".InternalError", "The internal exception class.", PyXmlSec_Error, 0)) == NULL) goto ON_FAIL; 203 | 204 | if ((PyXmlSec_VerificationError = PyErr_NewExceptionWithDoc( 205 | STRINGIFY(MODULE_NAME) ".VerificationError", "The verification exception class.", PyXmlSec_Error, 0)) == NULL) goto ON_FAIL; 206 | 207 | if (PyModule_AddObject(package, "Error", PyXmlSec_Error) < 0) goto ON_FAIL; 208 | if (PyModule_AddObject(package, "InternalError", PyXmlSec_InternalError) < 0) goto ON_FAIL; 209 | if (PyModule_AddObject(package, "VerificationError", PyXmlSec_VerificationError) < 0) goto ON_FAIL; 210 | 211 | #if PY_MINOR_VERSION >= 7 212 | if (PyThread_tss_create(&PyXmlSec_LastErrorKey) == 0) { 213 | PyXmlSec_InstallErrorCallback(); 214 | } 215 | #else 216 | PyXmlSec_LastErrorKey = PyThread_create_key(); 217 | PyXmlSec_InstallErrorCallback(); 218 | #endif 219 | 220 | return 0; 221 | 222 | ON_FAIL: 223 | Py_XDECREF(PyXmlSec_Error); 224 | Py_XDECREF(PyXmlSec_InternalError); 225 | Py_XDECREF(PyXmlSec_VerificationError); 226 | return -1; 227 | } 228 | -------------------------------------------------------------------------------- /src/exception.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_EXCEPTIONS_H__ 11 | #define __PYXMLSEC_EXCEPTIONS_H__ 12 | 13 | #include "platform.h" 14 | 15 | extern PyObject* PyXmlSec_Error; 16 | extern PyObject* PyXmlSec_InternalError; 17 | extern PyObject* PyXmlSec_VerificationError; 18 | 19 | void PyXmlSec_SetLastError(const char* msg); 20 | 21 | void PyXmlSec_SetLastError2(PyObject* type, const char* msg); 22 | 23 | void PyXmlSec_ClearError(void); 24 | 25 | void PyXmlSecEnableDebugTrace(int); 26 | 27 | void PyXmlSec_InstallErrorCallback(); 28 | 29 | #endif //__PYXMLSEC_EXCEPTIONS_H__ 30 | -------------------------------------------------------------------------------- /src/keys.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_KEY_H__ 11 | #define __PYXMLSEC_KEY_H__ 12 | 13 | #include "platform.h" 14 | 15 | #include 16 | 17 | typedef struct { 18 | PyObject_HEAD 19 | xmlSecKeyPtr handle; 20 | int is_own; 21 | } PyXmlSec_Key; 22 | 23 | extern PyTypeObject* PyXmlSec_KeyType; 24 | 25 | PyXmlSec_Key* PyXmlSec_NewKey(void); 26 | 27 | typedef struct { 28 | PyObject_HEAD 29 | xmlSecKeysMngrPtr handle; 30 | } PyXmlSec_KeysManager; 31 | 32 | extern PyTypeObject* PyXmlSec_KeysManagerType; 33 | 34 | // converts object `o` to PyXmlSec_KeysManager, None will be converted to NULL, increments ref_count 35 | int PyXmlSec_KeysManagerConvert(PyObject* o, PyXmlSec_KeysManager** p); 36 | 37 | #endif //__PYXMLSEC_KEY_H__ 38 | -------------------------------------------------------------------------------- /src/lxml.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #include "common.h" 11 | #include "lxml.h" 12 | #include "exception.h" 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #define XMLSEC_EXTRACT_VERSION(x, y) ((x / (y)) % 100) 22 | 23 | #define XMLSEC_EXTRACT_MAJOR(x) XMLSEC_EXTRACT_VERSION(x, 100 * 100) 24 | #define XMLSEC_EXTRACT_MINOR(x) XMLSEC_EXTRACT_VERSION(x, 100) 25 | #define XMLSEC_EXTRACT_PATCH(x) XMLSEC_EXTRACT_VERSION(x, 1) 26 | 27 | static long PyXmlSec_GetLibXmlVersionLong() { 28 | return PyOS_strtol(xmlParserVersion, NULL, 10); 29 | } 30 | long PyXmlSec_GetLibXmlVersionMajor() { 31 | return XMLSEC_EXTRACT_MAJOR(PyXmlSec_GetLibXmlVersionLong()); 32 | } 33 | long PyXmlSec_GetLibXmlVersionMinor() { 34 | return XMLSEC_EXTRACT_MINOR(PyXmlSec_GetLibXmlVersionLong()); 35 | } 36 | long PyXmlSec_GetLibXmlVersionPatch() { 37 | return XMLSEC_EXTRACT_PATCH(PyXmlSec_GetLibXmlVersionLong()); 38 | } 39 | 40 | long PyXmlSec_GetLibXmlCompiledVersionMajor() { 41 | return XMLSEC_EXTRACT_MAJOR(LIBXML_VERSION); 42 | } 43 | long PyXmlSec_GetLibXmlCompiledVersionMinor() { 44 | return XMLSEC_EXTRACT_MINOR(LIBXML_VERSION); 45 | } 46 | long PyXmlSec_GetLibXmlCompiledVersionPatch() { 47 | return XMLSEC_EXTRACT_PATCH(LIBXML_VERSION); 48 | } 49 | 50 | static int PyXmlSec_CheckLxmlLibraryVersion(void) { 51 | // Make sure that the version of libxml2 lxml is using is the same as the one we are using. Because 52 | // we pass trees between the two libraries, we need to make sure that they are using the same version 53 | // of libxml2, or we could run into difficult to debug segfaults. 54 | // See: https://github.com/xmlsec/python-xmlsec/issues/283 55 | 56 | PyObject* lxml = NULL; 57 | PyObject* version = NULL; 58 | 59 | // Default to failure 60 | int result = -1; 61 | 62 | lxml = PyImport_ImportModule("lxml.etree"); 63 | if (lxml == NULL) { 64 | goto FINALIZE; 65 | } 66 | version = PyObject_GetAttrString(lxml, "LIBXML_VERSION"); 67 | if (version == NULL) { 68 | goto FINALIZE; 69 | } 70 | if (!PyTuple_Check(version) || PyTuple_Size(version) < 2) { 71 | goto FINALIZE; 72 | } 73 | 74 | PyObject* major = PyTuple_GetItem(version, 0); 75 | if (major == NULL) { 76 | goto FINALIZE; 77 | } 78 | PyObject* minor = PyTuple_GetItem(version, 1); 79 | if (minor == NULL) { 80 | goto FINALIZE; 81 | } 82 | 83 | if (!PyLong_Check(major) || !PyLong_Check(minor)) { 84 | goto FINALIZE; 85 | } 86 | 87 | if (PyLong_AsLong(major) != PyXmlSec_GetLibXmlVersionMajor() || PyLong_AsLong(minor) != PyXmlSec_GetLibXmlVersionMinor()) { 88 | goto FINALIZE; 89 | } 90 | 91 | result = 0; 92 | 93 | FINALIZE: 94 | // Clear any errors that may have occurred 95 | PyErr_Clear(); 96 | 97 | // Cleanup our references, and return the result 98 | Py_XDECREF(lxml); 99 | Py_XDECREF(version); 100 | 101 | return result; 102 | } 103 | 104 | int PyXmlSec_InitLxmlModule(void) { 105 | if (PyXmlSec_CheckLxmlLibraryVersion() < 0) { 106 | PyXmlSec_SetLastError("lxml & xmlsec libxml2 library version mismatch"); 107 | return -1; 108 | } 109 | 110 | return import_lxml__etree(); 111 | } 112 | 113 | int PyXmlSec_IsElement(xmlNodePtr xnode) { 114 | return _isElement(xnode); 115 | } 116 | 117 | PyXmlSec_LxmlElementPtr PyXmlSec_elementFactory(PyXmlSec_LxmlDocumentPtr doc, xmlNodePtr xnode) { 118 | return elementFactory(doc, xnode); 119 | } 120 | 121 | 122 | int PyXmlSec_LxmlElementConverter(PyObject* o, PyXmlSec_LxmlElementPtr* p) { 123 | PyXmlSec_LxmlElementPtr node = rootNodeOrRaise(o); 124 | if (node == NULL) { 125 | return 0; 126 | } 127 | *p = node; 128 | // rootNodeOrRaise - increments ref-count, so need to compensate this. 129 | Py_DECREF(node); 130 | return 1; 131 | } 132 | -------------------------------------------------------------------------------- /src/lxml.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_LXML_H__ 11 | #define __PYXMLSEC_LXML_H__ 12 | 13 | #include "platform.h" 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | typedef struct LxmlElement* PyXmlSec_LxmlElementPtr; 22 | typedef struct LxmlDocument* PyXmlSec_LxmlDocumentPtr; 23 | 24 | // checks that xnode is Element 25 | int PyXmlSec_IsElement(xmlNodePtr xnode); 26 | // creates a new element 27 | PyXmlSec_LxmlElementPtr PyXmlSec_elementFactory(PyXmlSec_LxmlDocumentPtr doc, xmlNodePtr node); 28 | 29 | // converts o to PyObject, None object is not allowed, does not increment ref_counts 30 | int PyXmlSec_LxmlElementConverter(PyObject* o, PyXmlSec_LxmlElementPtr* p); 31 | 32 | // get version numbers for libxml2 both compiled and loaded 33 | long PyXmlSec_GetLibXmlVersionMajor(); 34 | long PyXmlSec_GetLibXmlVersionMinor(); 35 | long PyXmlSec_GetLibXmlVersionPatch(); 36 | 37 | long PyXmlSec_GetLibXmlCompiledVersionMajor(); 38 | long PyXmlSec_GetLibXmlCompiledVersionMinor(); 39 | long PyXmlSec_GetLibXmlCompiledVersionPatch(); 40 | 41 | #endif // __PYXMLSEC_LXML_H__ 42 | -------------------------------------------------------------------------------- /src/platform.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_PLATFORM_H__ 11 | #define __PYXMLSEC_PLATFORM_H__ 12 | 13 | #define PY_SSIZE_T_CLEAN 1 14 | 15 | #include 16 | #include 17 | 18 | #ifdef MS_WIN32 19 | #include 20 | #endif /* MS_WIN32 */ 21 | 22 | #define XMLSEC_VERSION_HEX ((XMLSEC_VERSION_MAJOR << 16) | (XMLSEC_VERSION_MINOR << 8) | (XMLSEC_VERSION_SUBMINOR)) 23 | 24 | // XKMS support was removed in version 1.2.21 25 | // https://mail.gnome.org/archives/commits-list/2015-February/msg10555.html 26 | #if XMLSEC_VERSION_HEX > 0x10214 27 | #define XMLSEC_NO_XKMS 1 28 | #endif 29 | 30 | #define XSTR(c) (const xmlChar*)(c) 31 | 32 | #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) 33 | typedef int Py_ssize_t; 34 | #define PY_SSIZE_T_MAX INT_MAX 35 | #define PY_SSIZE_T_MIN INT_MIN 36 | #endif 37 | 38 | static inline char* PyBytes_AsStringAndSize2(PyObject *obj, Py_ssize_t* length) { 39 | char* buffer = NULL; 40 | return ((PyBytes_AsStringAndSize(obj, &buffer, length) < 0) ? (char*)(0) : buffer); 41 | } 42 | 43 | #endif //__PYXMLSEC_PLATFORM_H__ 44 | -------------------------------------------------------------------------------- /src/tree.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #include "common.h" 11 | #include "utils.h" 12 | #include "lxml.h" 13 | 14 | #include 15 | 16 | #define PYXMLSEC_TREE_DOC "Common XML utility functions" 17 | 18 | static char PyXmlSec_TreeFindChild__doc__[] = \ 19 | "find_child(parent, name, namespace)\n" 20 | "Searches a direct child of the ``parent`` node having given ``name`` and ``namespace`` href.\n\n" 21 | ":param parent: the pointer to XML node\n" 22 | ":type parent: :class:`lxml.etree._Element`\n" 23 | ":param name: the name\n" 24 | ":type name: :class:`str`\n" 25 | ":param namespace: the namespace href (optional)\n" 26 | ":type namespace: :class:`str`\n" 27 | ":return: the pointer to the found node or :data:`None` if node is not found\n" 28 | ":rtype: :class:`lxml.etree._Element` or :data:`None`"; 29 | static PyObject* PyXmlSec_TreeFindChild(PyObject* self, PyObject *args, PyObject *kwargs) { 30 | static char *kwlist[] = { "parent", "name", "namespace", NULL}; 31 | 32 | PyXmlSec_LxmlElementPtr node = NULL; 33 | const char* name = NULL; 34 | const char* ns = (const char*)xmlSecDSigNs; 35 | xmlNodePtr res; 36 | 37 | PYXMLSEC_DEBUG("tree find_child - start"); 38 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|s:find_child", kwlist, 39 | PyXmlSec_LxmlElementConverter, &node, &name, &ns)) 40 | { 41 | goto ON_FAIL; 42 | } 43 | 44 | Py_BEGIN_ALLOW_THREADS; 45 | res = xmlSecFindChild(node->_c_node, XSTR(name), XSTR(ns)); 46 | Py_END_ALLOW_THREADS; 47 | 48 | PYXMLSEC_DEBUG("tree find_child - ok"); 49 | if (res == NULL) { 50 | Py_RETURN_NONE; 51 | } 52 | return (PyObject*)PyXmlSec_elementFactory(node->_doc, res); 53 | 54 | ON_FAIL: 55 | PYXMLSEC_DEBUG("tree find_child - fail"); 56 | return NULL; 57 | } 58 | 59 | static char PyXmlSec_TreeFindParent__doc__[] = \ 60 | "find_parent(node, name, namespace)\n" 61 | "Searches the ancestors axis of the ``node`` having given ``name`` and ``namespace`` href.\n\n" 62 | ":param node: the pointer to XML node\n" 63 | ":type node: :class:`lxml.etree._Element`\n" 64 | ":param name: the name\n" 65 | ":type name: :class:`str`\n" 66 | ":param namespace: the namespace href (optional)\n" 67 | ":type namespace: :class:`str`\n" 68 | ":return: the pointer to the found node or :data:`None` if node is not found\n" 69 | ":rtype: :class:`lxml.etree._Element` or :data:`None`"; 70 | static PyObject* PyXmlSec_TreeFindParent(PyObject* self, PyObject *args, PyObject *kwargs) { 71 | static char *kwlist[] = { "node", "name", "namespace", NULL}; 72 | 73 | PyXmlSec_LxmlElementPtr node = NULL; 74 | const char* name = NULL; 75 | const char* ns = (const char*)xmlSecDSigNs; 76 | xmlNodePtr res; 77 | 78 | PYXMLSEC_DEBUG("tree find_parent - start"); 79 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|s:find_parent", kwlist, 80 | PyXmlSec_LxmlElementConverter, &node, &name, &ns)) 81 | { 82 | goto ON_FAIL; 83 | } 84 | 85 | Py_BEGIN_ALLOW_THREADS; 86 | res = xmlSecFindParent(node->_c_node, XSTR(name), XSTR(ns)); 87 | Py_END_ALLOW_THREADS; 88 | 89 | PYXMLSEC_DEBUG("tree find_parent - ok"); 90 | if (res == NULL) { 91 | Py_RETURN_NONE; 92 | } 93 | return (PyObject*)PyXmlSec_elementFactory(node->_doc, res); 94 | 95 | ON_FAIL: 96 | PYXMLSEC_DEBUG("tree find_parent - fail"); 97 | return NULL; 98 | } 99 | 100 | static char PyXmlSec_TreeFindNode__doc__[] = \ 101 | "find_node(node, name, namespace)\n" 102 | "Searches all children of the given ``node`` having given ``name`` and ``namespace`` href.\n\n" 103 | ":param node: the pointer to XML node\n" 104 | ":type node: :class:`lxml.etree._Element`\n" 105 | ":param name: the name\n" 106 | ":type name: :class:`str`\n" 107 | ":param namespace: the namespace href (optional)\n" 108 | ":type namespace: :class:`str`\n" 109 | ":return: the pointer to the found node or :data:`None` if node is not found\n" 110 | ":rtype: :class:`lxml.etree._Element` or :data:`None`"; 111 | static PyObject* PyXmlSec_TreeFindNode(PyObject* self, PyObject *args, PyObject *kwargs) { 112 | static char *kwlist[] = { "node", "name", "namespace", NULL}; 113 | 114 | PyXmlSec_LxmlElementPtr node = NULL; 115 | const char* name = NULL; 116 | const char* ns = (const char*)xmlSecDSigNs; 117 | xmlNodePtr res; 118 | 119 | PYXMLSEC_DEBUG("tree find_node - start"); 120 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&s|s:find_node", kwlist, 121 | PyXmlSec_LxmlElementConverter, &node, &name, &ns)) 122 | { 123 | goto ON_FAIL; 124 | } 125 | 126 | Py_BEGIN_ALLOW_THREADS; 127 | res = xmlSecFindNode(node->_c_node, XSTR(name), XSTR(ns)); 128 | Py_END_ALLOW_THREADS; 129 | 130 | PYXMLSEC_DEBUG("tree find_node - ok"); 131 | if (res == NULL) { 132 | Py_RETURN_NONE; 133 | } 134 | return (PyObject*)PyXmlSec_elementFactory(node->_doc, res); 135 | 136 | ON_FAIL: 137 | PYXMLSEC_DEBUG("tree find_node - fail"); 138 | return NULL; 139 | } 140 | 141 | static char PyXmlSec_TreeAddIds__doc__[] = \ 142 | "add_ids(node, ids) -> None\n" 143 | "Registers ``ids`` as ids used below ``node``. ``ids`` is a sequence of attribute names "\ 144 | "used as XML ids in the subtree rooted at ``node``.\n"\ 145 | "A call to :func:`~.add_ids` may be necessary to make known which attributes contain XML ids.\n"\ 146 | "This is the case, if a transform references an id via ``XPointer`` or a self document uri and " 147 | "the id inkey_data_formation is not available by other means (e.g. an associated DTD or XML schema).\n\n" 148 | ":param node: the pointer to XML node\n" 149 | ":type node: :class:`lxml.etree._Element`\n" 150 | ":param ids: the list of ID attributes.\n" 151 | ":type ids: :class:`list` of strings"; 152 | static PyObject* PyXmlSec_TreeAddIds(PyObject* self, PyObject *args, PyObject *kwargs) { 153 | static char *kwlist[] = { "node", "ids", NULL}; 154 | 155 | PyXmlSec_LxmlElementPtr node = NULL; 156 | PyObject* ids = NULL; 157 | 158 | const xmlChar** list = NULL; 159 | 160 | Py_ssize_t n; 161 | PyObject* tmp; 162 | PyObject* key; 163 | Py_ssize_t i; 164 | 165 | PYXMLSEC_DEBUG("tree add_ids - start"); 166 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O:add_ids", kwlist, PyXmlSec_LxmlElementConverter, &node, &ids)) 167 | { 168 | goto ON_FAIL; 169 | } 170 | n = PyObject_Length(ids); 171 | if (n < 0) goto ON_FAIL; 172 | 173 | list = (const xmlChar**)xmlMalloc(sizeof(xmlChar*) * (n + 1)); 174 | if (list == NULL) { 175 | PyErr_SetString(PyExc_MemoryError, "no memory"); 176 | goto ON_FAIL; 177 | } 178 | 179 | for (i = 0; i < n; ++i) { 180 | key = PyLong_FromSsize_t(i); 181 | if (key == NULL) goto ON_FAIL; 182 | tmp = PyObject_GetItem(ids, key); 183 | Py_DECREF(key); 184 | if (tmp == NULL) goto ON_FAIL; 185 | list[i] = XSTR(PyUnicode_AsUTF8(tmp)); 186 | Py_DECREF(tmp); 187 | if (list[i] == NULL) goto ON_FAIL; 188 | } 189 | list[n] = NULL; 190 | 191 | Py_BEGIN_ALLOW_THREADS; 192 | xmlSecAddIDs(node->_doc->_c_doc, node->_c_node, list); 193 | Py_END_ALLOW_THREADS; 194 | 195 | PyMem_Free(list); 196 | 197 | PYXMLSEC_DEBUG("tree add_ids - ok"); 198 | Py_RETURN_NONE; 199 | ON_FAIL: 200 | PYXMLSEC_DEBUG("tree add_ids - fail"); 201 | xmlFree(list); 202 | return NULL; 203 | } 204 | 205 | static PyMethodDef PyXmlSec_TreeMethods[] = { 206 | { 207 | "find_child", 208 | (PyCFunction)PyXmlSec_TreeFindChild, 209 | METH_VARARGS|METH_KEYWORDS, 210 | PyXmlSec_TreeFindChild__doc__, 211 | }, 212 | { 213 | "find_parent", 214 | (PyCFunction)PyXmlSec_TreeFindParent, 215 | METH_VARARGS|METH_KEYWORDS, 216 | PyXmlSec_TreeFindParent__doc__, 217 | }, 218 | { 219 | "find_node", 220 | (PyCFunction)PyXmlSec_TreeFindNode, 221 | METH_VARARGS|METH_KEYWORDS, 222 | PyXmlSec_TreeFindNode__doc__, 223 | }, 224 | { 225 | "add_ids", 226 | (PyCFunction)PyXmlSec_TreeAddIds, 227 | METH_VARARGS|METH_KEYWORDS, 228 | PyXmlSec_TreeAddIds__doc__, 229 | }, 230 | {NULL, NULL} /* sentinel */ 231 | }; 232 | 233 | static PyModuleDef PyXmlSec_TreeModule = 234 | { 235 | PyModuleDef_HEAD_INIT, 236 | STRINGIFY(MODULE_NAME) ".tree", 237 | PYXMLSEC_TREE_DOC, 238 | -1, 239 | PyXmlSec_TreeMethods, /* m_methods */ 240 | NULL, /* m_slots */ 241 | NULL, /* m_traverse */ 242 | NULL, /* m_clear */ 243 | NULL, /* m_free */ 244 | }; 245 | 246 | 247 | int PyXmlSec_TreeModule_Init(PyObject* package) { 248 | PyObject* tree = PyModule_Create(&PyXmlSec_TreeModule); 249 | 250 | if (!tree) goto ON_FAIL; 251 | 252 | if (PyModule_AddObject(package, "tree", tree) < 0) goto ON_FAIL; 253 | 254 | return 0; 255 | ON_FAIL: 256 | Py_XDECREF(tree); 257 | return -1; 258 | } 259 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #include "utils.h" 11 | 12 | PyObject* PyXmlSec_GetFilePathOrContent(PyObject* file, int* is_content) { 13 | PyObject* data; 14 | PyObject* utf8; 15 | PyObject* tmp = NULL; 16 | 17 | if (PyObject_HasAttrString(file, "read")) { 18 | data = PyObject_CallMethod(file, "read", NULL); 19 | if (data != NULL && PyUnicode_Check(data)) { 20 | utf8 = PyUnicode_AsUTF8String(data); 21 | Py_DECREF(data); 22 | data = utf8; 23 | } 24 | *is_content = 1; 25 | return data; 26 | } 27 | *is_content = 0; 28 | if (!PyUnicode_FSConverter(file, &tmp)) { 29 | return NULL; 30 | } 31 | return tmp; 32 | } 33 | 34 | int PyXmlSec_SetStringAttr(PyObject* obj, const char* name, const char* value) { 35 | PyObject* tmp = PyUnicode_FromString(value); 36 | int r; 37 | 38 | if (tmp == NULL) { 39 | return -1; 40 | } 41 | r = PyObject_SetAttrString(obj, name, tmp); 42 | Py_DECREF(tmp); 43 | return r; 44 | } 45 | 46 | int PyXmlSec_SetLongAttr(PyObject* obj, const char* name, long value) { 47 | PyObject* tmp = PyLong_FromLong(value); 48 | int r; 49 | 50 | if (tmp == NULL) { 51 | return -1; 52 | } 53 | r = PyObject_SetAttrString(obj, name, tmp); 54 | Py_DECREF(tmp); 55 | return r; 56 | } 57 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Ryan Leckey 2 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 8 | // SOFTWARE. 9 | 10 | #ifndef __PYXMLSEC_UTILS_H__ 11 | #define __PYXMLSEC_UTILS_H__ 12 | 13 | #include "platform.h" 14 | 15 | int PyXmlSec_SetStringAttr(PyObject* obj, const char* name, const char* value); 16 | 17 | int PyXmlSec_SetLongAttr(PyObject* obj, const char* name, long value); 18 | 19 | // return content if file is fileobject, or fs encoded filepath 20 | PyObject* PyXmlSec_GetFilePathOrContent(PyObject* file, int* is_content); 21 | 22 | #endif //__PYXMLSEC_UTILS_H__ 23 | -------------------------------------------------------------------------------- /src/xmlsec/__init__.pyi: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable, Iterable 2 | from typing import IO, Any, AnyStr, TypeVar, overload 3 | 4 | from _typeshed import GenericPath, Self, StrOrBytesPath 5 | from lxml.etree import _Element 6 | 7 | from xmlsec import constants as constants 8 | from xmlsec import template as template 9 | from xmlsec import tree as tree 10 | from xmlsec.constants import __KeyData as KeyData 11 | from xmlsec.constants import __Transform as Transform 12 | 13 | _E = TypeVar('_E', bound=_Element) 14 | 15 | def enable_debug_trace(enabled: bool = ...) -> None: ... 16 | def get_libxml_version() -> tuple[int, int, int]: ... 17 | def get_libxml_compiled_version() -> tuple[int, int, int]: ... 18 | def init() -> None: ... 19 | def shutdown() -> None: ... 20 | def cleanup_callbacks() -> None: ... 21 | def register_default_callbacks() -> None: ... 22 | def register_callbacks( 23 | input_match_callback: Callable[[bytes], bool], 24 | input_open_callback: Callable[[bytes], Any], 25 | input_read_callback: Callable[[Any, memoryview], int], 26 | input_close_callback: Callable[[Any], None], 27 | ) -> None: ... 28 | @overload 29 | def base64_default_line_size() -> int: ... 30 | @overload 31 | def base64_default_line_size(size: int) -> None: ... 32 | 33 | class EncryptionContext: 34 | key: Key | None 35 | def __init__(self, manager: KeysManager | None = ...) -> None: ... 36 | def decrypt(self, node: _Element) -> _Element: ... 37 | def encrypt_binary(self, template: _E, data: bytes) -> _E: ... 38 | def encrypt_uri(self, template: _E, uri: str) -> _E: ... 39 | def encrypt_xml(self, template: _E, node: _Element) -> _E: ... 40 | def reset(self) -> None: ... 41 | 42 | class Error(Exception): ... 43 | class InternalError(Error): ... 44 | 45 | class Key: 46 | name: str 47 | @classmethod 48 | def from_binary_data(cls: type[Self], klass: KeyData, data: AnyStr) -> Self: ... 49 | @classmethod 50 | def from_binary_file(cls: type[Self], klass: KeyData, filename: StrOrBytesPath) -> Self: ... 51 | @classmethod 52 | def from_file(cls: type[Self], file: GenericPath[AnyStr] | IO[AnyStr], format: int, password: str | None = ...) -> Self: ... 53 | @classmethod 54 | def from_engine(cls: type[Self], engine_and_key_id: AnyStr) -> Self: ... 55 | @classmethod 56 | def from_memory(cls: type[Self], data: AnyStr, format: int, password: str | None = ...) -> Self: ... 57 | @classmethod 58 | def generate(cls: type[Self], klass: KeyData, size: int, type: int) -> Self: ... 59 | def load_cert_from_file(self, file: GenericPath[AnyStr] | IO[AnyStr], format: int) -> None: ... 60 | def load_cert_from_memory(self, data: AnyStr, format: int) -> None: ... 61 | def __copy__(self: Self) -> Self: ... 62 | def __deepcopy__(self: Self) -> Self: ... 63 | 64 | class KeysManager: 65 | def add_key(self, key: Key) -> None: ... 66 | def load_cert(self, filename: StrOrBytesPath, format: int, type: int) -> None: ... 67 | def load_cert_from_memory(self, data: AnyStr, format: int, type: int) -> None: ... 68 | 69 | class SignatureContext: 70 | key: Key | None 71 | def enable_reference_transform(self, transform: Transform) -> None: ... 72 | def enable_signature_transform(self, transform: Transform) -> None: ... 73 | def register_id(self, node: _Element, id_attr: str = ..., id_ns: str | None = ...) -> None: ... 74 | def set_enabled_key_data(self, keydata_list: Iterable[KeyData]) -> None: ... 75 | def sign(self, node: _Element) -> None: ... 76 | def sign_binary(self, bytes: bytes, transform: Transform) -> bytes: ... 77 | def verify(self, node: _Element) -> None: ... 78 | def verify_binary(self, bytes: bytes, transform: Transform, signature: bytes) -> None: ... 79 | 80 | class VerificationError(Error): ... 81 | -------------------------------------------------------------------------------- /src/xmlsec/constants.pyi: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import NamedTuple 3 | 4 | if sys.version_info >= (3, 8): 5 | from typing import Final 6 | else: 7 | from typing_extensions import Final 8 | 9 | class __KeyData(NamedTuple): # __KeyData type 10 | href: str 11 | name: str 12 | 13 | class __KeyDataNoHref(NamedTuple): # __KeyData type 14 | href: None 15 | name: str 16 | 17 | class __Transform(NamedTuple): # __Transform type 18 | href: str 19 | name: str 20 | usage: int 21 | 22 | class __TransformNoHref(NamedTuple): # __Transform type 23 | href: None 24 | name: str 25 | usage: int 26 | 27 | DSigNs: Final[str] 28 | EncNs: Final[str] 29 | KeyDataAes: Final[__KeyData] 30 | KeyDataDes: Final[__KeyData] 31 | KeyDataDsa: Final[__KeyData] 32 | KeyDataEc: Final[__KeyData] 33 | KeyDataEcdsa: Final[__KeyData] 34 | KeyDataEncryptedKey: Final[__KeyData] 35 | KeyDataFormatBinary: Final[int] 36 | KeyDataFormatCertDer: Final[int] 37 | KeyDataFormatCertPem: Final[int] 38 | KeyDataFormatDer: Final[int] 39 | KeyDataFormatPem: Final[int] 40 | KeyDataFormatPkcs12: Final[int] 41 | KeyDataFormatPkcs8Der: Final[int] 42 | KeyDataFormatPkcs8Pem: Final[int] 43 | KeyDataFormatUnknown: Final[int] 44 | KeyDataHmac: Final[__KeyData] 45 | KeyDataName: Final[__KeyDataNoHref] 46 | KeyDataRawX509Cert: Final[__KeyData] 47 | KeyDataRetrievalMethod: Final[__KeyDataNoHref] 48 | KeyDataRsa: Final[__KeyData] 49 | KeyDataTypeAny: Final[int] 50 | KeyDataTypeNone: Final[int] 51 | KeyDataTypePermanent: Final[int] 52 | KeyDataTypePrivate: Final[int] 53 | KeyDataTypePublic: Final[int] 54 | KeyDataTypeSession: Final[int] 55 | KeyDataTypeSymmetric: Final[int] 56 | KeyDataTypeTrusted: Final[int] 57 | KeyDataTypeUnknown: Final[int] 58 | KeyDataValue: Final[__KeyDataNoHref] 59 | KeyDataX509: Final[__KeyData] 60 | NodeCanonicalizationMethod: Final[str] 61 | NodeCipherData: Final[str] 62 | NodeCipherReference: Final[str] 63 | NodeCipherValue: Final[str] 64 | NodeDataReference: Final[str] 65 | NodeDigestMethod: Final[str] 66 | NodeDigestValue: Final[str] 67 | NodeEncryptedData: Final[str] 68 | NodeEncryptedKey: Final[str] 69 | NodeEncryptionMethod: Final[str] 70 | NodeEncryptionProperties: Final[str] 71 | NodeEncryptionProperty: Final[str] 72 | NodeKeyInfo: Final[str] 73 | NodeKeyName: Final[str] 74 | NodeKeyReference: Final[str] 75 | NodeKeyValue: Final[str] 76 | NodeManifest: Final[str] 77 | NodeObject: Final[str] 78 | NodeReference: Final[str] 79 | NodeReferenceList: Final[str] 80 | NodeSignature: Final[str] 81 | NodeSignatureMethod: Final[str] 82 | NodeSignatureProperties: Final[str] 83 | NodeSignatureValue: Final[str] 84 | NodeSignedInfo: Final[str] 85 | NodeX509Data: Final[str] 86 | Ns: Final[str] 87 | NsExcC14N: Final[str] 88 | NsExcC14NWithComments: Final[str] 89 | TransformAes128Cbc: Final[__Transform] 90 | TransformAes128Gcm: Final[__Transform] 91 | TransformAes192Cbc: Final[__Transform] 92 | TransformAes192Gcm: Final[__Transform] 93 | TransformAes256Cbc: Final[__Transform] 94 | TransformAes256Gcm: Final[__Transform] 95 | TransformDes3Cbc: Final[__Transform] 96 | TransformDsaSha1: Final[__Transform] 97 | TransformEcdsaSha1: Final[__Transform] 98 | TransformEcdsaSha224: Final[__Transform] 99 | TransformEcdsaSha256: Final[__Transform] 100 | TransformEcdsaSha384: Final[__Transform] 101 | TransformEcdsaSha512: Final[__Transform] 102 | TransformEnveloped: Final[__Transform] 103 | TransformExclC14N: Final[__Transform] 104 | TransformExclC14NWithComments: Final[__Transform] 105 | TransformHmacMd5: Final[__Transform] 106 | TransformHmacRipemd160: Final[__Transform] 107 | TransformHmacSha1: Final[__Transform] 108 | TransformHmacSha224: Final[__Transform] 109 | TransformHmacSha256: Final[__Transform] 110 | TransformHmacSha384: Final[__Transform] 111 | TransformHmacSha512: Final[__Transform] 112 | TransformInclC14N: Final[__Transform] 113 | TransformInclC14N11: Final[__Transform] 114 | TransformInclC14N11WithComments: Final[__Transform] 115 | TransformInclC14NWithComments: Final[__Transform] 116 | TransformKWAes128: Final[__Transform] 117 | TransformKWAes192: Final[__Transform] 118 | TransformKWAes256: Final[__Transform] 119 | TransformKWDes3: Final[__Transform] 120 | TransformMd5: Final[__Transform] 121 | TransformRemoveXmlTagsC14N: Final[__TransformNoHref] 122 | TransformRipemd160: Final[__Transform] 123 | TransformRsaMd5: Final[__Transform] 124 | TransformRsaOaep: Final[__Transform] 125 | TransformRsaPkcs1: Final[__Transform] 126 | TransformRsaRipemd160: Final[__Transform] 127 | TransformRsaSha1: Final[__Transform] 128 | TransformRsaSha224: Final[__Transform] 129 | TransformRsaSha256: Final[__Transform] 130 | TransformRsaSha384: Final[__Transform] 131 | TransformRsaSha512: Final[__Transform] 132 | TransformSha1: Final[__Transform] 133 | TransformSha224: Final[__Transform] 134 | TransformSha256: Final[__Transform] 135 | TransformSha384: Final[__Transform] 136 | TransformSha512: Final[__Transform] 137 | TransformUsageAny: Final[int] 138 | TransformUsageC14NMethod: Final[int] 139 | TransformUsageDSigTransform: Final[int] 140 | TransformUsageDigestMethod: Final[int] 141 | TransformUsageEncryptionMethod: Final[int] 142 | TransformUsageSignatureMethod: Final[int] 143 | TransformUsageUnknown: Final[int] 144 | TransformVisa3DHack: Final[__TransformNoHref] 145 | TransformXPath: Final[__Transform] 146 | TransformXPath2: Final[__Transform] 147 | TransformXPointer: Final[__Transform] 148 | TransformXslt: Final[__Transform] 149 | TypeEncContent: Final[str] 150 | TypeEncElement: Final[str] 151 | XPath2Ns: Final[str] 152 | XPathNs: Final[str] 153 | XPointerNs: Final[str] 154 | -------------------------------------------------------------------------------- /src/xmlsec/py.typed: -------------------------------------------------------------------------------- 1 | # Marker file for PEP 561. The xmlsec package uses stub files. 2 | -------------------------------------------------------------------------------- /src/xmlsec/template.pyi: -------------------------------------------------------------------------------- 1 | from collections.abc import Sequence 2 | from typing import Any 3 | 4 | from lxml.etree import _Element 5 | 6 | from xmlsec.constants import __Transform as Transform 7 | 8 | def add_encrypted_key( 9 | node: _Element, method: Transform, id: str | None = ..., type: str | None = ..., recipient: str | None = ... 10 | ) -> _Element: ... 11 | def add_key_name(node: _Element, name: str | None = ...) -> _Element: ... 12 | def add_key_value(node: _Element) -> _Element: ... 13 | def add_reference( 14 | node: _Element, digest_method: Transform, id: str | None = ..., uri: str | None = ..., type: str | None = ... 15 | ) -> _Element: ... 16 | def add_transform(node: _Element, transform: Transform) -> Any: ... 17 | def add_x509_data(node: _Element) -> _Element: ... 18 | def create(node: _Element, c14n_method: Transform, sign_method: Transform) -> _Element: ... 19 | def encrypted_data_create( 20 | node: _Element, 21 | method: Transform, 22 | id: str | None = ..., 23 | type: str | None = ..., 24 | mime_type: str | None = ..., 25 | encoding: str | None = ..., 26 | ns: str | None = ..., 27 | ) -> _Element: ... 28 | def encrypted_data_ensure_cipher_value(node: _Element) -> _Element: ... 29 | def encrypted_data_ensure_key_info(node: _Element, id: str | None = ..., ns: str | None = ...) -> _Element: ... 30 | def ensure_key_info(node: _Element, id: str | None = ...) -> _Element: ... 31 | def transform_add_c14n_inclusive_namespaces(node: _Element, prefixes: str | Sequence[str]) -> None: ... 32 | def x509_data_add_certificate(node: _Element) -> _Element: ... 33 | def x509_data_add_crl(node: _Element) -> _Element: ... 34 | def x509_data_add_issuer_serial(node: _Element) -> _Element: ... 35 | def x509_data_add_ski(node: _Element) -> _Element: ... 36 | def x509_data_add_subject_name(node: _Element) -> _Element: ... 37 | def x509_issuer_serial_add_issuer_name(node: _Element, name: str | None = ...) -> _Element: ... 38 | def x509_issuer_serial_add_serial_number(node: _Element, serial: str | None = ...) -> _Element: ... 39 | -------------------------------------------------------------------------------- /src/xmlsec/tree.pyi: -------------------------------------------------------------------------------- 1 | from collections.abc import Sequence 2 | from typing import overload 3 | 4 | from lxml.etree import _Element 5 | 6 | def add_ids(node: _Element, ids: Sequence[str]) -> None: ... 7 | @overload 8 | def find_child(parent: _Element, name: str) -> _Element | None: ... 9 | @overload 10 | def find_child(parent: _Element, name: str, namespace: str = ...) -> _Element | None: ... 11 | @overload 12 | def find_node(node: _Element, name: str) -> _Element | None: ... 13 | @overload 14 | def find_node(node: _Element, name: str, namespace: str = ...) -> _Element | None: ... 15 | @overload 16 | def find_parent(node: _Element, name: str) -> _Element | None: ... 17 | @overload 18 | def find_parent(node: _Element, name: str, namespace: str = ...) -> _Element | None: ... 19 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmlsec/python-xmlsec/967754b6ec084d3ee5baa73ad256cf19f0f231de/tests/__init__.py -------------------------------------------------------------------------------- /tests/base.py: -------------------------------------------------------------------------------- 1 | import gc 2 | import os 3 | import sys 4 | import unittest 5 | 6 | from lxml import etree 7 | 8 | import xmlsec 9 | 10 | etype = type(etree.Element('test')) 11 | 12 | ns = {'dsig': xmlsec.constants.DSigNs, 'enc': xmlsec.constants.EncNs} 13 | 14 | 15 | try: 16 | import resource 17 | 18 | test_iterations = int(os.environ.get('PYXMLSEC_TEST_ITERATIONS', '10')) 19 | except (ImportError, ValueError): 20 | test_iterations = 0 21 | 22 | 23 | class TestMemoryLeaks(unittest.TestCase): 24 | maxDiff = None 25 | 26 | iterations = test_iterations 27 | 28 | data_dir = os.path.join(os.path.dirname(__file__), "data") 29 | 30 | def setUp(self): 31 | gc.disable() 32 | self.addTypeEqualityFunc(etype, "assertXmlEqual") 33 | xmlsec.enable_debug_trace(1) 34 | 35 | def run(self, result=None): 36 | # run first time 37 | super(TestMemoryLeaks, self).run(result=result) 38 | if self.iterations == 0: 39 | return 40 | 41 | m_usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 42 | o_count = gc.get_count()[0] 43 | m_hits = 0 44 | o_hits = 0 45 | for _ in range(self.iterations): 46 | super(TestMemoryLeaks, self).run(result=result) 47 | m_usage_n = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 48 | if m_usage_n > m_usage: 49 | m_usage = m_usage_n 50 | m_hits += 1 51 | o_count_n = gc.get_count()[0] 52 | if o_count_n > o_count: 53 | o_count = o_count_n 54 | o_hits += 1 55 | del m_usage_n 56 | del o_count_n 57 | 58 | if m_hits > int(self.iterations * 0.8): 59 | result.buffer = False 60 | try: 61 | raise AssertionError("memory leak detected") 62 | except AssertionError: 63 | result.addError(self, sys.exc_info()) 64 | if o_hits > int(self.iterations * 0.8): 65 | result.buffer = False 66 | try: 67 | raise AssertionError("unreferenced objects detected") 68 | except AssertionError: 69 | result.addError(self, sys.exc_info()) 70 | 71 | def path(self, name): 72 | """Return full path for resource.""" 73 | return os.path.join(self.data_dir, name) 74 | 75 | def load(self, name): 76 | """Load resource by name.""" 77 | with open(self.path(name), "rb") as stream: 78 | return stream.read() 79 | 80 | def load_xml(self, name, xpath=None): 81 | """Return xml.etree.""" 82 | with open(self.path(name)) as f: 83 | root = etree.parse(f).getroot() 84 | if xpath is None: 85 | return root 86 | return root.find(xpath) 87 | 88 | def dump(self, root): 89 | print(etree.tostring(root)) 90 | 91 | def assertXmlEqual(self, first, second, msg=None): # noqa: N802 92 | """Check equality of etree.roots.""" 93 | msg = msg or '' 94 | if first.tag != second.tag: 95 | self.fail('Tags do not match: {} and {}. {}'.format(first.tag, second.tag, msg)) 96 | for name, value in first.attrib.items(): 97 | if second.attrib.get(name) != value: 98 | self.fail('Attributes do not match: {}={!r}, {}={!r}. {}'.format(name, value, name, second.attrib.get(name), msg)) 99 | for name in second.attrib.keys(): 100 | if name not in first.attrib: 101 | self.fail('x2 has an attribute x1 is missing: {}. {}'.format(name, msg)) 102 | if not _xml_text_compare(first.text, second.text): 103 | self.fail('text: {!r} != {!r}. {}'.format(first.text, second.text, msg)) 104 | if not _xml_text_compare(first.tail, second.tail): 105 | self.fail('tail: {!r} != {!r}. {}'.format(first.tail, second.tail, msg)) 106 | cl1 = sorted(first.getchildren(), key=lambda x: x.tag) 107 | cl2 = sorted(second.getchildren(), key=lambda x: x.tag) 108 | if len(cl1) != len(cl2): 109 | self.fail('children length differs, {} != {}. {}'.format(len(cl1), len(cl2), msg)) 110 | i = 0 111 | for c1, c2 in zip(cl1, cl2): 112 | i += 1 113 | self.assertXmlEqual(c1, c2) 114 | 115 | 116 | def _xml_text_compare(t1, t2): 117 | if not t1 and not t2: 118 | return True 119 | if t1 == '*' or t2 == '*': 120 | return True 121 | return (t1 or '').strip() == (t2 or '').strip() 122 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | def pytest_collection_modifyitems(items): 2 | """ 3 | Put the module init test first. 4 | 5 | This way, we implicitly check whether any subsequent test fails because of module reinitialization. 6 | """ 7 | 8 | def module_init_tests_first(item): 9 | return int('test_xmlsec.py::TestModule::test_reinitialize_module' not in item.nodeid) 10 | 11 | items.sort(key=module_init_tests_first) 12 | -------------------------------------------------------------------------------- /tests/data/deskey.bin: -------------------------------------------------------------------------------- 1 | 012345670123456701234567 2 | -------------------------------------------------------------------------------- /tests/data/doc.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Hello, World! 7 | 8 | -------------------------------------------------------------------------------- /tests/data/dsacert.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmlsec/python-xmlsec/967754b6ec084d3ee5baa73ad256cf19f0f231de/tests/data/dsacert.der -------------------------------------------------------------------------------- /tests/data/dsakey.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmlsec/python-xmlsec/967754b6ec084d3ee5baa73ad256cf19f0f231de/tests/data/dsakey.der -------------------------------------------------------------------------------- /tests/data/enc1-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Hello, World! 7 | 8 | -------------------------------------------------------------------------------- /tests/data/enc1-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | UrTgE0UxQa8xevs4SyRA0rsibEz/ZFDjCBD+t4pKSdajB/cefYObZzqq2l41Q6R/ 10 | tqYLht5hEBh26AHfjmQSJAL+eChXOt/EaOf63zzJedO90HGqIQyzOeOPURAl3Li8 11 | ivPyLVyocJDeVNeh7W+7kYwpFQ6PLuQxWsFFQXVoRAWbXHpZkSzVheR+5RpYJRTb 12 | 1UYXKxu8jg4NqbjucVMDIxUOzsVCDRyk8R8sQrM7D/H/N0y7DAY8oX/WZ45xLwUy 13 | DY/U86tTpTn95NwHD10SLyrL6rpXdbEuoIQHhWLwV9uQxnJA/Pn1KZ+xXK/fePfP 14 | 26PBo/hUrN5pm5U8ycc4iw== 15 | 16 | 17 | 18 | 19 | 2pb5Mxd0f+AW56Cs3MfQ9HJkUVeliSi1hVCNCVHTKeMyC2VL6lPhQ9+L01aSeTSY 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/enc2-in.xml: -------------------------------------------------------------------------------- 1 | 2 | test 3 | -------------------------------------------------------------------------------- /tests/data/enc2-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | HJwrfL7kOIB0QaldMJdza1HitpLCjw+eoult1C6yExDXJ09zKaSQER+pUL9Vt5fm 10 | d4Oitsf0CUNkjG1xWJdFsftqUIuvYGnkUNhT0vtqoYbdhJkCcB9cCwvTrww2+VTF 11 | NIasTdechlSD1qQOR8uf6+S94Ae4PVSfWU+5YLTJFpMjR+OT7f6BSbYNv1By6Cko 12 | G39WTSKTRcVDzcMxRepAGb59r508yKIJhwabCf3Opu+Ams7ia7BH4oa4ro9YSWwm 13 | hAJ0CN4a6b5odcRbNvuHcwWSxpoysWKbOROQ0H4xC4nGZeL/AXlpSc8eNuNG+g6D 14 | CTBwsOXCAEJYXPkTrnB3qQ== 15 | 16 | 17 | 18 | 19 | 4m5BRKEswOe8JISY7NrPGLBYv7Ay5pBV+nG6it51gz0= 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/enc3-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | test 4 | 5 | -------------------------------------------------------------------------------- /tests/data/enc3-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HJwrfL7kOIB0QaldMJdza1HitpLCjw+eoult1C6yExDXJ09zKaSQER+pUL9Vt5fm 7 | d4Oitsf0CUNkjG1xWJdFsftqUIuvYGnkUNhT0vtqoYbdhJkCcB9cCwvTrww2+VTF 8 | NIasTdechlSD1qQOR8uf6+S94Ae4PVSfWU+5YLTJFpMjR+OT7f6BSbYNv1By6Cko 9 | G39WTSKTRcVDzcMxRepAGb59r508yKIJhwabCf3Opu+Ams7ia7BH4oa4ro9YSWwm 10 | hAJ0CN4a6b5odcRbNvuHcwWSxpoysWKbOROQ0H4xC4nGZeL/AXlpSc8eNuNG+g6D 11 | CTBwsOXCAEJYXPkTrnB3qQ== 12 | 13 | 14 | 15 | 16 | 17 | 4m5BRKEswOe8JISY7NrPGLBYv7Ay5pBV+nG6it51gz0= 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/data/enc_template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/rsacert.pem: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 5 (0x5) 5 | Signature Algorithm: md5WithRSAEncryption 6 | Issuer: C=US, ST=California, L=Sunnyvale, O=XML Security Library (http://www.aleksey.com/xmlsec), OU=Root Certificate, CN=Aleksey Sanin/emailAddress=xmlsec@aleksey.com 7 | Validity 8 | Not Before: Mar 31 04:02:22 2003 GMT 9 | Not After : Mar 28 04:02:22 2013 GMT 10 | Subject: C=US, ST=California, O=XML Security Library (http://www.aleksey.com/xmlsec), OU=Examples RSA Certificate, CN=Aleksey Sanin/emailAddress=xmlsec@aleksey.com 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (2048 bit) 14 | Modulus (2048 bit): 15 | 00:97:b8:fe:b4:3f:83:35:78:16:89:04:ec:2b:61: 16 | 8c:bf:c4:5f:00:81:4a:45:e6:d9:cd:e9:e2:3c:97: 17 | 3b:45:ad:aa:e6:8d:0b:77:71:07:01:4f:7c:f9:7d: 18 | e2:19:aa:dd:91:59:f4:f1:cf:3d:ba:78:46:96:11: 19 | 9c:b6:5b:46:39:73:55:23:aa:f7:9e:00:5c:e5:e9: 20 | 49:ec:3b:9c:3f:84:99:3a:90:ad:df:7e:64:86:c6: 21 | 26:72:ce:31:08:79:7e:13:15:b8:e5:bf:d6:56:02: 22 | 8d:60:21:4c:27:18:64:fb:fb:55:70:f6:33:bd:2f: 23 | 55:70:d5:5e:7e:99:ae:a4:e0:aa:45:47:13:a8:30: 24 | d5:a0:8a:9d:cc:20:ec:e4:8e:51:c9:54:c5:7f:3e: 25 | 66:2d:74:bf:a3:7a:f8:f3:ec:94:57:39:b4:ac:00: 26 | 75:62:61:54:b4:d0:e0:52:86:f8:5e:77:ec:50:43: 27 | 9c:d2:ba:a7:8c:62:5a:bc:b2:fe:f3:cc:62:7e:23: 28 | 60:6b:c7:51:49:37:78:7e:25:15:30:ab:fa:b4:ae: 29 | 25:8f:22:fc:a3:48:7f:f2:0a:8a:6e:e0:fe:8d:f0: 30 | 01:ed:c6:33:cc:6b:a1:fd:a6:80:ef:06:8c:af:f6: 31 | 40:3a:8e:42:14:20:61:12:1f:e3:fc:05:b1:05:d5: 32 | 65:c3 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Basic Constraints: 36 | CA:FALSE 37 | Netscape Comment: 38 | OpenSSL Generated Certificate 39 | X509v3 Subject Key Identifier: 40 | 24:84:2C:F2:D4:59:20:62:8B:2E:5C:86:90:A3:AA:30:BA:27:1A:9C 41 | X509v3 Authority Key Identifier: 42 | keyid:B4:B9:EF:9A:E6:97:0E:68:65:1E:98:CE:FA:55:0D:89:06:DB:4C:7C 43 | DirName:/C=US/ST=California/L=Sunnyvale/O=XML Security Library (http://www.aleksey.com/xmlsec)/OU=Root Certificate/CN=Aleksey Sanin/emailAddress=xmlsec@aleksey.com 44 | serial:00 45 | 46 | Signature Algorithm: md5WithRSAEncryption 47 | b5:3f:9b:32:31:4a:ff:2f:84:3b:a8:9b:11:5c:a6:5c:f0:76: 48 | 52:d9:6e:f4:90:ad:fa:0d:90:c1:98:d5:4a:12:dd:82:6b:37: 49 | e8:d9:2d:62:92:c9:61:37:98:86:8f:a4:49:6a:5e:25:d0:18: 50 | 69:30:0f:98:8f:43:58:89:31:b2:3b:05:e2:ef:c7:a6:71:5f: 51 | f7:fe:73:c5:a7:b2:cd:2e:73:53:71:7d:a8:4c:68:1a:32:1b: 52 | 5e:48:2f:8f:9b:7a:a3:b5:f3:67:e8:b1:a2:89:4e:b2:4d:1b: 53 | 79:9c:ff:f0:0d:19:4f:4e:b1:03:3d:99:f0:44:b7:8a:0b:34: 54 | 9d:83 55 | -----BEGIN CERTIFICATE----- 56 | MIIE3zCCBEigAwIBAgIBBTANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCVVMx 57 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 58 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 59 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 60 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMB4X 61 | DTAzMDMzMTA0MDIyMloXDTEzMDMyODA0MDIyMlowgb8xCzAJBgNVBAYTAlVTMRMw 62 | EQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFy 63 | eSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMSEwHwYDVQQLExhFeGFt 64 | cGxlcyBSU0EgQ2VydGlmaWNhdGUxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf 65 | BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCCASIwDQYJKoZIhvcNAQEB 66 | BQADggEPADCCAQoCggEBAJe4/rQ/gzV4FokE7CthjL/EXwCBSkXm2c3p4jyXO0Wt 67 | quaNC3dxBwFPfPl94hmq3ZFZ9PHPPbp4RpYRnLZbRjlzVSOq954AXOXpSew7nD+E 68 | mTqQrd9+ZIbGJnLOMQh5fhMVuOW/1lYCjWAhTCcYZPv7VXD2M70vVXDVXn6ZrqTg 69 | qkVHE6gw1aCKncwg7OSOUclUxX8+Zi10v6N6+PPslFc5tKwAdWJhVLTQ4FKG+F53 70 | 7FBDnNK6p4xiWryy/vPMYn4jYGvHUUk3eH4lFTCr+rSuJY8i/KNIf/IKim7g/o3w 71 | Ae3GM8xrof2mgO8GjK/2QDqOQhQgYRIf4/wFsQXVZcMCAwEAAaOCAVcwggFTMAkG 72 | A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp 73 | ZmljYXRlMB0GA1UdDgQWBBQkhCzy1FkgYosuXIaQo6owuicanDCB+AYDVR0jBIHw 74 | MIHtgBS0ue+a5pcOaGUemM76VQ2JBttMfKGB0aSBzjCByzELMAkGA1UEBhMCVVMx 75 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 76 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 77 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 78 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggEA 79 | MA0GCSqGSIb3DQEBBAUAA4GBALU/mzIxSv8vhDuomxFcplzwdlLZbvSQrfoNkMGY 80 | 1UoS3YJrN+jZLWKSyWE3mIaPpElqXiXQGGkwD5iPQ1iJMbI7BeLvx6ZxX/f+c8Wn 81 | ss0uc1NxfahMaBoyG15IL4+beqO182fosaKJTrJNG3mc//ANGU9OsQM9mfBEt4oL 82 | NJ2D 83 | -----END CERTIFICATE----- 84 | -------------------------------------------------------------------------------- /tests/data/rsakey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAl7j+tD+DNXgWiQTsK2GMv8RfAIFKRebZzeniPJc7Ra2q5o0L 3 | d3EHAU98+X3iGardkVn08c89unhGlhGctltGOXNVI6r3ngBc5elJ7DucP4SZOpCt 4 | 335khsYmcs4xCHl+ExW45b/WVgKNYCFMJxhk+/tVcPYzvS9VcNVefpmupOCqRUcT 5 | qDDVoIqdzCDs5I5RyVTFfz5mLXS/o3r48+yUVzm0rAB1YmFUtNDgUob4XnfsUEOc 6 | 0rqnjGJavLL+88xifiNga8dRSTd4fiUVMKv6tK4ljyL8o0h/8gqKbuD+jfAB7cYz 7 | zGuh/aaA7waMr/ZAOo5CFCBhEh/j/AWxBdVlwwIDAQABAoIBAQCAvt6DnZF9gdW9 8 | l4vAlBqXb88d4phgELCp5tmviLUnP2NSGEWuqR7Eoeru2z9NgIxblvYfazh6Ty22 9 | kmNk6rcAcTnB9oYAcVZjUj8EUuEXlTFhXPvuNpafNu3RZd59znqJP1mSu+LpQWku 10 | NZMlabHnkTLDlGf7FXtvL9/rlgV4qk3QcDVF793JFszWrtK3mnld3KHQ6cuo9iSm 11 | 0rQKtkDjeHsRell8qTQvfBsgG1q2bv8QWT45/eQrra9mMbGTr3DbnXvoeJmTj1VN 12 | XJV7tBNllxxPahlYMByJaf/Tuva5j6HWUEIfYky5ihr2z1P/fNQ2OSCM6SQHpkiG 13 | EXQDueXBAoGBAMfW7KcmToEQEcTiqfey6C1LOLoemcX0/ROUktPq/5JQJRRrT4t7 14 | XevLX0ed8sLyR5T29XQtdnuV0DJfvcJD+6ZwfOcQ+f6ZzCaNXJP97JtEt5kSWY01 15 | Ei+nphZ0RFvPb04V3qDU9dElU26GR36CRBYJyM2WQPx4v+/YyDSZH9kLAoGBAMJc 16 | ZBU8pRbIia/FFOHUlS3v5P18nVmXyOd0fvRq0ZelaQCebTZ4K9wjnCfw//yzkb2Z 17 | 0vZFNB+xVBKB0Pt6nVvnSNzxdQ8EAXVFwHtXa25FUyP2RERQgTvmajqmgWjZsDYp 18 | 6GHcK3ZhmdmscQHF/Q2Uo4scvBcheahm9IXiNskpAoGAXelEgTBhSAmTMCEMmti6 19 | fz6QQ/bJcNu2apMxhOE0hT+gjT34vaWV9481EWTKho5w0TJVGumaem1mz6VqeXaV 20 | Nhw6tiOmN91ysNNRpEJ6BGWAmjCjYNaF21s/k+HDlhmfRuTEIHSzqDuQP6pewrbY 21 | 5Dpo4SQxGfRsznvjacRj0Q0CgYBN247oBvQnDUxCkhNMZ8kersOvW5T4x9neBge5 22 | R3UQZ12Jtu0O7dK8C7PJODyDcTeHmTAuIQjBTVrdUw1xP+v7XcoNX9hBnJws6zUw 23 | 85MAiFrGxCcSqqEqaqHRPtQGOXXiLKV/ViA++tgTn4VhbXtyTkG5P1iFd45xjFSV 24 | sUm7CQKBgDn92tHxzePly1L1mK584TkVryx4cP9RFHpebnmNduGwwjnRuYipoj8y 25 | pPPAkVbbaA3f9OB2go48rN0Ft9nHdlqgh9BpIKCVtkIb1XN0K3Oa/8BW8W/GAiNG 26 | HJcsrOtIrGVRdlyJG6bDaN8T49DnhOcsqMbf+IkIvfh50VeE9L/e 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/data/rsapub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl7j+tD+DNXgWiQTsK2GM 3 | v8RfAIFKRebZzeniPJc7Ra2q5o0Ld3EHAU98+X3iGardkVn08c89unhGlhGctltG 4 | OXNVI6r3ngBc5elJ7DucP4SZOpCt335khsYmcs4xCHl+ExW45b/WVgKNYCFMJxhk 5 | +/tVcPYzvS9VcNVefpmupOCqRUcTqDDVoIqdzCDs5I5RyVTFfz5mLXS/o3r48+yU 6 | Vzm0rAB1YmFUtNDgUob4XnfsUEOc0rqnjGJavLL+88xifiNga8dRSTd4fiUVMKv6 7 | tK4ljyL8o0h/8gqKbuD+jfAB7cYzzGuh/aaA7waMr/ZAOo5CFCBhEh/j/AWxBdVl 8 | wwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /tests/data/sign1-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/data/sign1-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 9H/rQr2Axe9hYTV2n/tCp+3UIQQ= 19 | 20 | 21 | Mx4psIy9/UY+u8QBJRDrwQWKRaCGz0WOVftyDzAe6WHAFSjMNr7qb2ojq9kdipT8 22 | Oub5q2OQ7mzdSLiiejkrO1VeqM/90yEIGI4En6KEB6ArEzw+iq4N1wm6EptcyxXx 23 | M9StAOOa9ilWYqR9Tfx3SW1urUIuKYgUitxsONiUHBVaW6HeX51bsXoTF++4ZI+D 24 | jiPBjN4HHmr0cbJ6BXk91S27ffZIfp1Qj5nL9onFLUGbR6EFgu2luiRzQbPuM2tP 25 | XxyI7GZ8AfHnRJK28ARvBC9oi+O1ej20S79CIV7gdBxbLbFprozBHAwOEC57YgJc 26 | x+YEjSjcO7SBIR1FiUA7pw== 27 | 28 | rsakey.pem 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/data/sign2-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/sign2-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | HjY8ilZAIEM2tBbPn5mYO1ieIX4= 19 | 20 | 21 | SIaj/6KY3C1SmDXU2++Gm31U1xTadFp04WhBgfsJFbxrL+q7GKSKN9kfQ+UpN9+i 22 | D5fWmuavXEHe4Gw6RMaMEkq2URQo7F68+d5J/ajq8/l4n+xE6/reGScVwT6L4dEP 23 | XXVJcAi2ZnQ3O7GTNvNGCPibL9mUcyCWBFZ92Uemtc/vJFCQ7ZyKMdMfACgxOwyN 24 | T/9971oog241/2doudhonc0I/3mgPYWkZdX6yvr62mEjnG+oUZkhWYJ4ewZJ4hM4 25 | JjbFqZO+OEzDRSbw3DkmuBA/mtlx+3t13SESfEub5hqoMdVmtth/eTb64dsPdl9r 26 | 3k1ACVX9f8aHfQQdJOmLFQ== 27 | 28 | rsakey.pem 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/data/sign3-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/sign3-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | HjY8ilZAIEM2tBbPn5mYO1ieIX4= 19 | 20 | 21 | SIaj/6KY3C1SmDXU2++Gm31U1xTadFp04WhBgfsJFbxrL+q7GKSKN9kfQ+UpN9+i 22 | D5fWmuavXEHe4Gw6RMaMEkq2URQo7F68+d5J/ajq8/l4n+xE6/reGScVwT6L4dEP 23 | XXVJcAi2ZnQ3O7GTNvNGCPibL9mUcyCWBFZ92Uemtc/vJFCQ7ZyKMdMfACgxOwyN 24 | T/9971oog241/2doudhonc0I/3mgPYWkZdX6yvr62mEjnG+oUZkhWYJ4ewZJ4hM4 25 | JjbFqZO+OEzDRSbw3DkmuBA/mtlx+3t13SESfEub5hqoMdVmtth/eTb64dsPdl9r 26 | 3k1ACVX9f8aHfQQdJOmLFQ== 27 | 28 | 29 | MIIE3zCCBEigAwIBAgIBBTANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCVVMx 30 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 31 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 32 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 33 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMB4X 34 | DTAzMDMzMTA0MDIyMloXDTEzMDMyODA0MDIyMlowgb8xCzAJBgNVBAYTAlVTMRMw 35 | EQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFy 36 | eSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMSEwHwYDVQQLExhFeGFt 37 | cGxlcyBSU0EgQ2VydGlmaWNhdGUxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf 38 | BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCCASIwDQYJKoZIhvcNAQEB 39 | BQADggEPADCCAQoCggEBAJe4/rQ/gzV4FokE7CthjL/EXwCBSkXm2c3p4jyXO0Wt 40 | quaNC3dxBwFPfPl94hmq3ZFZ9PHPPbp4RpYRnLZbRjlzVSOq954AXOXpSew7nD+E 41 | mTqQrd9+ZIbGJnLOMQh5fhMVuOW/1lYCjWAhTCcYZPv7VXD2M70vVXDVXn6ZrqTg 42 | qkVHE6gw1aCKncwg7OSOUclUxX8+Zi10v6N6+PPslFc5tKwAdWJhVLTQ4FKG+F53 43 | 7FBDnNK6p4xiWryy/vPMYn4jYGvHUUk3eH4lFTCr+rSuJY8i/KNIf/IKim7g/o3w 44 | Ae3GM8xrof2mgO8GjK/2QDqOQhQgYRIf4/wFsQXVZcMCAwEAAaOCAVcwggFTMAkG 45 | A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp 46 | ZmljYXRlMB0GA1UdDgQWBBQkhCzy1FkgYosuXIaQo6owuicanDCB+AYDVR0jBIHw 47 | MIHtgBS0ue+a5pcOaGUemM76VQ2JBttMfKGB0aSBzjCByzELMAkGA1UEBhMCVVMx 48 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 49 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 50 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 51 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggEA 52 | MA0GCSqGSIb3DQEBBAUAA4GBALU/mzIxSv8vhDuomxFcplzwdlLZbvSQrfoNkMGY 53 | 1UoS3YJrN+jZLWKSyWE3mIaPpElqXiXQGGkwD5iPQ1iJMbI7BeLvx6ZxX/f+c8Wn 54 | ss0uc1NxfahMaBoyG15IL4+beqO182fosaKJTrJNG3mc//ANGU9OsQM9mfBEt4oL 55 | NJ2D 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /tests/data/sign4-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/sign4-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello, World! 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ERS7F/ifxZoyTe0mhco+HeAEKGo= 16 | 17 | 18 | G+mZNFqh9w0wIkDSbuYwvVDu7CMP8PEsw7jfwiZBC8nyF3loAtYKKAkdi6Zy3dJs 19 | tU8qKfhzvabmCjdIrGkFTdtlCNCVKDMzwogFtxEX4Oh77X6jjx4b22XNJx4AbnUG 20 | JV/EcsD+po8s5qVEXw62lRRd8cMDafbzOA/rBH96CMNgZhzxyaF9VRLa/vbt1ht2 21 | hE1KkdZCB4Y0Lv3QyeDL2jax3NFks9FUv8IqoWYQSvywdMLY2ZMiQ9UpPeVfMizi 22 | trd5zDUSD/s3hyIEs4gD5NJF3HZPD/Fe2Zw1PBPj9eLADdEzcdueyCdPJ2YioFIi 23 | 1sMW/qPDhR/DoOJwGpUxwQ== 24 | 25 | 26 | MIIE3zCCBEigAwIBAgIBBTANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCVVMx 27 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 28 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 29 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 30 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMB4X 31 | DTAzMDMzMTA0MDIyMloXDTEzMDMyODA0MDIyMlowgb8xCzAJBgNVBAYTAlVTMRMw 32 | EQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFy 33 | eSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMSEwHwYDVQQLExhFeGFt 34 | cGxlcyBSU0EgQ2VydGlmaWNhdGUxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf 35 | BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCCASIwDQYJKoZIhvcNAQEB 36 | BQADggEPADCCAQoCggEBAJe4/rQ/gzV4FokE7CthjL/EXwCBSkXm2c3p4jyXO0Wt 37 | quaNC3dxBwFPfPl94hmq3ZFZ9PHPPbp4RpYRnLZbRjlzVSOq954AXOXpSew7nD+E 38 | mTqQrd9+ZIbGJnLOMQh5fhMVuOW/1lYCjWAhTCcYZPv7VXD2M70vVXDVXn6ZrqTg 39 | qkVHE6gw1aCKncwg7OSOUclUxX8+Zi10v6N6+PPslFc5tKwAdWJhVLTQ4FKG+F53 40 | 7FBDnNK6p4xiWryy/vPMYn4jYGvHUUk3eH4lFTCr+rSuJY8i/KNIf/IKim7g/o3w 41 | Ae3GM8xrof2mgO8GjK/2QDqOQhQgYRIf4/wFsQXVZcMCAwEAAaOCAVcwggFTMAkG 42 | A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp 43 | ZmljYXRlMB0GA1UdDgQWBBQkhCzy1FkgYosuXIaQo6owuicanDCB+AYDVR0jBIHw 44 | MIHtgBS0ue+a5pcOaGUemM76VQ2JBttMfKGB0aSBzjCByzELMAkGA1UEBhMCVVMx 45 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 46 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 47 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 48 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggEA 49 | MA0GCSqGSIb3DQEBBAUAA4GBALU/mzIxSv8vhDuomxFcplzwdlLZbvSQrfoNkMGY 50 | 1UoS3YJrN+jZLWKSyWE3mIaPpElqXiXQGGkwD5iPQ1iJMbI7BeLvx6ZxX/f+c8Wn 51 | ss0uc1NxfahMaBoyG15IL4+beqO182fosaKJTrJNG3mc//ANGU9OsQM9mfBEt4oL 52 | NJ2D 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /tests/data/sign5-in.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/data/sign5-out-xmlsec_1_2_36_to_37.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | HjY8ilZAIEM2tBbPn5mYO1ieIX4= 19 | 20 | 21 | SIaj/6KY3C1SmDXU2++Gm31U1xTadFp04WhBgfsJFbxrL+q7GKSKN9kfQ+UpN9+i 22 | D5fWmuavXEHe4Gw6RMaMEkq2URQo7F68+d5J/ajq8/l4n+xE6/reGScVwT6L4dEP 23 | XXVJcAi2ZnQ3O7GTNvNGCPibL9mUcyCWBFZ92Uemtc/vJFCQ7ZyKMdMfACgxOwyN 24 | T/9971oog241/2doudhonc0I/3mgPYWkZdX6yvr62mEjnG+oUZkhWYJ4ewZJ4hM4 25 | JjbFqZO+OEzDRSbw3DkmuBA/mtlx+3t13SESfEub5hqoMdVmtth/eTb64dsPdl9r 26 | 3k1ACVX9f8aHfQQdJOmLFQ== 27 | 28 | 29 | 30 | 31 | 32 | 33 | Test Issuer 34 | 1 35 | 36 | MIIE3zCCBEigAwIBAgIBBTANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCVVMx 37 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 38 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 39 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 40 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMB4X 41 | DTAzMDMzMTA0MDIyMloXDTEzMDMyODA0MDIyMlowgb8xCzAJBgNVBAYTAlVTMRMw 42 | EQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFy 43 | eSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMSEwHwYDVQQLExhFeGFt 44 | cGxlcyBSU0EgQ2VydGlmaWNhdGUxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf 45 | BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCCASIwDQYJKoZIhvcNAQEB 46 | BQADggEPADCCAQoCggEBAJe4/rQ/gzV4FokE7CthjL/EXwCBSkXm2c3p4jyXO0Wt 47 | quaNC3dxBwFPfPl94hmq3ZFZ9PHPPbp4RpYRnLZbRjlzVSOq954AXOXpSew7nD+E 48 | mTqQrd9+ZIbGJnLOMQh5fhMVuOW/1lYCjWAhTCcYZPv7VXD2M70vVXDVXn6ZrqTg 49 | qkVHE6gw1aCKncwg7OSOUclUxX8+Zi10v6N6+PPslFc5tKwAdWJhVLTQ4FKG+F53 50 | 7FBDnNK6p4xiWryy/vPMYn4jYGvHUUk3eH4lFTCr+rSuJY8i/KNIf/IKim7g/o3w 51 | Ae3GM8xrof2mgO8GjK/2QDqOQhQgYRIf4/wFsQXVZcMCAwEAAaOCAVcwggFTMAkG 52 | A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp 53 | ZmljYXRlMB0GA1UdDgQWBBQkhCzy1FkgYosuXIaQo6owuicanDCB+AYDVR0jBIHw 54 | MIHtgBS0ue+a5pcOaGUemM76VQ2JBttMfKGB0aSBzjCByzELMAkGA1UEBhMCVVMx 55 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 56 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 57 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 58 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggEA 59 | MA0GCSqGSIb3DQEBBAUAA4GBALU/mzIxSv8vhDuomxFcplzwdlLZbvSQrfoNkMGY 60 | 1UoS3YJrN+jZLWKSyWE3mIaPpElqXiXQGGkwD5iPQ1iJMbI7BeLvx6ZxX/f+c8Wn 61 | ss0uc1NxfahMaBoyG15IL4+beqO182fosaKJTrJNG3mc//ANGU9OsQM9mfBEt4oL 62 | NJ2D 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tests/data/sign5-out.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Hello, World! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | HjY8ilZAIEM2tBbPn5mYO1ieIX4= 19 | 20 | 21 | SIaj/6KY3C1SmDXU2++Gm31U1xTadFp04WhBgfsJFbxrL+q7GKSKN9kfQ+UpN9+i 22 | D5fWmuavXEHe4Gw6RMaMEkq2URQo7F68+d5J/ajq8/l4n+xE6/reGScVwT6L4dEP 23 | XXVJcAi2ZnQ3O7GTNvNGCPibL9mUcyCWBFZ92Uemtc/vJFCQ7ZyKMdMfACgxOwyN 24 | T/9971oog241/2doudhonc0I/3mgPYWkZdX6yvr62mEjnG+oUZkhWYJ4ewZJ4hM4 25 | JjbFqZO+OEzDRSbw3DkmuBA/mtlx+3t13SESfEub5hqoMdVmtth/eTb64dsPdl9r 26 | 3k1ACVX9f8aHfQQdJOmLFQ== 27 | 28 | 29 | 30 | 31 | 32 | 33 | Test Issuer 34 | 1 35 | 36 | MIIE3zCCBEigAwIBAgIBBTANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCVVMx 37 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 38 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 39 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 40 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMB4X 41 | DTAzMDMzMTA0MDIyMloXDTEzMDMyODA0MDIyMlowgb8xCzAJBgNVBAYTAlVTMRMw 42 | EQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFy 43 | eSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94bWxzZWMpMSEwHwYDVQQLExhFeGFt 44 | cGxlcyBSU0EgQ2VydGlmaWNhdGUxFjAUBgNVBAMTDUFsZWtzZXkgU2FuaW4xITAf 45 | BgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTCCASIwDQYJKoZIhvcNAQEB 46 | BQADggEPADCCAQoCggEBAJe4/rQ/gzV4FokE7CthjL/EXwCBSkXm2c3p4jyXO0Wt 47 | quaNC3dxBwFPfPl94hmq3ZFZ9PHPPbp4RpYRnLZbRjlzVSOq954AXOXpSew7nD+E 48 | mTqQrd9+ZIbGJnLOMQh5fhMVuOW/1lYCjWAhTCcYZPv7VXD2M70vVXDVXn6ZrqTg 49 | qkVHE6gw1aCKncwg7OSOUclUxX8+Zi10v6N6+PPslFc5tKwAdWJhVLTQ4FKG+F53 50 | 7FBDnNK6p4xiWryy/vPMYn4jYGvHUUk3eH4lFTCr+rSuJY8i/KNIf/IKim7g/o3w 51 | Ae3GM8xrof2mgO8GjK/2QDqOQhQgYRIf4/wFsQXVZcMCAwEAAaOCAVcwggFTMAkG 52 | A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp 53 | ZmljYXRlMB0GA1UdDgQWBBQkhCzy1FkgYosuXIaQo6owuicanDCB+AYDVR0jBIHw 54 | MIHtgBS0ue+a5pcOaGUemM76VQ2JBttMfKGB0aSBzjCByzELMAkGA1UEBhMCVVMx 55 | EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVN1bm55dmFsZTE9MDsGA1UE 56 | ChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20v 57 | eG1sc2VjKTEZMBcGA1UECxMQUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxl 58 | a3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tggEA 59 | MA0GCSqGSIb3DQEBBAUAA4GBALU/mzIxSv8vhDuomxFcplzwdlLZbvSQrfoNkMGY 60 | 1UoS3YJrN+jZLWKSyWE3mIaPpElqXiXQGGkwD5iPQ1iJMbI7BeLvx6ZxX/f+c8Wn 61 | ss0uc1NxfahMaBoyG15IL4+beqO182fosaKJTrJNG3mc//ANGU9OsQM9mfBEt4oL 62 | NJ2D 63 | emailAddress=xmlsec@aleksey.com,CN=Aleksey Sanin,OU=Examples RSA Certificate,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US 64 | JIQs8tRZIGKLLlyGkKOqMLonGpw= 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tests/data/sign6-in.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmlsec/python-xmlsec/967754b6ec084d3ee5baa73ad256cf19f0f231de/tests/data/sign6-in.bin -------------------------------------------------------------------------------- /tests/data/sign6-out.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmlsec/python-xmlsec/967754b6ec084d3ee5baa73ad256cf19f0f231de/tests/data/sign6-out.bin -------------------------------------------------------------------------------- /tests/data/sign_template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /tests/softhsm_setup.py: -------------------------------------------------------------------------------- 1 | """Testing the PKCS#11 shim layer. 2 | 3 | Heavily inspired by from https://github.com/IdentityPython/pyXMLSecurity by leifj 4 | under license "As is", see https://github.com/IdentityPython/pyXMLSecurity/blob/master/LICENSE.txt 5 | """ 6 | 7 | import logging 8 | import os 9 | import shutil 10 | import subprocess 11 | import tempfile 12 | import traceback 13 | import unittest 14 | 15 | DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') 16 | 17 | 18 | def paths_for_component(component: str, default_paths): 19 | env_path = os.environ.get(component) 20 | return [env_path] if env_path else default_paths 21 | 22 | 23 | def find_alts(component_name, alts) -> str: 24 | for a in alts: 25 | if os.path.exists(a): 26 | return a 27 | raise unittest.SkipTest('Required component is missing: {}'.format(component_name)) 28 | 29 | 30 | def run_cmd(args, softhsm_conf=None): 31 | env = {} 32 | if softhsm_conf is not None: 33 | env['SOFTHSM_CONF'] = softhsm_conf 34 | env['SOFTHSM2_CONF'] = softhsm_conf 35 | proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) 36 | out, err = proc.communicate() 37 | if err is not None and len(err) > 0: 38 | logging.error(err) 39 | if out is not None and len(out) > 0: 40 | logging.debug(out) 41 | rv = proc.wait() 42 | if rv: 43 | with open(softhsm_conf) as f: 44 | conf = f.read() 45 | msg = '[cmd: {cmd}] [code: {code}] [stdout: {out}] [stderr: {err}] [config: {conf}]' 46 | msg = msg.format( 47 | cmd=' '.join(args), 48 | code=rv, 49 | out=out.strip(), 50 | err=err.strip(), 51 | conf=conf, 52 | ) 53 | raise RuntimeError(msg) 54 | return out, err 55 | 56 | 57 | component_default_paths = { 58 | 'P11_MODULE': [ 59 | '/usr/lib/softhsm/libsofthsm2.so', 60 | '/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so', 61 | '/usr/lib/softhsm/libsofthsm.so', 62 | '/usr/lib64/softhsm/libsofthsm2.so', 63 | ], 64 | 'P11_ENGINE': [ 65 | '/usr/lib/ssl/engines/libpkcs11.so', 66 | '/usr/lib/engines/engine_pkcs11.so', 67 | '/usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so', 68 | '/usr/lib64/engines-1.1/pkcs11.so', 69 | '/usr/lib64/engines-1.1/libpkcs11.so', 70 | '/usr/lib64/engines-3/pkcs11.so', 71 | '/usr/lib64/engines-3/libpkcs11.so', 72 | '/usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so', 73 | '/usr/lib/x86_64-linux-gnu/engines-3/libpkcs11.so', 74 | ], 75 | 'PKCS11_TOOL': [ 76 | '/usr/bin/pkcs11-tool', 77 | ], 78 | 'SOFTHSM': [ 79 | '/usr/bin/softhsm2-util', 80 | '/usr/bin/softhsm', 81 | ], 82 | 'OPENSSL': [ 83 | '/usr/bin/openssl', 84 | ], 85 | } 86 | 87 | component_path = { 88 | component_name: find_alts(component_name, paths_for_component(component_name, default_paths)) 89 | for component_name, default_paths in component_default_paths.items() 90 | } 91 | 92 | softhsm_version = 1 93 | if component_path['SOFTHSM'].endswith('softhsm2-util'): 94 | softhsm_version = 2 95 | 96 | openssl_version = subprocess.check_output([component_path['OPENSSL'], 'version'])[8:11].decode() 97 | 98 | p11_test_files = [] 99 | softhsm_conf = None 100 | softhsm_db = None 101 | 102 | 103 | def _temp_file() -> str: 104 | f = tempfile.NamedTemporaryFile(delete=False) 105 | p11_test_files.append(f.name) 106 | return f.name 107 | 108 | 109 | def _temp_dir() -> str: 110 | d = tempfile.mkdtemp() 111 | p11_test_files.append(d) 112 | return d 113 | 114 | 115 | @unittest.skipIf(component_path['P11_MODULE'] is None, 'SoftHSM PKCS11 module not installed') 116 | def setup() -> None: 117 | logging.debug('Creating test pkcs11 token using softhsm') 118 | try: 119 | global softhsm_conf 120 | softhsm_conf = _temp_file() 121 | logging.debug('Generating softhsm.conf') 122 | with open(softhsm_conf, 'w') as f: 123 | if softhsm_version == 2: 124 | softhsm_db = _temp_dir() 125 | f.write( 126 | """ 127 | # Generated by test 128 | directories.tokendir = {} 129 | objectstore.backend = file 130 | log.level = DEBUG 131 | """.format( 132 | softhsm_db 133 | ) 134 | ) 135 | else: 136 | softhsm_db = _temp_file() 137 | f.write( 138 | """ 139 | # Generated by test 140 | 0:{} 141 | """.format( 142 | softhsm_db 143 | ) 144 | ) 145 | 146 | logging.debug('Initializing the token') 147 | out, err = run_cmd( 148 | [ 149 | component_path['SOFTHSM'], 150 | '--slot', 151 | '0', 152 | '--label', 153 | 'test', 154 | '--init-token', 155 | '--pin', 156 | 'secret1', 157 | '--so-pin', 158 | 'secret2', 159 | ], 160 | softhsm_conf=softhsm_conf, 161 | ) 162 | 163 | hash_priv_key = _temp_file() 164 | logging.debug('Converting test private key to format for softhsm') 165 | run_cmd( 166 | [ 167 | component_path['OPENSSL'], 168 | 'pkcs8', 169 | '-topk8', 170 | '-inform', 171 | 'PEM', 172 | '-outform', 173 | 'PEM', 174 | '-nocrypt', 175 | '-in', 176 | os.path.join(DATA_DIR, 'rsakey.pem'), 177 | '-out', 178 | hash_priv_key, 179 | ], 180 | softhsm_conf=softhsm_conf, 181 | ) 182 | 183 | logging.debug('Importing the test key to softhsm') 184 | run_cmd( 185 | [ 186 | component_path['SOFTHSM'], 187 | '--import', 188 | hash_priv_key, 189 | '--token', 190 | 'test', 191 | '--id', 192 | 'a1b2', 193 | '--label', 194 | 'test', 195 | '--pin', 196 | 'secret1', 197 | ], 198 | softhsm_conf=softhsm_conf, 199 | ) 200 | run_cmd( 201 | [ 202 | component_path['PKCS11_TOOL'], 203 | '--module', 204 | component_path['P11_MODULE'], 205 | '-l', 206 | '--pin', 207 | 'secret1', 208 | '-O', 209 | ], 210 | softhsm_conf=softhsm_conf, 211 | ) 212 | signer_cert_pem = _temp_file() 213 | openssl_conf = _temp_file() 214 | logging.debug('Generating OpenSSL config for version %s', openssl_version) 215 | with open(openssl_conf, 'w') as f: 216 | f.write( 217 | '\n'.join( 218 | [ 219 | 'openssl_conf = openssl_def', 220 | '[openssl_def]', 221 | 'engines = engine_section', 222 | '[engine_section]', 223 | 'pkcs11 = pkcs11_section', 224 | '[req]', 225 | 'distinguished_name = req_distinguished_name', 226 | '[req_distinguished_name]', 227 | '[pkcs11_section]', 228 | 'engine_id = pkcs11', 229 | # dynamic_path, 230 | "MODULE_PATH = {}".format(component_path['P11_MODULE']), 231 | 'init = 0', 232 | ] 233 | ) 234 | ) 235 | 236 | with open(openssl_conf, 'r') as f: 237 | logging.debug('-------- START DEBUG openssl_conf --------') 238 | logging.debug(f.readlines()) 239 | logging.debug('-------- END DEBUG openssl_conf --------') 240 | logging.debug('-------- START DEBUG paths --------') 241 | logging.debug(run_cmd(['ls', '-ld', component_path['P11_ENGINE']])) 242 | logging.debug(run_cmd(['ls', '-ld', component_path['P11_MODULE']])) 243 | logging.debug('-------- END DEBUG paths --------') 244 | 245 | signer_cert_der = _temp_file() 246 | 247 | logging.debug('Generating self-signed certificate') 248 | run_cmd( 249 | [ 250 | component_path['OPENSSL'], 251 | 'req', 252 | '-new', 253 | '-x509', 254 | '-subj', 255 | '/CN=Test Signer', 256 | '-engine', 257 | 'pkcs11', 258 | '-config', 259 | openssl_conf, 260 | '-keyform', 261 | 'engine', 262 | '-key', 263 | 'label_test', 264 | '-passin', 265 | 'pass:secret1', 266 | '-out', 267 | signer_cert_pem, 268 | ], 269 | softhsm_conf=softhsm_conf, 270 | ) 271 | 272 | run_cmd( 273 | [ 274 | component_path['OPENSSL'], 275 | 'x509', 276 | '-inform', 277 | 'PEM', 278 | '-outform', 279 | 'DER', 280 | '-in', 281 | signer_cert_pem, 282 | '-out', 283 | signer_cert_der, 284 | ], 285 | softhsm_conf=softhsm_conf, 286 | ) 287 | 288 | logging.debug('Importing certificate into token') 289 | 290 | run_cmd( 291 | [ 292 | component_path['PKCS11_TOOL'], 293 | '--module', 294 | component_path['P11_MODULE'], 295 | '-l', 296 | '--slot-index', 297 | '0', 298 | '--id', 299 | 'a1b2', 300 | '--label', 301 | 'test', 302 | '-y', 303 | 'cert', 304 | '-w', 305 | signer_cert_der, 306 | '--pin', 307 | 'secret1', 308 | ], 309 | softhsm_conf=softhsm_conf, 310 | ) 311 | 312 | # TODO: Should be teardowned in teardown # noqa: T101 313 | os.environ['SOFTHSM_CONF'] = softhsm_conf 314 | os.environ['SOFTHSM2_CONF'] = softhsm_conf 315 | 316 | except Exception as ex: 317 | print('-' * 64) 318 | traceback.print_exc() 319 | print('-' * 64) 320 | logging.exception('PKCS11 tests disabled: unable to initialize test token') 321 | raise ex 322 | 323 | 324 | def teardown() -> None: 325 | global p11_test_files 326 | for o in p11_test_files: 327 | if os.path.exists(o): 328 | if os.path.isdir(o): 329 | shutil.rmtree(o) 330 | else: 331 | os.unlink(o) 332 | p11_test_files = [] 333 | -------------------------------------------------------------------------------- /tests/test_constants.py: -------------------------------------------------------------------------------- 1 | """Test constants from :mod:`xmlsec.constants` module.""" 2 | 3 | import pytest 4 | 5 | import xmlsec 6 | 7 | 8 | def _constants(typename): 9 | return list( 10 | sorted( 11 | ( 12 | getattr(xmlsec.constants, name) 13 | for name in dir(xmlsec.constants) 14 | if type(getattr(xmlsec.constants, name)).__name__ == typename 15 | ), 16 | key=lambda t: t.name.lower(), 17 | ) 18 | ) 19 | 20 | 21 | @pytest.mark.parametrize('transform', _constants('__Transform'), ids=repr) 22 | def test_transform_str(transform): 23 | """Test string representation of ``xmlsec.constants.__Transform``.""" 24 | assert str(transform) == '{}, {}'.format(transform.name, transform.href) 25 | 26 | 27 | @pytest.mark.parametrize('transform', _constants('__Transform'), ids=repr) 28 | def test_transform_repr(transform): 29 | """Test raw string representation of ``xmlsec.constants.__Transform``.""" 30 | assert repr(transform) == '__Transform({!r}, {!r}, {})'.format(transform.name, transform.href, transform.usage) 31 | 32 | 33 | @pytest.mark.parametrize('keydata', _constants('__KeyData'), ids=repr) 34 | def test_keydata_str(keydata): 35 | """Test string representation of ``xmlsec.constants.__KeyData``.""" 36 | assert str(keydata) == '{}, {}'.format(keydata.name, keydata.href) 37 | 38 | 39 | @pytest.mark.parametrize('keydata', _constants('__KeyData'), ids=repr) 40 | def test_keydata_repr(keydata): 41 | """Test raw string representation of ``xmlsec.constants.__KeyData``.""" 42 | assert repr(keydata) == '__KeyData({!r}, {!r})'.format(keydata.name, keydata.href) 43 | -------------------------------------------------------------------------------- /tests/test_doc_examples.py: -------------------------------------------------------------------------------- 1 | """Run tests over code examples in the documentation.""" 2 | 3 | import contextlib 4 | import os 5 | import runpy 6 | import sys 7 | 8 | import pytest 9 | 10 | if sys.version_info >= (3, 4): 11 | from pathlib import Path 12 | else: # python2.7 compat 13 | from _pytest.pathlib import Path 14 | 15 | 16 | examples_dir = Path(__file__, '../../doc/source/examples').resolve() 17 | examples = sorted(examples_dir.glob('*.py')) 18 | 19 | 20 | @contextlib.contextmanager 21 | def cd(where_to): 22 | """ 23 | Temporarily change the working directory. 24 | 25 | Restore the current working dir after exiting the context. 26 | """ 27 | curr = Path.cwd() 28 | try: 29 | os.chdir(str(where_to)) 30 | yield 31 | finally: 32 | os.chdir(str(curr)) 33 | 34 | 35 | @pytest.mark.parametrize('example', examples, ids=lambda p: p.name) 36 | def test_doc_example(example): 37 | """ 38 | Verify example scripts included in the docs are up to date. 39 | 40 | Execute each script in :file:`docs/source/examples`, 41 | not raising any errors is good enough. 42 | """ 43 | with cd(example.parent): 44 | runpy.run_path(str(example)) 45 | -------------------------------------------------------------------------------- /tests/test_enc.py: -------------------------------------------------------------------------------- 1 | import tempfile 2 | 3 | from lxml import etree 4 | 5 | import xmlsec 6 | from tests import base 7 | 8 | consts = xmlsec.constants 9 | 10 | 11 | class TestEncryptionContext(base.TestMemoryLeaks): 12 | def test_init(self): 13 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 14 | del ctx 15 | 16 | def test_init_no_keys_manager(self): 17 | ctx = xmlsec.EncryptionContext() 18 | del ctx 19 | 20 | def test_init_bad_args(self): 21 | with self.assertRaisesRegex(TypeError, 'KeysManager required'): 22 | xmlsec.EncryptionContext(manager='foo') 23 | 24 | def test_no_key(self): 25 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 26 | self.assertIsNone(ctx.key) 27 | 28 | def test_get_key(self): 29 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 30 | self.assertIsNone(ctx.key) 31 | ctx.key = xmlsec.Key.from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatCertPem) 32 | self.assertIsNotNone(ctx.key) 33 | 34 | def test_del_key(self): 35 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 36 | ctx.key = xmlsec.Key.from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatCertPem) 37 | del ctx.key 38 | self.assertIsNone(ctx.key) 39 | 40 | def test_set_key(self): 41 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 42 | ctx.key = xmlsec.Key.from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatCertPem) 43 | self.assertIsNotNone(ctx.key) 44 | 45 | def test_set_key_bad_type(self): 46 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 47 | with self.assertRaisesRegex(TypeError, r'instance of \*xmlsec.Key\* expected.'): 48 | ctx.key = '' 49 | 50 | def test_set_invalid_key(self): 51 | ctx = xmlsec.EncryptionContext(manager=xmlsec.KeysManager()) 52 | with self.assertRaisesRegex(TypeError, 'empty key.'): 53 | ctx.key = xmlsec.Key() 54 | 55 | def test_encrypt_xml(self): 56 | root = self.load_xml('enc1-in.xml') 57 | enc_data = xmlsec.template.encrypted_data_create(root, consts.TransformAes128Cbc, type=consts.TypeEncElement, ns="xenc") 58 | xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) 59 | ki = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") 60 | ek = xmlsec.template.add_encrypted_key(ki, consts.TransformRsaOaep) 61 | xmlsec.template.encrypted_data_ensure_cipher_value(ek) 62 | data = root.find('./Data') 63 | self.assertIsNotNone(data) 64 | 65 | manager = xmlsec.KeysManager() 66 | manager.add_key(xmlsec.Key.from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatCertPem)) 67 | 68 | ctx = xmlsec.EncryptionContext(manager) 69 | ctx.key = xmlsec.Key.generate(consts.KeyDataAes, 128, consts.KeyDataTypeSession) 70 | 71 | encrypted = ctx.encrypt_xml(enc_data, data) 72 | self.assertIsNotNone(encrypted) 73 | 74 | enc_method = xmlsec.tree.find_child(enc_data, consts.NodeEncryptionMethod, consts.EncNs) 75 | self.assertIsNotNone(enc_method) 76 | self.assertEqual("http://www.w3.org/2001/04/xmlenc#aes128-cbc", enc_method.get("Algorithm")) 77 | ki = xmlsec.tree.find_child(enc_data, consts.NodeKeyInfo, consts.DSigNs) 78 | self.assertIsNotNone(ki) 79 | enc_method2 = xmlsec.tree.find_node(ki, consts.NodeEncryptionMethod, consts.EncNs) 80 | self.assertIsNotNone(enc_method2) 81 | self.assertEqual("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", enc_method2.get("Algorithm")) 82 | cipher_value = xmlsec.tree.find_node(ki, consts.NodeCipherValue, consts.EncNs) 83 | self.assertIsNotNone(cipher_value) 84 | 85 | def test_encrypt_xml_bad_args(self): 86 | ctx = xmlsec.EncryptionContext() 87 | with self.assertRaises(TypeError): 88 | ctx.encrypt_xml('', 0) 89 | 90 | def test_encrypt_xml_bad_template(self): 91 | ctx = xmlsec.EncryptionContext() 92 | with self.assertRaisesRegex(xmlsec.Error, 'unsupported `Type`, it should be `element` or `content`'): 93 | ctx.encrypt_xml(etree.Element('root'), etree.Element('node')) 94 | 95 | def test_encrypt_xml_bad_template_bad_type_attribute(self): 96 | ctx = xmlsec.EncryptionContext() 97 | with self.assertRaisesRegex(xmlsec.Error, 'unsupported `Type`, it should be `element` or `content`'): 98 | root = etree.Element('root') 99 | root.attrib['Type'] = 'foo' 100 | ctx.encrypt_xml(root, etree.Element('node')) 101 | 102 | def test_encrypt_xml_fail(self): 103 | ctx = xmlsec.EncryptionContext() 104 | with self.assertRaisesRegex(xmlsec.Error, 'failed to encrypt xml'): 105 | root = etree.Element('root') 106 | root.attrib['Type'] = consts.TypeEncElement 107 | ctx.encrypt_xml(root, etree.Element('node')) 108 | 109 | def test_encrypt_binary(self): 110 | root = self.load_xml('enc2-in.xml') 111 | enc_data = xmlsec.template.encrypted_data_create( 112 | root, consts.TransformAes128Cbc, type=consts.TypeEncContent, ns="xenc", mime_type="binary/octet-stream" 113 | ) 114 | xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) 115 | ki = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") 116 | ek = xmlsec.template.add_encrypted_key(ki, consts.TransformRsaOaep) 117 | xmlsec.template.encrypted_data_ensure_cipher_value(ek) 118 | 119 | manager = xmlsec.KeysManager() 120 | manager.add_key(xmlsec.Key.from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatCertPem)) 121 | 122 | ctx = xmlsec.EncryptionContext(manager) 123 | ctx.key = xmlsec.Key.generate(consts.KeyDataAes, 128, consts.KeyDataTypeSession) 124 | 125 | encrypted = ctx.encrypt_binary(enc_data, b'test') 126 | self.assertIsNotNone(encrypted) 127 | self.assertEqual("{{{}}}{}".format(consts.EncNs, consts.NodeEncryptedData), encrypted.tag) 128 | 129 | enc_method = xmlsec.tree.find_child(enc_data, consts.NodeEncryptionMethod, consts.EncNs) 130 | self.assertIsNotNone(enc_method) 131 | self.assertEqual("http://www.w3.org/2001/04/xmlenc#aes128-cbc", enc_method.get("Algorithm")) 132 | 133 | ki = xmlsec.tree.find_child(enc_data, consts.NodeKeyInfo, consts.DSigNs) 134 | self.assertIsNotNone(ki) 135 | enc_method2 = xmlsec.tree.find_node(ki, consts.NodeEncryptionMethod, consts.EncNs) 136 | self.assertIsNotNone(enc_method2) 137 | self.assertEqual("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", enc_method2.get("Algorithm")) 138 | cipher_value = xmlsec.tree.find_node(ki, consts.NodeCipherValue, consts.EncNs) 139 | self.assertIsNotNone(cipher_value) 140 | 141 | def test_encrypt_binary_bad_args(self): 142 | ctx = xmlsec.EncryptionContext() 143 | with self.assertRaises(TypeError): 144 | ctx.encrypt_binary('', 0) 145 | 146 | def test_encrypt_binary_bad_template(self): 147 | ctx = xmlsec.EncryptionContext() 148 | with self.assertRaisesRegex(xmlsec.Error, 'failed to encrypt binary'): 149 | ctx.encrypt_binary(etree.Element('root'), b'data') 150 | 151 | def test_encrypt_uri(self): 152 | root = self.load_xml('enc2-in.xml') 153 | enc_data = xmlsec.template.encrypted_data_create( 154 | root, consts.TransformAes128Cbc, type=consts.TypeEncContent, ns="xenc", mime_type="binary/octet-stream" 155 | ) 156 | xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) 157 | ki = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") 158 | ek = xmlsec.template.add_encrypted_key(ki, consts.TransformRsaOaep) 159 | xmlsec.template.encrypted_data_ensure_cipher_value(ek) 160 | 161 | manager = xmlsec.KeysManager() 162 | manager.add_key(xmlsec.Key.from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatCertPem)) 163 | 164 | ctx = xmlsec.EncryptionContext(manager) 165 | ctx.key = xmlsec.Key.generate(consts.KeyDataAes, 128, consts.KeyDataTypeSession) 166 | 167 | with tempfile.NamedTemporaryFile(delete=False) as tmpfile: 168 | tmpfile.write(b'test') 169 | 170 | encrypted = ctx.encrypt_binary(enc_data, 'file://' + tmpfile.name) 171 | self.assertIsNotNone(encrypted) 172 | self.assertEqual("{{{}}}{}".format(consts.EncNs, consts.NodeEncryptedData), encrypted.tag) 173 | 174 | enc_method = xmlsec.tree.find_child(enc_data, consts.NodeEncryptionMethod, consts.EncNs) 175 | self.assertIsNotNone(enc_method) 176 | self.assertEqual("http://www.w3.org/2001/04/xmlenc#aes128-cbc", enc_method.get("Algorithm")) 177 | 178 | ki = xmlsec.tree.find_child(enc_data, consts.NodeKeyInfo, consts.DSigNs) 179 | self.assertIsNotNone(ki) 180 | enc_method2 = xmlsec.tree.find_node(ki, consts.NodeEncryptionMethod, consts.EncNs) 181 | self.assertIsNotNone(enc_method2) 182 | self.assertEqual("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", enc_method2.get("Algorithm")) 183 | cipher_value = xmlsec.tree.find_node(ki, consts.NodeCipherValue, consts.EncNs) 184 | self.assertIsNotNone(cipher_value) 185 | 186 | def test_encrypt_uri_bad_args(self): 187 | ctx = xmlsec.EncryptionContext() 188 | with self.assertRaises(TypeError): 189 | ctx.encrypt_uri('', 0) 190 | 191 | def test_encrypt_uri_fail(self): 192 | ctx = xmlsec.EncryptionContext() 193 | with self.assertRaisesRegex(xmlsec.Error, 'failed to encrypt URI'): 194 | ctx.encrypt_uri(etree.Element('root'), '') 195 | 196 | def test_decrypt1(self): 197 | self.check_decrypt(1) 198 | 199 | def test_decrypt2(self): 200 | self.check_decrypt(2) 201 | 202 | def test_decrypt_key(self): 203 | root = self.load_xml('enc3-out.xml') 204 | enc_key = xmlsec.tree.find_child(root, consts.NodeEncryptedKey, consts.EncNs) 205 | self.assertIsNotNone(enc_key) 206 | 207 | manager = xmlsec.KeysManager() 208 | manager.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 209 | ctx = xmlsec.EncryptionContext(manager) 210 | keydata = ctx.decrypt(enc_key) 211 | ctx.reset() 212 | root.remove(enc_key) 213 | ctx.key = xmlsec.Key.from_binary_data(consts.KeyDataAes, keydata) 214 | enc_data = xmlsec.tree.find_child(root, consts.NodeEncryptedData, consts.EncNs) 215 | self.assertIsNotNone(enc_data) 216 | decrypted = ctx.decrypt(enc_data) 217 | self.assertIsNotNone(decrypted) 218 | self.assertEqual(self.load_xml("enc3-in.xml"), decrypted) 219 | 220 | def check_decrypt(self, i): 221 | root = self.load_xml('enc{}-out.xml'.format(i)) 222 | enc_data = xmlsec.tree.find_child(root, consts.NodeEncryptedData, consts.EncNs) 223 | self.assertIsNotNone(enc_data) 224 | 225 | manager = xmlsec.KeysManager() 226 | manager.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 227 | ctx = xmlsec.EncryptionContext(manager) 228 | decrypted = ctx.decrypt(enc_data) 229 | self.assertIsNotNone(decrypted) 230 | self.assertEqual(self.load_xml("enc{}-in.xml".format(i)), root) 231 | 232 | def test_decrypt_bad_args(self): 233 | ctx = xmlsec.EncryptionContext() 234 | with self.assertRaises(TypeError): 235 | ctx.decrypt('') 236 | -------------------------------------------------------------------------------- /tests/test_keys.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import tempfile 3 | 4 | import xmlsec 5 | from tests import base 6 | 7 | consts = xmlsec.constants 8 | 9 | 10 | class TestKeys(base.TestMemoryLeaks): 11 | def test_key_from_memory(self): 12 | key = xmlsec.Key.from_memory(self.load("rsakey.pem"), format=consts.KeyDataFormatPem) 13 | self.assertIsNotNone(key) 14 | 15 | def test_key_from_memory_with_bad_args(self): 16 | with self.assertRaises(TypeError): 17 | xmlsec.Key.from_memory(1, format="") 18 | 19 | def test_key_from_memory_invalid_data(self): 20 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot load key.*'): 21 | xmlsec.Key.from_memory(b'foo', format=consts.KeyDataFormatPem) 22 | 23 | def test_key_from_file(self): 24 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 25 | self.assertIsNotNone(key) 26 | 27 | def test_key_from_file_with_bad_args(self): 28 | with self.assertRaises(TypeError): 29 | xmlsec.Key.from_file(1, format="") 30 | 31 | def test_key_from_invalid_file(self): 32 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot read key.*'): 33 | with tempfile.NamedTemporaryFile() as tmpfile: 34 | tmpfile.write(b'foo') 35 | xmlsec.Key.from_file(tmpfile.name, format=consts.KeyDataFormatPem) 36 | 37 | def test_key_from_fileobj(self): 38 | with open(self.path("rsakey.pem"), "rb") as fobj: 39 | key = xmlsec.Key.from_file(fobj, format=consts.KeyDataFormatPem) 40 | self.assertIsNotNone(key) 41 | 42 | def test_key_from_invalid_fileobj(self): 43 | with tempfile.NamedTemporaryFile(delete=False) as tmpfile: 44 | tmpfile.write(b'foo') 45 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot read key.*'), open(tmpfile.name) as fp: 46 | xmlsec.Key.from_file(fp, format=consts.KeyDataFormatPem) 47 | 48 | def test_generate(self): 49 | key = xmlsec.Key.generate(klass=consts.KeyDataAes, size=256, type=consts.KeyDataTypeSession) 50 | self.assertIsNotNone(key) 51 | 52 | def test_generate_with_bad_args(self): 53 | with self.assertRaises(TypeError): 54 | xmlsec.Key.generate(klass="", size="", type="") 55 | 56 | def test_generate_invalid_size(self): 57 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot generate key.*'): 58 | xmlsec.Key.generate(klass=consts.KeyDataAes, size=0, type=consts.KeyDataTypeSession) 59 | 60 | def test_from_binary_file(self): 61 | key = xmlsec.Key.from_binary_file(klass=consts.KeyDataDes, filename=self.path("deskey.bin")) 62 | self.assertIsNotNone(key) 63 | 64 | def test_from_binary_file_with_bad_args(self): 65 | with self.assertRaises(TypeError): 66 | xmlsec.Key.from_binary_file(klass="", filename=1) 67 | 68 | def test_from_invalid_binary_file(self): 69 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot read key.*'): 70 | with tempfile.NamedTemporaryFile() as tmpfile: 71 | tmpfile.write(b'foo') 72 | xmlsec.Key.from_binary_file(klass=consts.KeyDataDes, filename=tmpfile.name) 73 | 74 | def test_from_binary_data(self): 75 | key = xmlsec.Key.from_binary_data(klass=consts.KeyDataDes, data=self.load("deskey.bin")) 76 | self.assertIsNotNone(key) 77 | 78 | def test_from_binary_data_with_bad_args(self): 79 | with self.assertRaises(TypeError): 80 | xmlsec.Key.from_binary_data(klass="", data=1) 81 | 82 | def test_from_invalid_binary_data(self): 83 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot read key.*'): 84 | xmlsec.Key.from_binary_data(klass=consts.KeyDataDes, data=b'') 85 | 86 | def test_load_cert_from_file(self): 87 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 88 | self.assertIsNotNone(key) 89 | key.load_cert_from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatPem) 90 | 91 | def test_load_cert_from_file_with_bad_args(self): 92 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 93 | self.assertIsNotNone(key) 94 | with self.assertRaises(TypeError): 95 | key.load_cert_from_file(1, format="") 96 | 97 | def test_load_cert_from_invalid_file(self): 98 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 99 | self.assertIsNotNone(key) 100 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot load cert.*'): 101 | with tempfile.NamedTemporaryFile() as tmpfile: 102 | tmpfile.write(b'foo') 103 | key.load_cert_from_file(tmpfile.name, format=consts.KeyDataFormatPem) 104 | 105 | def test_load_cert_from_fileobj(self): 106 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 107 | self.assertIsNotNone(key) 108 | with open(self.path("rsacert.pem"), "rb") as fobj: 109 | key.load_cert_from_file(fobj, format=consts.KeyDataFormatPem) 110 | 111 | def test_load_cert_from_fileobj_with_bad_args(self): 112 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 113 | self.assertIsNotNone(key) 114 | with self.assertRaises(TypeError), open(self.path("rsacert.pem"), "rb") as fobj: 115 | key.load_cert_from_file(fobj, format='') 116 | 117 | def test_load_cert_from_invalid_fileobj(self): 118 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 119 | self.assertIsNotNone(key) 120 | with tempfile.NamedTemporaryFile(delete=False) as tmpfile: 121 | tmpfile.write(b'foo') 122 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot load cert.*'), open(tmpfile.name) as fp: 123 | key.load_cert_from_file(fp, format=consts.KeyDataFormatPem) 124 | 125 | def test_load_cert_from_memory(self): 126 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 127 | self.assertIsNotNone(key) 128 | key.load_cert_from_memory(self.load("rsacert.pem"), format=consts.KeyDataFormatPem) 129 | 130 | def test_load_cert_from_memory_with_bad_args(self): 131 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 132 | self.assertIsNotNone(key) 133 | with self.assertRaises(TypeError): 134 | key.load_cert_from_memory(1, format="") 135 | 136 | def test_load_cert_from_memory_invalid_data(self): 137 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 138 | self.assertIsNotNone(key) 139 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot load cert.*'): 140 | key.load_cert_from_memory(b'', format=consts.KeyDataFormatPem) 141 | 142 | def test_get_name(self): 143 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 144 | self.assertIsNone(key.name) 145 | 146 | def test_get_name_invalid_key(self): 147 | key = xmlsec.Key() 148 | with self.assertRaisesRegex(ValueError, 'key is not ready'): 149 | key.name 150 | 151 | def test_del_name(self): 152 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 153 | key.name = "rsakey" 154 | del key.name 155 | self.assertIsNone(key.name) 156 | 157 | def test_set_name(self): 158 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 159 | key.name = "rsakey" 160 | self.assertEqual("rsakey", key.name) 161 | 162 | def test_set_name_invalid_key(self): 163 | key = xmlsec.Key() 164 | with self.assertRaisesRegex(ValueError, 'key is not ready'): 165 | key.name = 'foo' 166 | 167 | def test_copy(self): 168 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 169 | key2 = copy.copy(key) 170 | del key 171 | key2.load_cert_from_file(self.path("rsacert.pem"), format=consts.KeyDataFormatPem) 172 | 173 | 174 | class TestKeysManager(base.TestMemoryLeaks): 175 | def test_add_key(self): 176 | key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 177 | mngr = xmlsec.KeysManager() 178 | mngr.add_key(key) 179 | 180 | def test_add_key_with_bad_args(self): 181 | mngr = xmlsec.KeysManager() 182 | with self.assertRaises(TypeError): 183 | mngr.add_key("") 184 | 185 | def test_load_cert(self): 186 | mngr = xmlsec.KeysManager() 187 | mngr.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 188 | mngr.load_cert(self.path("rsacert.pem"), format=consts.KeyDataFormatPem, type=consts.KeyDataTypeTrusted) 189 | 190 | def test_load_cert_with_bad_args(self): 191 | mngr = xmlsec.KeysManager() 192 | mngr.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 193 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot load cert.*'): 194 | with tempfile.NamedTemporaryFile() as tmpfile: 195 | tmpfile.write(b'foo') 196 | mngr.load_cert(tmpfile.name, format=consts.KeyDataFormatPem, type=consts.KeyDataTypeTrusted) 197 | 198 | def test_load_invalid_cert(self): 199 | mngr = xmlsec.KeysManager() 200 | mngr.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 201 | with self.assertRaises(TypeError): 202 | mngr.load_cert(1, format="", type="") 203 | 204 | def test_load_cert_from_memory(self): 205 | mngr = xmlsec.KeysManager() 206 | mngr.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 207 | mngr.load_cert_from_memory(self.load("rsacert.pem"), format=consts.KeyDataFormatPem, type=consts.KeyDataTypeTrusted) 208 | 209 | def test_load_cert_from_memory_with_bad_args(self): 210 | mngr = xmlsec.KeysManager() 211 | mngr.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 212 | with self.assertRaises(TypeError): 213 | mngr.load_cert_from_memory(1, format="", type="") 214 | 215 | def test_load_cert_from_memory_invalid_data(self): 216 | mngr = xmlsec.KeysManager() 217 | mngr.add_key(xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem)) 218 | with self.assertRaisesRegex(xmlsec.Error, '.*cannot load cert.*'): 219 | mngr.load_cert_from_memory(b'', format=consts.KeyDataFormatPem, type=consts.KeyDataTypeTrusted) 220 | 221 | def test_load_invalid_key(self): 222 | mngr = xmlsec.KeysManager() 223 | with self.assertRaises(ValueError): 224 | mngr.add_key(xmlsec.Key()) 225 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from io import BytesIO 3 | from unittest import skipIf 4 | 5 | import xmlsec 6 | from tests import base 7 | from xmlsec import constants as consts 8 | 9 | 10 | class TestBase64LineSize(base.TestMemoryLeaks): 11 | def tearDown(self): 12 | xmlsec.base64_default_line_size(64) 13 | super(TestBase64LineSize, self).tearDown() 14 | 15 | def test_get_base64_default_line_size(self): 16 | self.assertEqual(xmlsec.base64_default_line_size(), 64) 17 | 18 | def test_set_base64_default_line_size_positional_arg(self): 19 | xmlsec.base64_default_line_size(0) 20 | self.assertEqual(xmlsec.base64_default_line_size(), 0) 21 | 22 | def test_set_base64_default_line_size_keyword_arg(self): 23 | xmlsec.base64_default_line_size(size=0) 24 | self.assertEqual(xmlsec.base64_default_line_size(), 0) 25 | 26 | def test_set_base64_default_line_size_with_bad_args(self): 27 | size = xmlsec.base64_default_line_size() 28 | for bad_size in (None, '', object()): 29 | with self.assertRaises(TypeError): 30 | xmlsec.base64_default_line_size(bad_size) 31 | self.assertEqual(xmlsec.base64_default_line_size(), size) 32 | 33 | def test_set_base64_default_line_size_rejects_negative_values(self): 34 | size = xmlsec.base64_default_line_size() 35 | with self.assertRaises(ValueError): 36 | xmlsec.base64_default_line_size(-1) 37 | self.assertEqual(xmlsec.base64_default_line_size(), size) 38 | 39 | 40 | class TestCallbacks(base.TestMemoryLeaks): 41 | def setUp(self): 42 | super().setUp() 43 | xmlsec.cleanup_callbacks() 44 | 45 | def _sign_doc(self): 46 | root = self.load_xml("doc.xml") 47 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 48 | xmlsec.template.add_reference(sign, consts.TransformSha1, uri="cid:123456") 49 | 50 | ctx = xmlsec.SignatureContext() 51 | ctx.key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) 52 | ctx.sign(sign) 53 | return sign 54 | 55 | def _expect_sign_failure(self): 56 | with self.assertRaisesRegex(xmlsec.Error, 'failed to sign'): 57 | self._sign_doc() 58 | 59 | def _mismatch_callbacks(self, match_cb=lambda filename: False): 60 | return [ 61 | match_cb, 62 | lambda filename: None, 63 | lambda none, buf: 0, 64 | lambda none: None, 65 | ] 66 | 67 | def _register_mismatch_callbacks(self, match_cb=lambda filename: False): 68 | xmlsec.register_callbacks(*self._mismatch_callbacks(match_cb)) 69 | 70 | def _register_match_callbacks(self): 71 | xmlsec.register_callbacks( 72 | lambda filename: filename == b'cid:123456', 73 | lambda filename: BytesIO(b''), 74 | lambda bio, buf: bio.readinto(buf), 75 | lambda bio: bio.close(), 76 | ) 77 | 78 | def _find(self, elem, *tags): 79 | try: 80 | return elem.xpath( 81 | './' + '/'.join('xmldsig:{}'.format(tag) for tag in tags), 82 | namespaces={ 83 | 'xmldsig': 'http://www.w3.org/2000/09/xmldsig#', 84 | }, 85 | )[0] 86 | except IndexError as e: 87 | raise KeyError(tags) from e 88 | 89 | def _verify_external_data_signature(self): 90 | signature = self._sign_doc() 91 | digest = self._find(signature, 'SignedInfo', 'Reference', 'DigestValue').text 92 | self.assertEqual(digest, 'VihZwVMGJ48NsNl7ertVHiURXk8=') 93 | 94 | def test_sign_external_data_no_callbacks_fails(self): 95 | self._expect_sign_failure() 96 | 97 | def test_sign_external_data_default_callbacks_fails(self): 98 | xmlsec.register_default_callbacks() 99 | self._expect_sign_failure() 100 | 101 | def test_sign_external_data_no_matching_callbacks_fails(self): 102 | self._register_mismatch_callbacks() 103 | self._expect_sign_failure() 104 | 105 | def test_sign_data_from_callbacks(self): 106 | self._register_match_callbacks() 107 | self._verify_external_data_signature() 108 | 109 | def test_sign_data_not_first_callback(self): 110 | bad_match_calls = 0 111 | 112 | def match_cb(filename): 113 | nonlocal bad_match_calls 114 | bad_match_calls += 1 115 | return False 116 | 117 | for _ in range(2): 118 | self._register_mismatch_callbacks(match_cb) 119 | 120 | self._register_match_callbacks() 121 | 122 | for _ in range(2): 123 | self._register_mismatch_callbacks() 124 | 125 | self._verify_external_data_signature() 126 | self.assertEqual(bad_match_calls, 0) 127 | 128 | @skipIf(sys.platform == "win32", "unclear behaviour on windows") 129 | def test_failed_sign_because_default_callbacks(self): 130 | mismatch_calls = 0 131 | 132 | def mismatch_cb(filename): 133 | nonlocal mismatch_calls 134 | mismatch_calls += 1 135 | return False 136 | 137 | # NB: These first two sets of callbacks should never get called, 138 | # because the default callbacks always match beforehand: 139 | self._register_match_callbacks() 140 | self._register_mismatch_callbacks(mismatch_cb) 141 | xmlsec.register_default_callbacks() 142 | self._register_mismatch_callbacks(mismatch_cb) 143 | self._register_mismatch_callbacks(mismatch_cb) 144 | self._expect_sign_failure() 145 | self.assertEqual(mismatch_calls, 2) 146 | 147 | def test_register_non_callables(self): 148 | for idx in range(4): 149 | cbs = self._mismatch_callbacks() 150 | cbs[idx] = None 151 | self.assertRaises(TypeError, xmlsec.register_callbacks, *cbs) 152 | 153 | def test_sign_external_data_fails_on_read_callback_wrong_returns(self): 154 | xmlsec.register_callbacks( 155 | lambda filename: filename == b'cid:123456', 156 | lambda filename: BytesIO(b''), 157 | lambda bio, buf: None, 158 | lambda bio: bio.close(), 159 | ) 160 | self._expect_sign_failure() 161 | -------------------------------------------------------------------------------- /tests/test_pkcs11.py: -------------------------------------------------------------------------------- 1 | import xmlsec 2 | from tests import base 3 | from xmlsec import constants as consts 4 | 5 | KEY_URL = "pkcs11;pkcs11:token=test;object=test;pin-value=secret1" 6 | 7 | 8 | def setUpModule(): 9 | from tests import softhsm_setup 10 | 11 | softhsm_setup.setup() 12 | 13 | 14 | def tearDownModule(): 15 | from tests import softhsm_setup 16 | 17 | softhsm_setup.teardown() 18 | 19 | 20 | class TestKeys(base.TestMemoryLeaks): 21 | def test_del_key(self): 22 | ctx = xmlsec.SignatureContext(manager=xmlsec.KeysManager()) 23 | ctx.key = xmlsec.Key.from_engine(KEY_URL) 24 | del ctx.key 25 | self.assertIsNone(ctx.key) 26 | 27 | def test_set_key(self): 28 | ctx = xmlsec.SignatureContext(manager=xmlsec.KeysManager()) 29 | ctx.key = xmlsec.Key.from_engine(KEY_URL) 30 | self.assertIsNotNone(ctx.key) 31 | 32 | def test_sign_bad_args(self): 33 | ctx = xmlsec.SignatureContext() 34 | ctx.key = xmlsec.Key.from_engine(KEY_URL) 35 | with self.assertRaises(TypeError): 36 | ctx.sign('') 37 | 38 | def test_sign_fail(self): 39 | ctx = xmlsec.SignatureContext() 40 | ctx.key = xmlsec.Key.from_engine(KEY_URL) 41 | with self.assertRaisesRegex(xmlsec.Error, 'failed to sign'): 42 | ctx.sign(self.load_xml('sign1-in.xml')) 43 | 44 | def test_sign_case1(self): 45 | """Should sign a pre-constructed template file using a key from a pkcs11 engine.""" 46 | root = self.load_xml("sign1-in.xml") 47 | sign = xmlsec.tree.find_node(root, consts.NodeSignature) 48 | self.assertIsNotNone(sign) 49 | 50 | ctx = xmlsec.SignatureContext() 51 | ctx.key = xmlsec.Key.from_engine(KEY_URL) 52 | self.assertIsNotNone(ctx.key) 53 | ctx.key.name = 'rsakey.pem' 54 | self.assertEqual("rsakey.pem", ctx.key.name) 55 | 56 | ctx.sign(sign) 57 | self.assertEqual(self.load_xml("sign1-out.xml"), root) 58 | -------------------------------------------------------------------------------- /tests/test_templates.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from lxml import etree 4 | 5 | import xmlsec 6 | from tests import base 7 | 8 | consts = xmlsec.constants 9 | 10 | 11 | class TestTemplates(base.TestMemoryLeaks): 12 | def test_create(self): 13 | root = self.load_xml("doc.xml") 14 | sign = xmlsec.template.create( 15 | root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1, id="Id", ns="test" 16 | ) 17 | self.assertEqual("Id", sign.get("Id")) 18 | self.assertEqual("test", sign.prefix) 19 | 20 | def test_create_bad_args(self): 21 | with self.assertRaises(TypeError): 22 | xmlsec.template.create('', c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 23 | 24 | def test_encrypt_data_create(self): 25 | root = self.load_xml("doc.xml") 26 | enc = xmlsec.template.encrypted_data_create( 27 | root, method=consts.TransformDes3Cbc, id="Id", type="Type", mime_type="MimeType", encoding="Encoding", ns="test" 28 | ) 29 | for a in ("Id", "Type", "MimeType", "Encoding"): 30 | self.assertEqual(a, enc.get(a)) 31 | self.assertEqual("test", enc.prefix) 32 | 33 | def test_ensure_key_info(self): 34 | root = self.load_xml("doc.xml") 35 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 36 | ki = xmlsec.template.ensure_key_info(sign, id="Id") 37 | self.assertEqual("Id", ki.get("Id")) 38 | 39 | def test_ensure_key_info_fail(self): 40 | with self.assertRaisesRegex(xmlsec.Error, 'cannot ensure key info.'): 41 | xmlsec.template.ensure_key_info(etree.fromstring(b''), id="Id") 42 | 43 | def test_ensure_key_info_bad_args(self): 44 | with self.assertRaises(TypeError): 45 | xmlsec.template.ensure_key_info('', id=0) 46 | 47 | def test_add_encrypted_key(self): 48 | root = self.load_xml("doc.xml") 49 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 50 | ki = xmlsec.template.ensure_key_info(sign) 51 | ek = xmlsec.template.add_encrypted_key(ki, consts.TransformRsaOaep) 52 | self.assertEqual(ek, xmlsec.tree.find_node(self.load_xml("sign_template.xml"), consts.NodeEncryptedKey, consts.EncNs)) 53 | ek2 = xmlsec.template.add_encrypted_key(ki, consts.TransformRsaOaep, id="Id", type="Type", recipient="Recipient") 54 | for a in ("Id", "Type", "Recipient"): 55 | self.assertEqual(a, ek2.get(a)) 56 | 57 | def test_add_key_name(self): 58 | root = self.load_xml("doc.xml") 59 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 60 | ki = xmlsec.template.ensure_key_info(sign) 61 | kn = xmlsec.template.add_key_name(ki) 62 | self.assertEqual(kn, xmlsec.tree.find_node(self.load_xml("sign_template.xml"), consts.NodeKeyName, consts.DSigNs)) 63 | kn2 = xmlsec.template.add_key_name(ki, name="name") 64 | self.assertEqual("name", kn2.text) 65 | 66 | def test_add_key_name_none(self): 67 | root = self.load_xml("doc.xml") 68 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 69 | ki = xmlsec.template.ensure_key_info(sign) 70 | kn2 = xmlsec.template.add_key_name(ki, name=None) 71 | self.assertEqual(kn2.text, None) 72 | print(etree.tostring(kn2)) 73 | 74 | def test_add_key_name_bad_args(self): 75 | with self.assertRaises(TypeError): 76 | xmlsec.template.add_key_name('') 77 | 78 | def test_add_reference(self): 79 | root = self.load_xml("doc.xml") 80 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 81 | ref = xmlsec.template.add_reference(sign, consts.TransformSha1, id="Id", uri="URI", type="Type") 82 | for a in ("Id", "URI", "Type"): 83 | self.assertEqual(a, ref.get(a)) 84 | 85 | def test_add_reference_bad_args(self): 86 | with self.assertRaises(TypeError): 87 | xmlsec.template.add_reference('', consts.TransformSha1) 88 | with self.assertRaises(TypeError): 89 | xmlsec.template.add_reference(etree.Element('root'), '') 90 | 91 | def test_add_reference_fail(self): 92 | with self.assertRaisesRegex(xmlsec.Error, 'cannot add reference.'): 93 | xmlsec.template.add_reference(etree.Element('root'), consts.TransformSha1) 94 | 95 | def test_add_transform_bad_args(self): 96 | with self.assertRaises(TypeError): 97 | xmlsec.template.add_transform('', consts.TransformSha1) 98 | with self.assertRaises(TypeError): 99 | xmlsec.template.add_transform(etree.Element('root'), '') 100 | 101 | def test_add_key_value(self): 102 | root = self.load_xml("doc.xml") 103 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 104 | ki = xmlsec.template.ensure_key_info(sign) 105 | kv = xmlsec.template.add_key_value(ki) 106 | self.assertEqual(kv, xmlsec.tree.find_node(self.load_xml("sign_template.xml"), consts.NodeKeyValue, consts.DSigNs)) 107 | 108 | def test_add_key_value_bad_args(self): 109 | with self.assertRaises(TypeError): 110 | xmlsec.template.add_key_value('') 111 | 112 | def test_add_x509_data(self): 113 | root = self.load_xml("doc.xml") 114 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 115 | ki = xmlsec.template.ensure_key_info(sign) 116 | x509 = xmlsec.template.add_x509_data(ki) 117 | xmlsec.template.x509_data_add_certificate(x509) 118 | xmlsec.template.x509_data_add_crl(x509) 119 | issuer = xmlsec.template.x509_data_add_issuer_serial(x509) 120 | xmlsec.template.x509_data_add_ski(x509) 121 | xmlsec.template.x509_data_add_subject_name(x509) 122 | xmlsec.template.x509_issuer_serial_add_issuer_name(issuer) 123 | xmlsec.template.x509_issuer_serial_add_serial_number(issuer) 124 | self.assertEqual(x509, xmlsec.tree.find_node(self.load_xml("sign_template.xml"), consts.NodeX509Data, consts.DSigNs)) 125 | 126 | def test_add_x509_data_bad_args(self): 127 | with self.assertRaises(TypeError): 128 | xmlsec.template.add_x509_data('') 129 | 130 | def test_x509_issuer_serial_add_issuer(self): 131 | root = self.load_xml("doc.xml") 132 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 133 | ki = xmlsec.template.ensure_key_info(sign) 134 | x509 = xmlsec.template.add_x509_data(ki) 135 | issuer = xmlsec.template.x509_data_add_issuer_serial(x509) 136 | name = xmlsec.template.x509_issuer_serial_add_issuer_name(issuer, name="Name") 137 | serial = xmlsec.template.x509_issuer_serial_add_serial_number(issuer, serial="Serial") 138 | self.assertEqual("Name", name.text) 139 | self.assertEqual("Serial", serial.text) 140 | 141 | def test_x509_issuer_serial_add_issuer_bad_args(self): 142 | with self.assertRaises(TypeError): 143 | xmlsec.template.x509_data_add_issuer_serial('') 144 | 145 | def test_x509_issuer_serial_add_issuer_name_bad_args(self): 146 | with self.assertRaises(TypeError): 147 | xmlsec.template.x509_issuer_serial_add_issuer_name('') 148 | 149 | def test_x509_issuer_serial_add_serial_number_bad_args(self): 150 | with self.assertRaises(TypeError): 151 | xmlsec.template.x509_issuer_serial_add_serial_number('') 152 | 153 | def test_x509_data_add_subject_name_bad_args(self): 154 | with self.assertRaises(TypeError): 155 | xmlsec.template.x509_data_add_subject_name('') 156 | 157 | def test_x509_data_add_ski_bad_args(self): 158 | with self.assertRaises(TypeError): 159 | xmlsec.template.x509_data_add_ski('') 160 | 161 | def test_x509_data_add_certificate_bad_args(self): 162 | with self.assertRaises(TypeError): 163 | xmlsec.template.x509_data_add_certificate('') 164 | 165 | def test_x509_data_add_crl_bad_args(self): 166 | with self.assertRaises(TypeError): 167 | xmlsec.template.x509_data_add_crl('') 168 | 169 | def test_add_encrypted_key_bad_args(self): 170 | with self.assertRaises(TypeError): 171 | xmlsec.template.add_encrypted_key('', 0) 172 | 173 | def test_encrypted_data_create_bad_args(self): 174 | with self.assertRaises(TypeError): 175 | xmlsec.template.encrypted_data_create('', 0) 176 | 177 | def test_encrypted_data_ensure_cipher_value(self): 178 | root = self.load_xml("doc.xml") 179 | enc = xmlsec.template.encrypted_data_create(root, method=consts.TransformDes3Cbc) 180 | cv = xmlsec.template.encrypted_data_ensure_cipher_value(enc) 181 | self.assertEqual(cv, xmlsec.tree.find_node(self.load_xml("sign_template.xml"), consts.NodeCipherValue, consts.EncNs)) 182 | 183 | def test_encrypted_data_ensure_cipher_value_bad_args(self): 184 | with self.assertRaises(TypeError): 185 | xmlsec.template.encrypted_data_ensure_cipher_value('') 186 | 187 | def test_encrypted_data_ensure_key_info(self): 188 | root = self.load_xml("doc.xml") 189 | enc = xmlsec.template.encrypted_data_create(root, method=consts.TransformDes3Cbc) 190 | ki = xmlsec.template.encrypted_data_ensure_key_info(enc) 191 | self.assertEqual(ki, xmlsec.tree.find_node(self.load_xml("enc_template.xml"), consts.NodeKeyInfo, consts.DSigNs)) 192 | ki2 = xmlsec.template.encrypted_data_ensure_key_info(enc, id="Id", ns="test") 193 | self.assertEqual("Id", ki2.get("Id")) 194 | self.assertEqual("test", ki2.prefix) 195 | 196 | def test_encrypted_data_ensure_key_info_bad_args(self): 197 | with self.assertRaises(TypeError): 198 | xmlsec.template.encrypted_data_ensure_key_info('') 199 | 200 | @unittest.skipIf(not hasattr(consts, 'TransformXslt'), reason='XSLT transformations not enabled') 201 | def test_transform_add_c14n_inclusive_namespaces(self): 202 | root = self.load_xml("doc.xml") 203 | sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) 204 | ref = xmlsec.template.add_reference(sign, consts.TransformSha1) 205 | trans1 = xmlsec.template.add_transform(ref, consts.TransformEnveloped) 206 | xmlsec.template.transform_add_c14n_inclusive_namespaces(trans1, "default") 207 | trans2 = xmlsec.template.add_transform(ref, consts.TransformXslt) 208 | xmlsec.template.transform_add_c14n_inclusive_namespaces(trans2, ["ns1", "ns2"]) 209 | self.assertEqual(ref, xmlsec.tree.find_node(self.load_xml("sign_template.xml"), consts.NodeReference, consts.DSigNs)) 210 | 211 | def test_transform_add_c14n_inclusive_namespaces_bad_args(self): 212 | with self.assertRaises(TypeError): 213 | xmlsec.template.transform_add_c14n_inclusive_namespaces('', []) 214 | -------------------------------------------------------------------------------- /tests/test_tree.py: -------------------------------------------------------------------------------- 1 | import xmlsec 2 | from tests import base 3 | 4 | consts = xmlsec.constants 5 | 6 | 7 | class TestTree(base.TestMemoryLeaks): 8 | def test_find_child(self): 9 | root = self.load_xml("sign_template.xml") 10 | si = xmlsec.tree.find_child(root, consts.NodeSignedInfo, consts.DSigNs) 11 | self.assertEqual(consts.NodeSignedInfo, si.tag.partition('}')[2]) 12 | self.assertIsNone(xmlsec.tree.find_child(root, consts.NodeReference)) 13 | self.assertIsNone(xmlsec.tree.find_child(root, consts.NodeSignedInfo, consts.EncNs)) 14 | 15 | def test_find_child_bad_args(self): 16 | with self.assertRaises(TypeError): 17 | xmlsec.tree.find_child('', 0, True) 18 | 19 | def test_find_parent(self): 20 | root = self.load_xml("sign_template.xml") 21 | si = xmlsec.tree.find_child(root, consts.NodeSignedInfo, consts.DSigNs) 22 | self.assertIs(root, xmlsec.tree.find_parent(si, consts.NodeSignature)) 23 | self.assertIsNone(xmlsec.tree.find_parent(root, consts.NodeSignedInfo)) 24 | 25 | def test_find_parent_bad_args(self): 26 | with self.assertRaises(TypeError): 27 | xmlsec.tree.find_parent('', 0, True) 28 | 29 | def test_find_node(self): 30 | root = self.load_xml("sign_template.xml") 31 | ref = xmlsec.tree.find_node(root, consts.NodeReference) 32 | self.assertEqual(consts.NodeReference, ref.tag.partition('}')[2]) 33 | self.assertIsNone(xmlsec.tree.find_node(root, consts.NodeReference, consts.EncNs)) 34 | 35 | def test_find_node_bad_args(self): 36 | with self.assertRaises(TypeError): 37 | xmlsec.tree.find_node('', 0, True) 38 | 39 | def test_add_ids(self): 40 | root = self.load_xml("sign_template.xml") 41 | xmlsec.tree.add_ids(root, ["id1", "id2", "id3"]) 42 | 43 | def test_add_ids_bad_args(self): 44 | with self.assertRaises(TypeError): 45 | xmlsec.tree.add_ids('', []) 46 | -------------------------------------------------------------------------------- /tests/test_type_stubs.py: -------------------------------------------------------------------------------- 1 | """Test type stubs for correctness where possible.""" 2 | 3 | import os 4 | 5 | import pytest 6 | 7 | import xmlsec 8 | 9 | black = pytest.importorskip('black') 10 | 11 | 12 | constants_stub_header = """ 13 | import sys 14 | from typing import NamedTuple 15 | 16 | if sys.version_info >= (3, 8): 17 | from typing import Final 18 | else: 19 | from typing_extensions import Final 20 | 21 | class __KeyData(NamedTuple): # __KeyData type 22 | href: str 23 | name: str 24 | 25 | class __KeyDataNoHref(NamedTuple): # __KeyData type 26 | href: None 27 | name: str 28 | 29 | class __Transform(NamedTuple): # __Transform type 30 | href: str 31 | name: str 32 | usage: int 33 | 34 | class __TransformNoHref(NamedTuple): # __Transform type 35 | href: None 36 | name: str 37 | usage: int 38 | 39 | """ 40 | 41 | 42 | def gen_constants_stub(): 43 | """ 44 | Generate contents of the file:`xmlsec/constants.pyi`. 45 | 46 | Simply load all constants at runtime, 47 | generate appropriate type hint for each constant type. 48 | """ 49 | 50 | def process_constant(name): 51 | """Generate line in stub file for constant name.""" 52 | obj = getattr(xmlsec.constants, name) 53 | type_name = type(obj).__name__ 54 | if type_name in ('__KeyData', '__Transform') and obj.href is None: 55 | type_name += 'NoHref' 56 | return '{name}: Final[{type_name}]'.format(name=name, type_name=type_name) 57 | 58 | names = list(sorted(name for name in dir(xmlsec.constants) if not name.startswith('__'))) 59 | lines = [process_constant(name) for name in names] 60 | return constants_stub_header + os.linesep.join(lines) 61 | 62 | 63 | def test_xmlsec_constants_stub(request): 64 | """ 65 | Generate the stub file for :mod:`xmlsec.constants` from existing code. 66 | 67 | Compare it against the existing stub :file:`xmlsec/constants.pyi`. 68 | """ 69 | stub = request.config.rootpath / 'src' / 'xmlsec' / 'constants.pyi' 70 | mode = black.FileMode(target_versions={black.TargetVersion.PY39}, line_length=130, is_pyi=True, string_normalization=False) 71 | formatted = black.format_file_contents(gen_constants_stub(), fast=False, mode=mode) 72 | assert formatted == stub.read_text() 73 | -------------------------------------------------------------------------------- /tests/test_xmlsec.py: -------------------------------------------------------------------------------- 1 | import xmlsec 2 | from tests import base 3 | 4 | 5 | class TestModule(base.TestMemoryLeaks): 6 | def test_reinitialize_module(self): 7 | """ 8 | This test doesn't explicitly verify anything, but will be invoked first in the suite. 9 | 10 | So if the subsequent tests don't fail, we know that the ``init()``/``shutdown()`` 11 | function pair doesn't break anything. 12 | """ 13 | xmlsec.shutdown() 14 | xmlsec.init() 15 | --------------------------------------------------------------------------------