All notable changes are documented here. The format follows Keep a Changelog, and this project adheres to Semantic Versioning.

This file tracks the pkcs11ex Hex package specifically. The sister packages (sign_core, soft_signer, pkcs11ex_audit) maintain their own changelogs in their respective directories.

[Unreleased]

Changed

  • Pkcs11ex.Slot.Pool.register/2 logs a warning when the same slot is re-registered with a different pool size. Previously silent — config drift between two startup paths could go unnoticed.
  • Pkcs11ex.PKCS12.unsupported_kdf_from_output/1 regex tightened. The previous \d+(?:\.\d+)+ matched the first dot-separated number anywhere in the openssl error — including the openssl version string ("OpenSSL 3.2.1"), which would be mis-reported as the unsupported KDF OID. New patterns anchor to "unknown algorithm <oid>", "unsupported algorithm <oid>", "OID = <oid>", "(OID: <oid>)" and require ≥ 3 OID arcs to filter out short version strings.
  • Mix.Tasks.Pkcs11ex.ImportP12 moduledoc spells out the BEAM-heap threat model around the plaintext PKCS#1 RSA private key transit. The implementation is unchanged; the threat model is now explicit so operators can size their mitigations.

Changed

  • NIF surfaces CKR_USER_ALREADY_LOGGED_IN as the typed atom :user_already_logged_in instead of an opaque {:pkcs11_error, msg} tuple. Pkcs11ex.Slot.Server matches on the atom directly, dropping the substring-match-on-error-message helper that would silently break on cryptoki release wording changes.
  • Pinned :rustler to ~> 0.37.0 (was ~> 0.36). Vec<u8> encoding shape differs between 0.36 and 0.37; the loose constraint left the NIF return shape ABI-unstable, which had to be papered over with multi-shape return handling in Pkcs11ex.sign_bytes/2 and Pkcs11ex.Slot.Server. Pin removes the ambiguity; redundant return-shape branches dropped.
  • Configurable Pkcs11ex.Slot.Server GenServer.call timeouts — per-call :call_timeout opt and :slot_call_timeout application-env knob. Previously hardcoded at 30s for sign/verify/login/logout/status and 60s for import_keypair, which silently truncated long-running operations on slow cloud HSMs.
  • Protocol consolidation re-enabled in :test. The fixture-only JWSTestSigner (with its defimpl SignCore.Signer) moved to test/support/, gated by elixirc_paths(:test). Previously consolidate_protocols: Mix.env() != :test worked around the in-test defimpl; production and test now run with identical consolidation behavior.

[0.1.0]

Initial Hex release. The repository hosts a family of packages from a single git tree (Phoenix-style monorepo); this changelog covers the pkcs11ex package — the PKCS#11 hardware provider.

Added — Hex publish setup

  • Conditional :sign_core and :pkcs11ex_audit deps: path during monorepo dev, Hex when consuming the published tarball.
  • Tightened package: files: … whitelist to ship lib/, the Rust NIF source, specs, README, and CHANGELOG.

Added — monorepo split

  • Pkcs11ex.Signer struct — implements SignCore.Signer protocol. Supports both the slot-ref form (%Pkcs11ex.Signer{slot_ref: :foo, key_ref: :bar}) and the explicit-module form (%Pkcs11ex.Signer{module: ..., slot_id: ..., key_label: ...}).
  • Pkcs11ex.PDF, Pkcs11ex.XML, Pkcs11ex.JWS — convenience wrappers around SignCore.{PDF,XML,JWS}. Translate the historical option ergonomics (signer: {slot_ref, key_ref}, module:/slot_id:/key_label:) into a Pkcs11ex.Signer struct that the format adapters dispatch on.
  • Pkcs11ex.X509 — backwards-compat alias for SignCore.X509.
  • Format-adapter primitives (PDF Reader/Writer, CMS, XML/XAdES, X509, Policy, Algorithm) extracted into the new sign_core package — see sign_core/CHANGELOG.md.

Added — Phase 5 (Compliance) complete

  • B-T sign for PDF (tsa_url: opt) — attaches an RFC 3161 TimeStampToken as id-aa-signatureTimeStampToken in CMS unsignedAttrs.
  • B-T sign for XML (tsa_url: opt) — attaches a <xades:SignatureTimeStamp> under <xades:UnsignedSignatureProperties> per ETSI EN 319 132-1 §5.4.1.
  • Pkcs11ex.Audit.Anchor.RFC3161.extract_token/1 — strips PKIStatusInfo from a TimeStampResp and surfaces the bare TST. Used by both PDF and XML B-T attach paths.
  • Telemetry meta + error-class wiring for the new {:bt_failed, …} errors.

Added — Phase 4 (Format Expansion) complete

  • PAdES B-BPkcs11ex.PDF.sign/2 and verify/2. Hand-rolled CMS encoder over OTP's 'CryptographicMessageSyntax-2009' codec; minimal trailer/xref reader and incremental-update writer; no full PDF parser. PSS signature parameters (RSASSA-PSS-params) encoded canonically for OpenSSL / BouncyCastle compatibility.
  • XAdES B-BPkcs11ex.XML.sign/2 and verify/2. Exclusive XML Canonicalization 1.0 via vendored + patched xmerl_c14n; <xades:SigningCertificateV2> with RFC 5035 IssuerSerial.
  • 6-step verify pipeline for both formats: locate, append-attack detection, parse, allowlist gate, message digest match, signature math.
  • Conformance suite — pdfsig (Poppler) and xmlsec1 (libxmlsec1) accept the output. Opt-in via mix test --include conformance.
  • SafeNet eToken end-to-end test harness (test/pkcs11ex/safenet/). Validated against a real SafeNet Token JC with a self-generated RSA-2048 keypair under PSS-SHA-256.

Added — Phase 3 (Cloud)

  • :cloud_hsm slot type — login is a no-op (cloud auth via libkmsp11 / IAM, not PKCS#11 user PIN). Slot.login/2 returns {:error, :no_pin_required}.
  • :driver_config env-var passthrough — KMS_PKCS11_CONFIG set automatically for libkmsp11 drivers before module_load.
  • examples/gcp-cloud-hsm/ — runnable runtime.exs + kmsp11.yaml + README walkthrough (gcloud kms setup, Workload Identity Federation auth).

Added — Phase 2 (Hybrid)

  • Per-slot persistent session model (parking_lot::Mutex<cryptoki::Session> Rustler resource).
  • Pkcs11ex.SlotSupervisor + Pkcs11ex.Slot.Server GenServer — round-robin session pool for cloud HSMs.
  • pin_callback lifecycle (config-resolved MFA, PIN never enters GenServer state, Rust-side Zeroizing<Vec<u8>>).
  • :signer resolution in sign_bytes/2 and JWS.
  • Inactivity timeout + :reauthentication policy (:prompt / :fail).
  • mix pkcs11ex.import_p12 task.

Added — Phase 1 (PoC)

  • Initial Mix project with Rustler bridge, OTP application scaffold, configuration files.
  • Pkcs11ex.Native — Rust NIF over the cryptoki crate.
  • Pkcs11ex.sign_bytes/2 and verify_bytes/4 Layer 2 primitives.
  • Pkcs11ex.JWS — RFC 7797 detached JWS, PS256.
  • Pkcs11ex.Policy.PinnedRegistry (default trust policy; SPKI pinning).
  • Pkcs11ex.PKCS12 read-only loader (openssl CLI backing).
  • Specifications in docs/specs/: specs.md (architecture, threat model) and api.md (configuration, behaviours, surface, error taxonomy).
  • Toolchain pins (.tool-versions): Elixir 1.19+, Erlang/OTP 28+, Rust 1.85+.