p11ex --- PKCS#11 bindings for Elixir
p11ex is an Elixir library that provides access to the PKCS#11 interface 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_CloseAllSessions: Close all open sessions for a tokenC_CloseSession: Close a PKCS#11 sessionC_CopyObject: Copy an object, optionally changing attributesC_CreateObject: Create an object, e.g. to import a keyC_DecryptInit,C_Decrypt,C_DecryptUpdate, andC_DecryptFinal: Decryption in chunks and as a complete blockC_DeriveKey: Derive a key from a base key (e.g. ECDH)C_DestroyObject: Delete objects in the token or sessionC_DigestInit,C_Digest,C_DigestUpdate,C_DigestFinal: Compute a hash digest in the tokenC_EncryptInit,C_Encrypt,C_EncryptUpdate, andC_EncryptFinal: Encryption in chunks and as a complete blockC_FindObjects: Search for objects stored in the tokenC_GenerateKey: Generate a symmetric keyC_GenerateKeyPair: Generate asymmetric key pairC_GenerateRandom: Generate random bytes using the tokenC_GetAttributeValue: Retrieve attributes of an objectC_GetInfo: Retrieve general information about the loaded PKCS#11 moduleC_GetMechanismInfo: Retrieve information about a mechanismC_GetMechanismList: List cryptographic mechanisms supported by tokenC_GetSessionInfo: Retrieve status information about a sessionC_GetSlotList: List tokensC_GetTokenInfo: Retrieve information about a tokenC_InitPIN: Set the user PIN (as SO)C_InitToken: Initialize a token with an SO PIN and a labelC_Login: Authenticate an open sessionC_Logout: Deauthenticate an open sessionC_OpenSession: Open a new PKCS#11 sessionC_SeedRandom: Mix additional seed material into the token's RNGC_SetAttributeValue: Change attributes of an objectC_SetPIN: Change the PIN of the logged-in userC_SignInit,C_Sign,C_SignUpdate,C_SignFinal: Sign data in chunks and as a complete blockC_UnwrapKey: Decrypt an exported key into the tokenC_VerifyInit,C_Verify: Verify a signatureC_WrapKey: Encrypt an extractable key and make it exportable
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.
- Multi-part verification:
C_VerifyUpdateandC_VerifyFinal. Signatures over large inputs must be verified withC_Verifyin one call. - Mechanism parameters for mechanisms other than AES-GCM, AES-CBC/CBC-PAD/OFB, AES-CTR, AES-CCM, AES-CMAC-GENERAL, RSA-PSS (
CKM_RSA_PKCS_PSSonly, see below), RSA-OAEP, EdDSA and ECDH1-DERIVE. Notably, the CFB/CTS/XTS modes,CKM_RSA_AES_KEY_WRAPand 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_PSSandCKM_SHA512_RSA_PKCS_PSS. These takeCK_RSA_PKCS_PSS_PARAMSas well, but the parameters are only wired up forCKM_RSA_PKCS_PSS. UseCKM_RSA_PKCS_PSSinstead 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_EncryptMessageand friends),C_LoginUserandC_SessionCancel. - Miscellaneous:
C_WaitForSlotEvent,C_GetObjectSize,C_DigestKey, the operation-state functions and the legacy recovery and dual-function operations.
Tested PKCS#11 Modules
The test suite runs automatically against three software tokens:
| Module | Version | Platforms | Notes |
|---|---|---|---|
| SoftHSM | 2.7.0 | Linux (AMD64, ARM64), macOS (ARM64); OTP 27–29 | The reference token and the only one with two tokens. Multi-part ECDSA with hashing (CKM_ECDSA_SHA256) is skipped: 2.7.0 lists it but rejects C_SignUpdate. |
| kryoptic | 1.5.3, with post-quantum mechanisms | Linux (AMD64); OTP 29 | Offers the PKCS#11 2.40, 3.0 and 3.2 interfaces. Multi-part AES-GCM decryption is skipped until a release contains latchset/kryoptic#519. |
| NSS softoken | as packaged in Ubuntu 26.04 (3.120) | Linux (AMD64); OTP 29 | Multi-part AES-GCM and Ed448 are unsupported. Ed25519 signing, single-part AES-GCM encryption and AES-CMAC verification are skipped because of NSS bugs (2075850, 2075851, 2075845). |
kryoptic and NSS each provide a single token, so the tests that need two tokens run on SoftHSM only. Tests that depend on one module's behaviour are tagged in the suite (@tag :<module>_only, @tag unsupported_on: :<module>).
Additional tests are available for the Yubikey PKCS#11 module, 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:
ERL_FLAGS="+SDio 20" mix test
Telemetry
p11ex emits :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.
: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 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
list-slots: List available PKCS#11 slots/tokenslist-objects: List objects (keys, certificates) on a tokenkey-gen-aes: Generate AES symmetric keysexport-pubk: Export public keys (RSA, EC, EdDSA)sign: Sign data with RSA, EC, or EdDSA algorithmskey-wrap/key-unwrap: Wrap/unwrap keys for secure transportkcv-gen: Compute Key Check Value (fingerprint) of secret keysbench-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 eddsa mechanism and digest none, since EdDSA signs the full message:
p11ex_cli sign --module /path/to/pkcs11-module.so --token-label my-token --pin-file pin.txt \
eddsa none label:my-ed25519-key input.txt output.sig
Release Process
Releases are made with manually triggered Forgejo Actions workflows on Codeberg: the library goes to Hex.pm, followed by optional Codeberg release notes and p11ex_cli binaries. See RELEASE.md for the checklist, the required secrets and troubleshooting.