<div align="right">
<img src="img/p11ex-logo-400x400.png" alt="p11ex logo" width="100">
</div>
# p11ex --- PKCS#11 bindings for Elixir
`p11ex` is an Elixir library that provides access to the [PKCS#11 interface](https://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html) for cryptographic tokens such as Hardware Security Modules and smartcards. The library exposes most PKCS#11 functionality to Elixir, though it is not yet feature complete. Available functions include:
- `C_GetSlotList`: List tokens
- `C_GetTokenInfo`: Retrieve information about a token
- `C_OpenSession`: Open a new PKCS#11 session
- `C_CloseSession`: Close a PKCS#11 session
- `C_CloseAllSession`: Close all open sessions for a token
- `C_GetSessionInfo`: Retrieve status information about a session
- `C_Login`: Authenticate an open session
- `C_Logout`: Deauthenticate an open session
- `C_GenerateKey`: Generate a symmetric key
- `C_FindObjects`: Search for objects stored in the token
- `C_GetAttributeValue`: Retrieve attributes of an object
- `C_EncryptInit`, `C_Encrypt`, `C_EncryptUpdate`, and `C_EncryptFinal`: Encryption in chunks and as a complete block
- `C_DecryptInit`, `C_Decrypt`, `C_DecryptUpdate`, and `C_DecryptFinal`: Decryption in chunks and as a complete block
- `C_GenerateRandom`: Generate random bytes using the token
- `C_DestroyObject`: Delete objects in the token or session
- `C_GetMechanismList`: List cryptographic mechanisms supported by token
- `C_GetMechanismInfo`: Retrieve information about a mechanism
- `C_SignInit`, `C_Sign`, `C_SignUpdate`, `C_SignFinal`: Sign data in chunks and as a complete block
- `C_VerifyInit`, `C_Verify`: Verify a signature
- `C_DigestInit`, `C_Digest`, `C_DigestUpdate`, `C_DigestFinal`: Compute a hash digest in the token
- `C_GenKeyPair`: Generate asymmetric key pair
- `C_WrapKey`: Encrypt an extractable key and make it exportable
- `C_UnwrapKey`: Decrypt an exported key into the token
Some PKCS#11 functions require mechanism parameters as arguments. Common parameter types are supported and documented in the Elixir documentation.
The implementation is automatically tested with [SoftHSM](https://github.com/softhsm/softHSMv2) on Linux (AMD64 and ARM64) and macOS (ARM64). Additional tests are available for the [Yubikey PKCS#11 module](https://developers.yubico.com/yubico-piv-tool/YKCS11/), though these do not run automatically as part of the build.
## Build Prerequisite: PKCS#11 Headers
The NIF build expects PKCS#11 C headers in `c_src/`.
In CI these files are downloaded before compilation; for local builds, do the same manually:
```bash
cd c_src
curl -fL -O https://raw.githubusercontent.com/oasis-tcs/pkcs11/master/published/3-01/pkcs11.h
curl -fL -O https://raw.githubusercontent.com/oasis-tcs/pkcs11/master/published/3-01/pkcs11t.h
curl -fL -O https://raw.githubusercontent.com/oasis-tcs/pkcs11/master/published/3-01/pkcs11f.h
cd ..
mix deps.compile p11ex --force
```
Do not commit downloaded header files accidentally if your workflow stores them outside the source tree.
## p11ex_cli --- CLI program to use PKCS#11 tokens
The project also includes a CLI program named `p11ex_cli` for working with cryptographic tokens. This program provides access to key `p11ex` functions.
### Available Commands
- `slot-list`: List available PKCS#11 slots/tokens
- `object-list`: List objects (keys, certificates) on a token
- `key-gen-aes`: Generate AES symmetric keys
- `export-pubk`: Export public keys (RSA, EC, EdDSA)
- `sign`: Sign data with RSA, EC, or EdDSA algorithms
- `key-wrap` / `key-unwrap`: Wrap/unwrap keys for secure transport
- `kcv-gen`: Compute Key Check Value (fingerprint) of secret keys
- `bench-aes-encrypt-block`: Benchmark AES encryption performance
For detailed documentation on each command, run `p11ex_cli <command> --help`.
### EdDSA Support
p11ex_cli supports EdDSA signatures (Ed25519, Ed448). Use the `sign` command with the appropriate mechanism:
```bash
p11ex_cli sign --mechanism Ed25519 --key-label my-ed25519-key --data-file input.txt --signature-file output.sig
```
## Release Process
The release process is semi-automated using three GitHub Actions workflows. Before creating a release, ensure the `CHANGELOG.md` is updated with the changes to be released.
### Prerequisites
- Commits must follow [Conventional Commits](https://www.conventionalcommits.org/) format
- `CHANGELOG.md` should have an "Unreleased" section with pending changes
- Required secrets must be configured in repository settings (see RELEASE.md for details)
### Three Workflows Involved
The release process uses three separate workflows that should be triggered in order:
#### 1. Release Library to Hex.pm (`.github/workflows/release-lib-hexpm.yml`)
This workflow:
- Calculates the next version based on conventional commits
- Updates `mix.exs` with the new version
- Creates a git tag (e.g., `v0.4.0`)
- Builds documentation and Hex package
- **Performs a dry-run build test** to verify the package structure (Makefile, required files)
- Publishes the package to [Hex.pm](https://hex.pm/packages/p11ex)
- Triggered manually via Actions UI
**Run this first** - it creates the git tag needed by the other workflows. The dry-run test ensures the package can be compiled correctly before publishing.
#### 2. Release CLI Binaries (`.github/workflows/release-cli.yml`)
This workflow:
- Builds `p11ex_cli` binaries for Linux (amd64, arm64) and macOS (arm64)
- Creates tar.gz archives of the binaries
- Generates SHA256 checksums
- Uploads release artifacts
- Requires a version tag as input (must match a git tag from step 1)
- Triggered manually via Actions UI
**Run this second** - after the git tag exists from step 1.
#### 3. Create Codeberg Release (`.github/workflows/release-lib-codeberg.yml`)
This workflow:
- Creates a release on Codeberg (Forgejo) using the Forgejo API
- Uses the git tag created in step 1
- Generates release notes referencing CHANGELOG.md
- Provides installation instructions in the release description
- Requires tag name and version as inputs
- Triggered manually via Actions UI
**Run this third** - after the git tag exists from step 1.
### Release Steps
1. **Update CHANGELOG.md**
- Move items from "Unreleased" section to a new version section
- Follow the [Keep a Changelog](https://keepachangelog.com/) format
- Commit and push the changelog update
2. **Trigger Library Release**
- Go to Actions → "Release p11ex library to Hex.pm"
- Click "Run workflow"
- The workflow will calculate version, update files, and publish
3. **Trigger CLI Release**
- Wait for library release to complete and tag to be created
- Go to Actions → "Release p11ex-cli binaries"
- Click "Run workflow"
- Enter the version tag (e.g., `0.4.0`)
- Download the release artifacts from the workflow run
4. **Create GitHub/Codeberg Release** (if applicable)
- Create a release on the repository using the git tag
- Upload the CLI binaries from the workflow artifacts
- Add release notes from CHANGELOG.md
For detailed information about the release process, troubleshooting, and manual release procedures, see [RELEASE.md](RELEASE.md).
### Quick Reference
| Workflow | Trigger | Input | Output |
|----------|---------|--------|--------|
| `release-lib-hexpm.yml` | Manual | None | Hex.pm package, git tag |
| `release-cli.yml` | Manual | Version tag | CLI binaries, checksums |
| `release-lib-codeberg.yml` | Manual | Tag name, version | Codeberg release |
**Note:** The Docker workflows (`.github/workflows/docker-base.yml` and `.github/workflows/docker-build.yml`) build and maintain the test infrastructure images used by the release workflows. They run automatically on a schedule (twice weekly) or can be triggered manually if needed.