Current section

Files

Jump to
p11ex README.md
Raw

README.md

<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/v3.1/os/pkcs11-base-v3.1-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_GetInfo`: Retrieve general information about the loaded PKCS#11 module
- `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_SeedRandom`: Mix additional seed material into the token's RNG
- `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.

## Not Yet Supported

The following are not (yet) available. If you need one of them, please open an issue.

- **Object creation and modification**: `C_CreateObject`, `C_SetAttributeValue`, `C_CopyObject`. Key material and certificates can only enter a token via generation or `C_UnwrapKey`, and attributes cannot be changed after an object is created.
- **Multi-part verification**: `C_VerifyUpdate` and `C_VerifyFinal`. Signatures over large inputs must be verified with `C_Verify` in one call.
- **Token administration**: `C_InitToken`, `C_InitPIN`, `C_SetPIN`. Use `softhsm2-util` or `pkcs11-tool` to initialize tokens and manage PINs.
- **Mechanism parameters** for mechanisms other than AES-GCM, AES-CBC/OFB, AES-CTR, AES-CCM, AES-CMAC-GENERAL, RSA-PSS (`CKM_RSA_PKCS_PSS` only, see below), RSA-OAEP and ECDH1-DERIVE. Notably, `CKM_AES_CBC_PAD`, the CFB/CTS/XTS modes, `CKM_RSA_AES_KEY_WRAP` and the HKDF and SP800-108 key derivation mechanisms cannot be used yet. Mechanisms that take no parameters work regardless of whether they are named in the library.
- **RSA-PSS mechanisms that digest on the token**: `CKM_SHA1_RSA_PKCS_PSS`, `CKM_SHA224_RSA_PKCS_PSS`, `CKM_SHA256_RSA_PKCS_PSS`, `CKM_SHA384_RSA_PKCS_PSS` and `CKM_SHA512_RSA_PKCS_PSS`. These take `CK_RSA_PKCS_PSS_PARAMS` as well, but the parameters are only wired up for `CKM_RSA_PKCS_PSS`. Use `CKM_RSA_PKCS_PSS` instead and digest the data before signing.
- **PKCS#11 3.0 additions**: the module is loaded via `C_GetFunctionList`, so the 3.0 function set is unavailable. This includes the message-based API (`C_EncryptMessage` and friends), `C_LoginUser` and `C_SessionCancel`.
- **Miscellaneous**: `C_WaitForSlotEvent`, `C_GetObjectSize`, `C_DigestKey`, the operation-state functions and the legacy recovery and dual-function operations.

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.

## Concurrency and Schedulers

All PKCS#11 NIFs in `p11ex` run on the dirty I/O scheduler pool to prevent blocking the normal schedulers when calling slow HSM operations (network round-trips, USB transactions). This ensures the Erlang VM remains responsive even during long-running cryptographic operations.

For high-concurrency applications using network-attached HSMs, you may need to increase the dirty I/O scheduler pool size using the `+SDio` emulator flag (default is 10 threads). Example:

```bash
ERL_FLAGS="+SDio 20" mix test
```

## Telemetry

`p11ex` emits [`:telemetry`](https://hexdocs.pm/telemetry) events for every operation that reaches the PKCS#11 module, plus session, token and module lifecycle events. This lets a host application track per-operation latency and error rates by PKCS#11 return code, which is what you need when the token is a network HSM. No reporter or metrics backend is bundled; attach your own handlers.

```elixir
:telemetry.attach("p11ex-latency", [:p11ex, :session, :operation, :stop], fn _event, measurements, metadata, _config ->
  duration = System.convert_time_unit(measurements.duration, :native, :microsecond)
  IO.puts("#{metadata.operation} took #{duration}us (#{metadata.result})")
end, nil)
```

See the [Telemetry guide](guides/telemetry.md) for the full event reference, including which values are deliberately never emitted.

## 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.