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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/actions/test-setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: test-setup
author: Matthias Valvekens
description: Perform set-up for python-pkcs11 CI
inputs:
os:
description: OS to target
required: true
python-version:
description: Python version to target
required: true
dependency-group:
description: UV dependency group to install
required: true
pkcs11-platform:
description: PKCS#11 platform to target
required: true
token-label:
description: Label assigned to the token
required: true
token-user-pin:
description: User PIN to configure on the token
required: true
token-so-pin:
description: Security officer PIN to configure on the token
required: true
outputs:
module:
description: Path to PKCS#11 module
value: ${{ steps.install-result.outputs.module }}
module2:
description: Path to alternative PKCS#11 module ('multi' only)
value: ${{ steps.install-result.outputs.module2 }}
runs:
using: "composite"
steps:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- uses: ./.github/actions/install-softhsm
if: inputs.pkcs11-platform == 'softhsm' || inputs.pkcs11-platform == 'multi'
id: softhsm
with:
os: ${{ inputs.os }}
token-label: ${{ inputs.token-label }}
token-so-pin: ${{ inputs.token-so-pin }}
token-user-pin: ${{ inputs.token-user-pin }}
- uses: ./.github/actions/install-opencryptoki
# only run opencryptoki tests on ubuntu
# (macos and windows don't seem to be supported)
if: inputs.pkcs11-platform == 'opencryptoki' || inputs.pkcs11-platform == 'multi'
id: opencryptoki
with:
os: ${{ inputs.os }}
token-label: ${{ inputs.token-label }}
token-so-pin: ${{ inputs.token-so-pin }}
token-user-pin: ${{ inputs.token-user-pin }}
- name: Set module path
id: install-result
shell: bash
run: |
if [[ "$PLATFORM" == 'opencryptoki' ]]; then
echo "module=${{ steps.opencryptoki.outputs.module }}" >> "$GITHUB_OUTPUT"
elif [[ "$PLATFORM" == 'softhsm' ]]; then
echo "module=${{ steps.softhsm.outputs.module }}" >> "$GITHUB_OUTPUT"
elif [[ "$PLATFORM" == 'multi' ]]; then
# NOTE: the 'multi' platform is only used for testing the code that
# swaps between multiple PKCS#11 implementations. As such, any two
# PKCS#11 implementations will do. If we add a 3rd platform
# to the CI at a later stage that is faster to install than opencryptoki,
# switching is an option.
echo "module=${{ steps.softhsm.outputs.module }}" >> "$GITHUB_OUTPUT"
echo "module2=${{ steps.opencryptoki.outputs.module }}" >> "$GITHUB_OUTPUT"
else
echo "$PLATFORM is not a valid PKCS#11 platform choice"
exit 1
fi
env:
PLATFORM: ${{ inputs.pkcs11-platform }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
python-version: ${{ inputs.python-version }}
- name: Install testing dependencies
shell: bash
run: uv sync --no-dev --exact --group "${{ inputs.dependency-group }}"
99 changes: 99 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Coverage
on:
pull_request: {}
workflow_dispatch: {}
env:
UV_PYTHON_PREFERENCE: only-system
UV_NO_SYNC: "1"
PKCS11_TOKEN_LABEL: "TEST"
PKCS11_TOKEN_PIN: "1234"
PKCS11_TOKEN_SO_PIN: "5678"
jobs:
# For now, we run the coverage as a separate job.
# At the time of writing, the latest version of Cython's line tracing
# seems to lead to segfaults in Python 3.13 -> TODO: investigate
pytest-coverage:
runs-on: ubuntu-latest
strategy:
matrix:
pkcs11-platform:
- softhsm
- opencryptoki
steps:
- name: Acquire sources
uses: actions/checkout@v4
- name: Arm coverage-only compiler directives
# Unfortunately, it doesn't seem to be possible to pass directives
# to Cython through environment variables: https://github.com/cython/cython/issues/3930
# Doing it here is still better than introducing a non-declarative setup.py into the
# build again.
run: sed -i 's/#coverage#cython/#cython/g' pkcs11/*.pyx
- uses: ./.github/actions/test-setup
id: setup
with:
os: ubuntu-latest
python-version: "3.12"
dependency-group: coverage
token-label: ${{ env.PKCS11_TOKEN_LABEL }}
token-so-pin: ${{ env.PKCS11_TOKEN_SO_PIN }}
token-user-pin: ${{ env.PKCS11_TOKEN_PIN }}
pkcs11-platform: ${{ matrix.pkcs11-platform }}
env:
CFLAGS: "-DCYTHON_TRACE_NOGIL=1"
EXT_BUILD_DEBUG: "1"
- name: Run tests
run: uv run pytest -v --cov=pkcs11 --cov-branch --cov-report=xml:${{ matrix.pkcs11-platform }}-coverage.xml
env:
PKCS11_MODULE: ${{ steps.setup.outputs.module }}
- name: Stash coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-${{ strategy.job-index }}
path: "*-coverage.xml"
pytest-coverage-multilib:
runs-on: ubuntu-latest
steps:
- name: Acquire sources
uses: actions/checkout@v4
- uses: ./.github/actions/test-setup
id: setup
with:
os: ubuntu-latest
pkcs11-platform: multi
token-label: ${{ env.PKCS11_TOKEN_LABEL }}
token-so-pin: ${{ env.PKCS11_TOKEN_SO_PIN }}
token-user-pin: ${{ env.PKCS11_TOKEN_PIN }}
python-version: "3.12"
dependency-group: coverage
- name: Run tests
run: uv run pytest -v --cov=pkcs11 --cov-branch --cov-report=xml:multilib-coverage.xml tests/test_multilib.py
env:
PKCS11_MODULE: ${{ steps.setup.outputs.module }}
PKCS11_MODULE2: ${{ steps.setup.outputs.module2 }}
- name: Stash coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-multilib
path: "*-coverage.xml"
codecov-upload:
permissions:
actions: write
contents: read
runs-on: ubuntu-latest
needs: [pytest-coverage]
steps:
# checkout necessary to ensure the uploaded report contains the correct paths
- uses: actions/checkout@v4
- name: Retrieve coverage reports
uses: actions/download-artifact@v4
with:
pattern: coverage-*
path: ./reports/
- name: Upload all coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./reports/
flags: unittests
env_vars: OS,PYTHON
name: codecov-umbrella
73 changes: 43 additions & 30 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ env:
PKCS11_TOKEN_PIN: "1234"
PKCS11_TOKEN_SO_PIN: "5678"
jobs:
run:
tests:
runs-on: ${{ matrix.os }}
strategy:
# Our test suite is pretty fast, so fail-fast: false allows for better troubleshooting.
Expand All @@ -27,46 +27,59 @@ jobs:
- "3.11"
- "3.12"
- "3.13"
pkcs11-platform:
- softhsm
- opencryptoki
exclude:
# only run opencryptoki tests on ubuntu
# (macos and windows don't seem to be supported)
- pkcs11-platform: opencryptoki
os: windows-latest
- pkcs11-platform: opencryptoki
os: macos-latest
steps:
- name: Acquire sources
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: ./.github/actions/install-softhsm
id: softhsm
- uses: ./.github/actions/test-setup
id: setup
with:
os: ${{ matrix.os }}
token-label: ${{ env.PKCS11_TOKEN_LABEL }}
token-so-pin: ${{ env.PKCS11_TOKEN_SO_PIN }}
token-user-pin: ${{ env.PKCS11_TOKEN_PIN }}
- uses: ./.github/actions/install-opencryptoki
# only run opencryptoki tests on ubuntu
# (macos and windows don't seem to be supported)
if: matrix.os == 'ubuntu-latest'
id: opencryptoki
python-version: ${{ matrix.python-version }}
pkcs11-platform: ${{ matrix.pkcs11-platform }}
dependency-group: testing
- name: Run tests
run: uv run pytest -v
env:
PKCS11_MODULE: ${{ steps.setup.outputs.module }}
multilib-tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
steps:
- name: Acquire sources
uses: actions/checkout@v4
- uses: ./.github/actions/test-setup
id: setup
with:
os: ${{ matrix.os }}
os: ubuntu-latest
pkcs11-platform: multi
token-label: ${{ env.PKCS11_TOKEN_LABEL }}
token-so-pin: ${{ env.PKCS11_TOKEN_SO_PIN }}
token-user-pin: ${{ env.PKCS11_TOKEN_PIN }}
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
- name: Install testing dependencies
run: uv sync --no-dev --exact --group testing
- name: Run tests with SoftHSM
run: uv run pytest -v
env:
PKCS11_MODULE: ${{ steps.softhsm.outputs.module }}
- name: Run tests with opencryptoki
if: matrix.os == 'ubuntu-latest'
run: uv run pytest -v
dependency-group: testing
- name: Run tests
run: uv run pytest -v tests/test_multilib.py
env:
PKCS11_MODULE: ${{ steps.opencryptoki.outputs.module }}
# For testing logic around swapping PKCS#11 libs
PKCS11_MODULE2: ${{ steps.softhsm.outputs.module }}
PKCS11_MODULE: ${{ steps.setup.outputs.module }}
PKCS11_MODULE2: ${{ steps.setup.outputs.module2 }}
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ __pycache__
/dist/
/docs/_build
/python_pkcs11.egg-info/
/.eggs/
/.eggs/
.coverage
*coverage.xml
*.html
8 changes: 4 additions & 4 deletions pkcs11/_pkcs11.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ Definitions imported from PKCS11 C headers.

from cython.view cimport array

from pkcs11.defaults import *
from pkcs11.exceptions import *

cdef extern from '../extern/cryptoki.h':
Expand Down Expand Up @@ -638,10 +637,11 @@ cdef inline CK_ULONG_buffer(length):
return array(shape=(length,), itemsize=sizeof(CK_ULONG), format='L')


cdef inline object map_rv_to_error(
CK_RV rv
):
# Note: this `cdef inline` declaration doesn't seem to be consistently labelled
# as executed by Cython's line tracing, so we flag it as nocover
# to avoid noise in the metrics.

cdef inline object map_rv_to_error(CK_RV rv): # pragma: nocover
if rv == CKR_ATTRIBUTE_TYPE_INVALID:
exc = AttributeTypeInvalid()
elif rv == CKR_ATTRIBUTE_VALUE_INVALID:
Expand Down
2 changes: 1 addition & 1 deletion pkcs11/_pkcs11.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!python
#cython: language_level=3
#coverage#cython: linetrace=True
"""
High-level Python PKCS#11 Wrapper.

Expand Down
23 changes: 23 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,35 @@ archs = ["universal2"]
[tool.setuptools.packages.find]
include = ["pkcs11*"]

[tool.coverage.run]
plugins = ["Cython.Coverage"]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"pragma: nocover",
"raise AssertionError",
"raise NotImplementedError",
"raise MemoryError",
"raise TypeError",
"TYPE_CHECKING",
"^\\s*\\.\\.\\.",
"noqa"
]
precision = 2

[dependency-groups]
testing = [
"cryptography>=44.0.0",
"parameterized>=0.9.0",
"pytest>=8.3.4",
]
coverage = [
{ include-group = "testing" },
"coverage>=7.9.1",
"pytest-cov>=4.0,<6.3",
"cython",
]
docs = [
"sphinx>=7.4.7",
"sphinx-rtd-theme>=3.0.2",
Expand Down
26 changes: 26 additions & 0 deletions tests/test_multilib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
PKCS#11 Slots and Tokens
"""

import os
import unittest

import pkcs11

from . import LIB


@unittest.skipUnless("PKCS11_MODULE2" in os.environ, "Requires an additional PKCS#11 module")
class MultilibTests(unittest.TestCase):
def test_double_initialise_different_libs(self):
lib1 = pkcs11.lib(LIB)
lib2 = pkcs11.lib(os.environ["PKCS11_MODULE2"])
self.assertIsNotNone(lib1)
self.assertIsNotNone(lib2)
self.assertIsNot(lib1, lib2)

slots1 = lib1.get_slots()
slots2 = lib2.get_slots()

self.assertGreaterEqual(len(slots1), 1)
self.assertGreaterEqual(len(slots2), 1)
Loading
Loading