Development Guide
=================
This guide covers setting up a development environment for hsmkey, running tests,
and understanding the test infrastructure.
Prerequisites
-------------
Before starting development, ensure you have the following installed:
- Python 3.10 or later
- `uv `_ - Fast Python package manager
- `just `_ - Command runner
- OpenSSL - For generating test keys
- One of the following HSM backends:
- SoftHSM2 (recommended for development)
- Kryoptic (Rust-based PKCS#11 token)
Installing Prerequisites
^^^^^^^^^^^^^^^^^^^^^^^^
**Ubuntu/Debian:**
.. code-block:: bash
# Install system packages
sudo apt-get update
sudo apt-get install -y softhsm2 opensc openssl
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install just
cargo install just
# Or use the package manager
sudo apt-get install -y just
**Fedora/RHEL:**
.. code-block:: bash
sudo dnf install softhsm opensc openssl
curl -LsSf https://astral.sh/uv/install.sh | sh
cargo install just
**macOS:**
.. code-block:: bash
brew install softhsm openssl just
curl -LsSf https://astral.sh/uv/install.sh | sh
Setting Up the Development Environment
--------------------------------------
1. Clone the Repository
^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: bash
git clone https://github.com/kushaldas/hsmkey
cd hsmkey
2. Create Virtual Environment and Install Dependencies
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Using ``uv``, create a virtual environment and install all dependencies:
.. code-block:: bash
# Sync all dependencies including dev extras
uv sync --extra dev
This installs:
- Core dependencies (``cryptography``, ``python-pkcs11``)
- JWCrypto integration (``jwcrypto``)
- Development tools (``pytest``, ``pytest-cov``, ``ruff``, ``ty``)
3. Configure SoftHSM2
^^^^^^^^^^^^^^^^^^^^^
Create the SoftHSM2 configuration:
.. code-block:: bash
# Create configuration directory
mkdir -p ~/.config/softhsm
mkdir -p ~/.local/share/softhsm/tokens
# Create configuration file
cat > ~/.config/softhsm/softhsm2.conf << EOF
directories.tokendir = $HOME/.local/share/softhsm/tokens
objectstore.backend = file
log.level = INFO
slots.removable = false
EOF
# Set environment variable (add to ~/.bashrc for persistence)
export SOFTHSM2_CONF="$HOME/.config/softhsm/softhsm2.conf"
4. Generate Test Keys and Initialize HSM
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use the provided justfile commands:
.. code-block:: bash
# Full setup: generate keys and import to HSM
just setup
This runs:
- ``just recreate-keys`` - Generates RSA, EC, and EdDSA test keys
- ``just import-keys`` - Imports keys to SoftHSM2
Running Tests
-------------
Basic Test Commands
^^^^^^^^^^^^^^^^^^^
.. code-block:: bash
# Run all tests
just test
# Run tests with verbose output
uv run pytest tests/ -v
# Run specific test file
uv run pytest tests/test_rsa.py -v
# Run specific test class
uv run pytest tests/test_jws_hsm.py::TestJWSRSASigning -v
# Run specific test
uv run pytest tests/test_jws_hsm.py::TestJWSRSASigning::test_rs256_sign -v
Test Coverage
^^^^^^^^^^^^^
.. code-block:: bash
# Run tests with coverage report
just test-cov
# Or directly with pytest
uv run pytest tests/ -v --cov=src/hsmkey --cov-report=term-missing
# Generate HTML coverage report
uv run pytest tests/ --cov=src/hsmkey --cov-report=html
# Open htmlcov/index.html in browser
Test Structure
--------------
The test suite is organized as follows:
.. code-block:: text
tests/
├── conftest.py # Pytest fixtures and configuration
├── test_rsa.py # RSA key tests
├── test_ec.py # Elliptic curve key tests
├── test_ed25519.py # Ed25519 key tests
├── test_ed448.py # Ed448 key tests
├── test_jws_hsm.py # JWS signing/verification tests
├── test_jwe_hsm.py # JWE encryption/decryption tests
├── test_jwt_hsm.py # JWT token tests
└── test_integration.py # Integration and interoperability tests
Test Fixtures
^^^^^^^^^^^^^
The ``conftest.py`` file provides shared fixtures:
.. code-block:: python
# HSM session fixture - provides authenticated session
def test_example(hsm_session):
key = HSMJWK.from_hsm(hsm_session, key_label="rsa-2048")
# ... use key
# Session pool fixture - for connection management tests
def test_pool(session_pool):
with session_pool.session() as session:
# ... use session
Environment Variables
^^^^^^^^^^^^^^^^^^^^^
Tests can be configured via environment variables:
.. list-table::
:header-rows: 1
:widths: 30 50 20
* - Variable
- Description
- Default
* - ``HSM_MODULE``
- Path to PKCS#11 library
- ``/usr/lib/softhsm/libsofthsm2.so``
* - ``HSM_TOKEN_LABEL``
- Token label
- ``hsmkey-test``
* - ``HSM_PIN``
- User PIN
- ``12345678``
* - ``EDDSA_AVAILABLE``
- Whether EdDSA keys are available
- ``true``
Test Markers
^^^^^^^^^^^^
Custom pytest markers are used to conditionally skip tests:
.. code-block:: python
@pytest.mark.requires_eddsa
def test_ed25519_signing(hsm_session):
"""This test is skipped when EDDSA_AVAILABLE=false."""
key = HSMJWK.from_hsm(hsm_session, key_label="ed25519")
# ...
The ``requires_eddsa`` marker automatically skips tests when ``EDDSA_AVAILABLE=false``,
which is useful for testing with HSM backends that don't support EdDSA.
Testing with Kryoptic
---------------------
`Kryoptic `_ is an alternative Rust-based
PKCS#11 software token. It requires additional setup.
Building Kryoptic
^^^^^^^^^^^^^^^^^
.. code-block:: bash
# Clone Kryoptic
cd ~
git clone https://github.com/latchset/kryoptic.git
cd kryoptic
# Check your OpenSSL version
openssl version
**For systems with OpenSSL 3.2+ (Fedora 40+, etc.):**
.. code-block:: bash
# Build with full EdDSA support
cargo build --release --no-default-features \
--features "sqlitedb,aes,ecdsa,ecdh,eddsa,ffdh,hash,hmac,hkdf,pbkdf2,rsa,sp800_108,dynamic"
With EdDSA support enabled, you can run the full test suite without skipping EdDSA tests.
**For systems with OpenSSL < 3.2 (Ubuntu 24.04, Ubuntu 22.04, RHEL 9, etc.):**
.. code-block:: bash
# Build without EdDSA
cargo build --release --no-default-features \
--features "sqlitedb,aes,ecdsa,ecdh,ffdh,hash,hmac,hkdf,pbkdf2,rsa,sp800_108,dynamic"
Note: Ubuntu 24.04 ships with OpenSSL 3.0.x, which does not support EdDSA in Kryoptic.
Setting Up Kryoptic
^^^^^^^^^^^^^^^^^^^
.. code-block:: bash
# Create configuration
just ensure-kryoptic-config
# Initialize token and import keys
just setup-kryoptic
Running Tests with Kryoptic
^^^^^^^^^^^^^^^^^^^^^^^^^^^
**If you built Kryoptic with EdDSA support (OpenSSL 3.2+):**
.. code-block:: bash
# Setup Kryoptic with RSA/EC keys
just setup-kryoptic
# Import EdDSA keys
just import-eddsa-kryoptic
# Run full test suite
just test-kryoptic-full
**If you built Kryoptic without EdDSA support:**
.. code-block:: bash
# Run tests against Kryoptic (skips EdDSA tests)
just test-kryoptic
The ``just test-kryoptic`` command automatically:
- Sets ``HSM_MODULE`` to the Kryoptic library path
- Sets ``EDDSA_AVAILABLE=false`` to skip EdDSA tests
- Ignores standalone EdDSA test files
Kryoptic and EdDSA Support
^^^^^^^^^^^^^^^^^^^^^^^^^^
EdDSA (Ed25519/Ed448) support in Kryoptic depends on your OpenSSL version:
.. list-table::
:header-rows: 1
:widths: 30 20 50
* - Distribution
- OpenSSL Version
- EdDSA Support
* - Fedora 40+
- 3.2+
- Full support
* - Ubuntu 24.04
- 3.0.13
- Not supported
* - Ubuntu 22.04
- 3.0.x
- Not supported
* - RHEL 9 / Rocky 9
- 3.0.x
- Not supported
To check your OpenSSL version:
.. code-block:: bash
openssl version
If you have OpenSSL 3.2+, rebuild Kryoptic with the ``eddsa`` feature to enable
full EdDSA support.
Available Justfile Commands
---------------------------
Run ``just`` to see all available commands:
.. code-block:: bash
just # List all commands
**Key Management:**
.. code-block:: bash
just recreate-keys # Generate test keys on disk
just init-hsm # Initialize SoftHSM2 token
just import-keys # Import keys to SoftHSM2
just list-keys # List keys in HSM
just delete-hsm # Delete the test token
just setup # Full setup (recreate-keys + import-keys)
just reset # Delete token and reimport keys
**Testing:**
.. code-block:: bash
just test # Run all tests
just test-cov # Run tests with coverage
just lint # Run type checker
**Kryoptic:**
.. code-block:: bash
just ensure-kryoptic-config # Create Kryoptic config
just init-kryoptic # Initialize Kryoptic token
just import-keys-kryoptic # Import RSA/EC keys to Kryoptic
just import-eddsa-kryoptic # Import EdDSA keys (requires OpenSSL 3.2+)
just list-keys-kryoptic # List keys in Kryoptic
just setup-kryoptic # Full Kryoptic setup (without EdDSA)
just test-kryoptic # Run tests without EdDSA
just test-kryoptic-full # Run full tests with EdDSA (OpenSSL 3.2+)
just clean-kryoptic # Clean Kryoptic storage
**Documentation:**
.. code-block:: bash
just docs # Build Sphinx documentation
just docs-serve # Build and serve docs locally
**Cleanup:**
.. code-block:: bash
just clean # Clean generated files
Code Style and Linting
----------------------
Type Checking
^^^^^^^^^^^^^
.. code-block:: bash
# Run type checker
just lint
# Or directly
uv run ty check src/
Note: The ``python-pkcs11`` library lacks type stubs, so some type errors are expected.
Code Formatting
^^^^^^^^^^^^^^^
The project uses ``ruff`` for linting and formatting:
.. code-block:: bash
# Check formatting
uv run ruff check src/ tests/
# Auto-fix issues
uv run ruff check --fix src/ tests/
# Format code
uv run ruff format src/ tests/
Building Documentation
----------------------
The documentation uses Sphinx with the Read the Docs theme:
.. code-block:: bash
# Install docs dependencies
uv sync --extra docs
# Build HTML documentation
just docs
# Or manually
cd docs && uv run make html
# View locally
open docs/_build/html/index.html
Building the Package
--------------------
To build the package for distribution:
.. code-block:: bash
# Build wheel and sdist
uv build
# Output in dist/
ls dist/
Continuous Integration
----------------------
The project uses GitHub Actions for CI. The workflow:
1. **Lint** - Runs type checking
2. **Test with SoftHSM2** - Full test suite with SoftHSM2
3. **Test with Kryoptic** - Test suite with Kryoptic (EdDSA tests skipped)
4. **Build** - Builds the package
CI Configuration
^^^^^^^^^^^^^^^^
The CI automatically handles:
- Installing SoftHSM2 or building Kryoptic
- Setting up HSM tokens
- Importing test keys
- Running tests with appropriate environment variables
For Kryoptic tests, ``EDDSA_AVAILABLE=false`` is set to skip EdDSA tests.
Troubleshooting
---------------
Common Issues
^^^^^^^^^^^^^
**"HSM module not found"**
.. code-block:: bash
# Check SoftHSM2 installation
ls -la /usr/lib/softhsm/libsofthsm2.so
# or
ls -la /usr/lib64/softhsm/libsofthsm2.so
# Set correct path
export HSM_MODULE="/path/to/libsofthsm2.so"
**"Token not found"**
.. code-block:: bash
# Ensure SOFTHSM2_CONF is set
export SOFTHSM2_CONF="$HOME/.config/softhsm/softhsm2.conf"
# List available tokens
softhsm2-util --show-slots
# Reinitialize if needed
just reset
**"Key not found"**
.. code-block:: bash
# List keys in token
just list-keys
# Reimport keys
just import-keys
**"EdDSA tests failing with Kryoptic"**
EdDSA (Ed25519/Ed448) requires OpenSSL 3.2+ with Kryoptic. On older systems:
.. code-block:: bash
# Run with EdDSA tests skipped
EDDSA_AVAILABLE=false just test-kryoptic
Contributing
------------
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests: ``just test``
5. Run linter: ``just lint``
6. Submit a pull request
Ensure all tests pass and code follows the project style before submitting.