diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 12cfbd3..0033861 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -1,6 +1,8 @@ name: Code quality on: push: +env: + UV_PYTHON_PREFERENCE: only-system jobs: run: runs-on: ubuntu-latest @@ -20,7 +22,7 @@ jobs: architecture: x64 - name: Install dev dependencies - run: uv sync --python-preference only-system + run: uv sync - name: ruff format run: uv run ruff format --diff . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..9b116a3 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,57 @@ +name: Tests +on: + push: +env: + UV_PYTHON_PREFERENCE: only-system + PKCS11_TOKEN_LABEL: TEST + PKCS11_TOKEN_PIN: 1234 + PKCS11_TOKEN_SO_PIN: 5678 +jobs: + run: + # Run in Ubuntu 22.04 right now, as oscrypto fails on OpenSSL versions with a + # double-digit patch number (such as provided by Ubuntu 24.04): + # https://community.snowflake.com/s/article/Python-Connector-fails-to-connect-with-LibraryNotFoundError-Error-detecting-the-version-of-libcrypto + # https://github.com/wbond/oscrypto/issues/78 + runs-on: ubuntu-22.04 + strategy: + matrix: + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + + steps: + - name: Acquire sources + uses: actions/checkout@v4.1.1 + + - name: Setup Python + uses: actions/setup-python@v5.0.0 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + python-version: ${{ matrix.python-version }} + + - name: Install dev dependencies + run: uv sync --all-extras + + # Locally compile softhsmv2. For unknown reasons, the version installed by Ubuntu fails on + # Github Actions (while working e.g. in Docker). + - name: Install Softhsm + run: | + curl https://dist.opendnssec.org/source/softhsm-2.6.1.tar.gz | tar -zxv + (cd softhsm-2.6.1 && ./configure --prefix=$HOME --disable-p11-kit --disable-gost && make all install CC="gcc" CXX="g++") + echo "$HOME/bin" >> "$GITHUB_PATH" + echo "PKCS11_MODULE=$HOME/lib/softhsm/libsofthsm2.so" >> "$GITHUB_ENV" + + - name: Initialize token + run: softhsm2-util --init-token --free --label $PKCS11_TOKEN_LABEL --pin $PKCS11_TOKEN_PIN --so-pin $PKCS11_TOKEN_SO_PIN + + - name: Run tests + run: uv run pytest -v \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 58915ca..0000000 --- a/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -sudo: false -language: python - -python: - - '3.5' - - '3.6' - - '3.7' - - '3.8' - -env: - global: - - PKCS11_MODULE=/home/travis/lib/softhsm/libsofthsm2.so - - PKCS11_TOKEN_LABEL=TEST - - PKCS11_TOKEN_PIN=1234 - - PKCS11_TOKEN_SO_PIN=5678 - -cache: - - pip - - ccache # For SoftHSMv2 - -before_install: - - pip install -U pip setuptools - - pip install -r dev-requirements.txt - # Install SoftHSMv2 - - curl https://dist.opendnssec.org/source/softhsm-2.5.0.tar.gz | tar -zxv - - (cd softhsm-2.5.0 && ./configure --prefix=$HOME --disable-p11-kit --disable-gost && make all install CC="ccache gcc" CXX="ccache g++") - -before_script: - # Initialise a token on the SoftHSM - - $HOME/bin/softhsm2-util --init-token --free --label TEST --pin 1234 --so-pin 5678 - # Build our extension - - python setup.py build_ext -i - -script: python -m unittest - - -deploy: - provider: pypi - user: danni - password: - secure: "A/W51+GTE9CBAm4m+1AVg11EAF63BUBrCXIonmYCdTT2htEGStk9AJnZGOinHPhwgJoWujBqgqyjqm8wJSvsmhyPSWxGk20lkCJOptcHdExu4FoSnLNNzAgPtZH5lLarkpvxB20J9hUUb4CQbgz5BWeNqFPvKigKFworCksRr9EM4J/Ys8tmkI2zwSTRDH2YAmhI/h8BWGpHMP+pNUsjlp9ZbDaxgNY85r7RloP07N5R0A7TPePH8wJzuGMDOv8dLazdr0epCbvFk+2CyJ7KiEJoX+SlS/2Hi7OKnmuf7QG2z2YyukLlJcP+IhRfzZDVgUeXwJbu24XAUdMoBS8OxId8dOKFla+GJScpWGpA9rO5vgItTAYLG7sd9HuveCtvUZxqbJd5teST4PdcxjjeO5LxYkgXKrLEo1dvDtPOm/veA0axFrXzlberJCKyN2T6grfM5QVUCORQnUYPnOqkYXMHFKSIUfa2mpfJ8NZaHR7jEbddU/PpQTHwlcehMtTx9IKQyfzillmhiXVc+UMOInFbEsU4oD9f0eP2fcs2dDB3ppR+Rdkh8bb80zH2r55Giu4Fv/WGcllwaYvMGkm6TBdod/Hva4sypJaLkLFDH6LQ0jOBHfPdFYKzEn16kZ1F4X+QWl6lKiz4XWAJI8Cf73y9Acj1Q+4MAHobkE/908M=" - skip_upload_docs: true - on: - tags: true - python: 3.6 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a62fea0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +ARG IMAGE=debian:stable +FROM $IMAGE + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" apt-get install -y gcc python3 python3-dev softhsm2 openssl && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +WORKDIR /test + +ADD uv.lock pyproject.toml setup.py . +ADD pkcs11/ pkcs11/ +ADD extern/ extern/ + +ENV UV_LINK_MODE=copy +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --all-extras + +ENV PKCS11_MODULE=/usr/lib/softhsm/libsofthsm2.so +ENV PKCS11_TOKEN_LABEL=TEST +ENV PKCS11_TOKEN_PIN=1234 +ENV PKCS11_TOKEN_SO_PIN=5678 +RUN softhsm2-util --init-token --free --label TEST --pin 1234 --so-pin 5678 + +ADD tests/ tests/ +CMD ["uv", "run", "pytest", "-v"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f857ee8..cde45ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,9 +60,11 @@ include = ["pkcs11*"] dev = [ "cryptography>=44.0.0", "oscrypto>=1.3.0", + "parameterized>=0.9.0", + "pytest>=8.3.4", "ruff>=0.8.3", "setuptools>=75.6.0", "setuptools-scm>=8.1.0", "sphinx>=7.4.7", "sphinx-rtd-theme>=3.0.2", -] \ No newline at end of file +] diff --git a/tests/test_x509.py b/tests/test_x509.py index b4a7dca..e6ce991 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -5,6 +5,7 @@ import base64 import datetime import subprocess +import tempfile from asn1crypto import pem from asn1crypto.csr import CertificationRequest, CertificationRequestInfo @@ -223,13 +224,21 @@ def test_self_sign_certificate(self): } ) - # Pipe our certificate to OpenSSL to verify it - with subprocess.Popen( - (OPENSSL, "verify"), stdin=subprocess.PIPE, stdout=subprocess.DEVNULL - ) as proc: - proc.stdin.write(pem.armor("CERTIFICATE", cert.dump())) - proc.stdin.close() - self.assertEqual(proc.wait(), 0) + pem_cert = pem.armor("CERTIFICATE", cert.dump()) + + with tempfile.NamedTemporaryFile() as pem_file: + pem_file.write(pem_cert) + pem_file.flush() + + # Pipe our certificate to OpenSSL to verify it + with subprocess.Popen( + (OPENSSL, "verify", "-CAfile", pem_file.name), + stdin=subprocess.PIPE, + stdout=subprocess.DEVNULL, + ) as proc: + proc.stdin.write(pem.armor("CERTIFICATE", cert.dump())) + proc.stdin.close() + self.assertEqual(proc.wait(), 0) @Only.openssl @requires(Mechanism.RSA_PKCS_KEY_PAIR_GEN, Mechanism.SHA1_RSA_PKCS) diff --git a/uv.lock b/uv.lock index 413a2bb..662bb30 100644 --- a/uv.lock +++ b/uv.lock @@ -245,6 +245,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + [[package]] name = "idna" version = "3.10" @@ -275,6 +284,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, ] +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + [[package]] name = "jinja2" version = "3.1.4" @@ -376,6 +394,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] +[[package]] +name = "parameterized" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1", size = 24351 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + [[package]] name = "pycparser" version = "2.22" @@ -394,6 +430,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, ] +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + [[package]] name = "python-pkcs11" version = "0.7.0" @@ -406,6 +459,8 @@ dependencies = [ dev = [ { name = "cryptography" }, { name = "oscrypto" }, + { name = "parameterized" }, + { name = "pytest" }, { name = "ruff" }, { name = "setuptools" }, { name = "setuptools-scm" }, @@ -420,6 +475,8 @@ requires-dist = [{ name = "asn1crypto", specifier = ">=1.4.0" }] dev = [ { name = "cryptography", specifier = ">=44.0.0" }, { name = "oscrypto", specifier = ">=1.3.0" }, + { name = "parameterized", specifier = ">=0.9.0" }, + { name = "pytest", specifier = ">=8.3.4" }, { name = "ruff", specifier = ">=0.8.3" }, { name = "setuptools", specifier = ">=75.6.0" }, { name = "setuptools-scm", specifier = ">=8.1.0" },