A VectorGuard Labs Product  ·  v2.3.2

Protect the Digital Secrets
That Keep Your Business Running.

PyHSM is a production-ready software Key Management Service (KMS) that securely stores encryption keys, signs data, manages key lifecycles, and keeps an auditable record of every operation so your applications are safer by design.

Stop embedding encryption keys in your code.

Stop building your own key management from scratch.

Give your applications a secure vault for their most valuable secrets.

Think of PyHSM as a bank vault for your software's secrets — open source, no hardware required.
sudo apt install pipx -y && pipx install vectorguard-pyhsm

Every Application Has Secrets

If those secrets are compromised, attackers can impersonate users, steal money, or decrypt sensitive information.

🔑 Encryption keys
🔒 API tokens
📄 Digital certificates
✏️ Signing keys
🔔 Password reset tokens
🛡️ Session secrets

Most teams store these secrets in environment variables, config files, or worse — hardcoded in source code. One breach exposes everything.

PyHSM gives those secrets a secure home — encrypted at rest, access-controlled, audited, and lifecycle-managed.

Built for Real-World Applications

See how teams use PyHSM to protect what matters most.

Online Banking & Fintech

Protect the encryption keys securing customer accounts, transaction signatures, and payment data.

Healthcare

Safeguard patient data, digital identities, and comply with HIPAA encryption requirements.

Cryptocurrency & Web3

Sign blockchain transactions without ever exposing private keys to application code or disk.

SaaS Platforms

Securely store API credentials, webhook secrets, and per-tenant encryption keys at scale.

AI & ML Platforms

Protect model credentials, inference API secrets, and sensitive customer data used in pipelines.

IoT & Edge

Manage device certificates and signing keys across distributed deployments without cloud dependency.

Why Not Use the cryptography Library?

Python's cryptography library gives you primitives. PyHSM gives you a complete key management system.

Capability cryptography library PyHSM
Encrypt / Decrypt Yes (you manage keys yourself) Yes (keys managed for you)
Key storage & encryption at rest Not included Double-encrypted keystore (AES-GCM + AES-KWP)
Key rotation Build it yourself Built-in with version tracking
Access control & caller identity Not included Caller ACLs, rate limits, operation caps
Audit logging Not included HMAC-chained tamper-evident log
Key lifecycle policies Not included Expiration, archival, destruction
Process isolation Not included Separate OS process for key material
Memory zeroization Not included Deterministic byte-by-byte clearing
Shamir secret sharing Not included M-of-N key reconstruction

The cryptography library is a toolbox. PyHSM is the locksmith. PyHSM knows how to use the tools securely, manages the keys, tracks who accessed what, and ensures nothing is left exposed.

Why Teams Choose PyHSM

A complete key management system, not a wrapper around a single crypto library.

AES-256-GCM / GCM-SIV Encryption

AES-256-GCM with hybrid nonce strategy (Python) and nonce-misuse-resistant AES-256-GCM-SIV (TypeScript). Argon2id key derivation following OWASP recommendations.

Shamir Secret Sharing

Split master keys into shares with configurable thresholds. No single point of compromise. M-of-N unlock ceremony for production deployments. Per-share HMAC-SHA256 integrity (256-bit, index-bound) prevents corrupted or adversarially crafted shares from being accepted during reconstruction.

HMAC-Chained Audit Logs

Tamper-evident audit trail with cryptographic chaining. Every operation is recorded and verifiable. Rotation-seal entries (rotationSeal/rotationOpen) extend integrity across file boundaries — deletion of any rotated log file is detectable via verify_across_rotations(). CEF/LEEF/JSON SIEM export.

Dual Implementation

Full feature parity across Python and TypeScript. Use whichever fits your stack. 274 tests (180 Python + 94 TypeScript), 80% coverage enforced in CI.

Key Rotation & Policies

Built-in key expiration, operation limits, caller ACLs, and automatic rotation via rotate_every_days policy. Keys rotate lazily on next use when due.

Self-Test on Startup

Known-Answer Tests (KATs) covering all 10 active cryptographic primitives run automatically against published NIST and RFC test vectors. Crypto modules are verified before any key operation is accepted.

Pluggable Storage Backends

Built-in FileBackend (atomic writes) and MemoryBackend (testing/ephemeral). Implement the StorageBackend interface to plug in any database, S3 bucket, or cloud store.

Encrypted Backup & Restore

Create encrypted point-in-time backups of your keystore with create_backup(). Verify integrity at any time with verify_backup() — HMAC check without loading into the live store.

Prometheus & OpenTelemetry

Export operational metrics in Prometheus text format or OpenTelemetry OTLP JSON. First-class observability without extra agents.

How PyHSM Stacks Up

PyHSM's 3 biggest competitors — and why teams choose PyHSM instead.

AWS KMS

Amazon's fully managed cloud key management service. Deep AWS integration, FIPS-certified hardware — but every key and every API call costs money, and your keys live on Amazon's infrastructure.

Google Cloud KMS

Google's managed key management service. Similar story to AWS KMS — excellent if you're already on GCP, but you're locked into Google's infrastructure and pricing model.

HashiCorp Vault

IBM-owned secrets management platform with a Transit secrets engine for cryptography-as-a-service. Powerful, but requires Consul for HA, a dedicated ops team, and ~$1,140/month on HCP.

Feature PyHSM AWS KMS Google Cloud KMS HashiCorp Vault
Pricing model AGPL-3.0 free · Commercial license from $149/mo (Startup) · $349/mo (Growth) · $749/mo (Scale) $1/key/month + $0.03/10K API calls $0.06/key/month + $0.03/10K operations ~$1.58/cluster/hour (HCP managed) or self-hosted infra cost
Vendor lock-in ✅ None — runs anywhere ❌ AWS-only ❌ GCP-only ⚠️ HCL policy language, Consul dependency for HA
Data sovereignty ✅ Full — you own and control the keystore ❌ Keys stored on AWS infrastructure ❌ Keys stored on Google infrastructure ⚠️ Depends on deployment model
secp256k1 (Ethereum/Bitcoin) ✅ Native, built-in ✅ Supported (SECG P-256k1) ✅ Supported ❌ Not in Transit — requires a custom third-party plugin
Ed25519 (Solana/Cosmos) ✅ Native, built-in ✅ Supported (added Nov 2025) ⚠️ Limited support ❌ Not in Transit natively
Shamir M-of-N secret sharing ✅ Built-in, HMAC-hardened ❌ Not available ❌ Not available ⚠️ Shamir-based unseal only (not a user-facing API)
Tamper-evident audit log ✅ HMAC-chained, spans rotation boundaries, SIEM export ⚠️ AWS CloudTrail (separate service, additional cost) ⚠️ Cloud Audit Logs (separate service) ✅ Audit log available
Per-key ACLs + rate limiting ✅ Built-in per key ⚠️ IAM policies (complex, coarse-grained) ⚠️ IAM policies (complex, coarse-grained) ✅ Vault policies
Argon2id KDF (memory-hard) ✅ Required, OWASP-recommended ❌ Not applicable (managed service) ❌ Not applicable ❌ Not applicable
Process isolation (IPC) ✅ Unix socket IPC server, mutual HMAC auth ✅ By design (managed service) ✅ By design (managed service) ✅ By design (separate server)
Python + TypeScript SDKs ✅ Both included, feature parity ✅ AWS SDK (multi-language) ✅ Google SDK (multi-language) ✅ Multi-language clients
JWK import/export ✅ Full RFC 7517 (all key types) ⚠️ Import only (limited) ⚠️ Import only (limited) ✅ Supported
Automatic key rotation ✅ rotate_every_days policy, lazy ✅ Automatic rotation available ✅ Automatic rotation available ✅ Supported
Prometheus + OTLP metrics ✅ Built-in ❌ Requires CloudWatch ❌ Requires Cloud Monitoring ✅ Built-in
FIPS 140-2/3 certified ❌ FIPS-approved algorithms, not certified ✅ FIPS 140-2 validated HSMs ✅ FIPS 140-2 validated HSMs ⚠️ Depends on deployment
Compliance docs included ✅ SOC 2, PCI-DSS, HIPAA, NIST, ISO 27001, GDPR ✅ AWS handles its own compliance ✅ Google handles its own compliance ⚠️ Partial — org must document
No cluster/infra to manage ✅ pip install, 5 lines of code ✅ Fully managed ✅ Fully managed ❌ Requires Consul for HA, dedicated ops
Network latency per operation ✅ Sub-millisecond (local) ❌ 5–50ms per API call ❌ 5–50ms per API call ⚠️ Local if self-hosted, ~20ms if HCP
Open source ✅ AGPL-3.0 ❌ Proprietary ❌ Proprietary ✅ MPL 2.0 (OSS core)

Where PyHSM Wins Decisively

Cost at Scale

AWS KMS at 50 keys with 1M API calls/month costs ~$53/month. Google KMS is similar. Vault HCP runs ~$1,140/month before client fees. PyHSM's annual commercial license is a flat fee — no per-operation billing, ever.

Blockchain Signing Without Plugins

HashiCorp Vault's Transit engine does not support secp256k1 natively — you have to find, audit, and maintain a third-party plugin. PyHSM has it built in with zero additional setup.

No Infrastructure Overhead

Vault requires Consul for high availability, a dedicated operator, and an HCL policy language to learn. PyHSM is pip install vectorguard-pyhsm and five lines of Python. No cluster, no sidecar, no ops team required.

You Own Your Keys

With AWS KMS and Google Cloud KMS, your keys live on someone else's infrastructure. With PyHSM, the keystore is a file you control, encrypted with a key only you hold. No cloud provider can be compelled to hand your keys to a third party.

Where PyHSM doesn't win: If you need a FIPS 140-2 certified module for a U.S. federal contract or PCI Level 1 SAQ D, AWS KMS or a hardware HSM is the correct answer. PyHSM's COMPLIANCE.md is explicit about this — it's not a gap to hide, it's a scope boundary.

Plans & Pricing

PyHSM is free and open source under AGPL-3.0. Commercial licenses unlock proprietary use. Production Support gives you direct access to the engineering team.

Monthly Annual Save 17%

Commercial License

Required if you embed PyHSM in a proprietary product, run it behind a SaaS API, or need a written agreement for procurement. Not needed for open-source, internal, or evaluation use.

Startup
$149 /mo

1 production deployment · up to 5 keys

  • 1 production deployment
  • Up to 5 keys
  • Use in proprietary products & services
  • No AGPL source-disclosure obligation
  • Written license agreement
Ideal for: Early-stage startups shipping their first production integration.
Get License
Scale
$749 /mo

Unlimited deployments · unlimited keys

  • Unlimited deployments
  • Unlimited keys
  • Use in proprietary products & services
  • No AGPL source-disclosure obligation
  • Written license agreement
Ideal for: Enterprises and high-growth platforms running PyHSM at scale across many services.
Get License
All tiers include: right to use PyHSM in proprietary products and services without AGPL obligations · written license agreement for legal, procurement & compliance review · license coverage for the full billing term (monthly or annual). Annual billing saves the equivalent of two months (~17% discount).

Production Support

For teams that want ongoing expert guidance and a direct line to the creator — deployment help, security reviews, architecture reviews, and incident troubleshooting.

PyHSM Production Support
$2,500 /month

Priority access to the engineering team for teams running PyHSM in production.

  • Production deployment assistance
  • Priority technical support
  • Security configuration reviews
  • Architecture reviews
  • Integration/code reviews
  • Upgrade assistance
  • Incident troubleshooting
  • Direct access to the PyHSM maintainer
  • Defined response-time SLA
Ideal for: Startups and mid-stage teams in web3, fintech, and SaaS that need reliable access to a key management expert.
Get Started

How It Works

1

Install PyHSM

Clone the repo or pip install vectorguard-pyhsm. Open source under AGPL-3.0 — use it freely in open-source projects. Commercial or proprietary use requires a commercial license.

2

Build With Confidence

Full documentation, test suite, README.md, and OPERATIONS.md cover every security decision. Read the source. Audit it. It's your code now.

3

Get Expert Support

When you need implementation guidance or code review, get Priority Support.

4

Ship Securely

Deploy with confidence knowing the creator of PyHSM is available to review, advise, and help when things get complex.

Full Demo

A complete walkthrough showing how PyHSM encrypts a private key, splits the master password with Shamir secret sharing, and recovers it using the default Anvil private key.

Note: The private key used in this demo is 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80. This is the default Anvil (Foundry) test private key. Never use test keys in production.
1

Encrypt the Private Key

First, we create a keystore and generate an AES-256 encryption key inside it. Then we encrypt the Anvil private key using that key. The master password protects the entire keystore. Without it, the encrypted data is useless.

Create keystore and generate an AES-256 encryption key
vectorguard-pyhsm --store demo-keystore.enc generate vault-key --type aes-256
# → Generated aes-256 key: vault-key
Encrypt the private key
vectorguard-pyhsm --store demo-keystore.enc encrypt vault-key \
  -d "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
# → 0200000001f383ad67000000009a3d48dfa78fe787...(ciphertext)

The private key is now encrypted inside the keystore. The ciphertext output is the AES-256-GCM encrypted form of the key; it's completely useless without the master password. Even if an attacker steals the keystore file, they cannot recover the private key.

2

Shard with Shamir 3-of-5

Now we eliminate the single point of failure. Instead of one person holding the master password, we split it into 5 shares using Shamir's Secret Sharing. Any 3 shares can reconstruct the password, but 2 or fewer reveal zero information. This is mathematically proven, not just computationally hard.

First, convert the master password to its hex representation
# Each ASCII character becomes its 2-digit hex code:
# 'd' = 64, 'e' = 65, 'm' = 6d, 'o' = 6f, '-' = 2d, 'm' = 6d, 'a' = 61, ...
#
# Full breakdown of "demo-master-password":
#   d=64  e=65  m=6d  o=6f  -=2d  m=6d  a=61  s=73
#   t=74  e=65  r=72  -=2d  p=70  a=61  s=73  s=73
#   w=77  o=6f  r=72  d=64
#
# You can verify this with Python:
python3 -c "print('demo-master-password'.encode().hex())"
# → 64656d6f2d6d61737465722d70617373776f7264

The split command operates on hex-encoded byte strings. The hex value 64656d6f2d6d61737465722d70617373776f7264 is simply the ASCII encoding of "demo-master-password" represented in hexadecimal. Each character maps to its hex byte value (e.g., 'd' → 64, 'e' → 65, 'm' → 6d). This is not encryption or hashing; it's a direct, reversible encoding that the Shamir split algorithm requires as input.

Now split the hex-encoded password into 5 shares (3 required to reconstruct)
vectorguard-pyhsm split -k 3 -n 5 -s "64656d6f2d6d61737465722d70617373776f7264"

# → Share 1: {"index": 1, "data": "d18a992fabfb5eb8e53689284a0f69f734d3312b", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}
# → Share 2: {"index": 2, "data": "0985e4adb3d6b7e7c3e9a8fba6d4a5958b7628ae", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}
# → Share 3: {"index": 3, "data": "bc6a10ed3540882c52ba53fe9cbabf11c8ca6be1", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}
# → Share 4: {"index": 4, "data": "ce2a97e3180640240fce01c779af615d67ae9ba0", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}
# → Share 5: {"index": 5, "data": "7bc563a39e907fef9e9dfac243c17bd92412d8ef", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}

Distribute one share to each of 5 custodians. No single custodian (or even any 2) can reconstruct the password. Each share should be stored in a separate location — different password managers, different physical safes, or different geographic regions depending on your threat model.

3

What Happens Next — Recovery

When you need to decrypt (sign a transaction, recover the key, rotate credentials, etc.), you gather any 3 of the 5 custodians. Each provides their share, and the master password is reconstructed.

1 Gather any 3 of the 5 custodians
2 Each provides their share
3 Reconstruct the master password:
Reconstruct the master password from any 3 shares
vectorguard-pyhsm reconstruct \
  --share '{"index": 1, "data": "d18a992fabfb5eb8e53689284a0f69f734d3312b", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}' \
  --share '{"index": 3, "data": "bc6a10ed3540882c52ba53fe9cbabf11c8ca6be1", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}' \
  --share '{"index": 5, "data": "7bc563a39e907fef9e9dfac243c17bd92412d8ef", "share_hmac": "...", "hmac_salt": "...", "reconstruction_hmac": "..."}' 
# → 64656d6f2d6d61737465722d70617373776f7264 (hex of "demo-master-password")

Notice we used shares 1, 3, and 5 (any combination of 3 works). The reconstruction outputs the hex-encoded master password which is then used to unlock the keystore.

4

Decrypt and Recover the Private Key

With the reconstructed master password, unlock the keystore and decrypt the original private key.

Use the reconstructed password to decrypt
vectorguard-pyhsm --store demo-keystore.enc decrypt vault-key \
  -d "0200000001f383ad67...(ciphertext)" \
  -p "demo-master-password"
# → 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

The original private key is recovered exactly as it was. The audit log records every operation with HMAC-chained tamper detection; so you have a permanent, verifiable record of who decrypted what and when.

Security Properties

The private key is never stored in plaintext anywhere

The master password doesn't exist as a single secret (it's split across 5 people)

Any 2 or fewer shares reveal zero information (information-theoretic, not computational)

Every encrypt/decrypt operation is permanently recorded in the audit log

If someone tampers with the keystore file, PyHSM detects it and refuses to open

Built for Security Teams

Open Source

AGPL-3.0 licensed. Read every line. Security-conscious developers trust what they can verify. Commercial use in proprietary products requires a commercial license — contact us.

No Vendor Lock-In

You own the code, the keys, and the deployment. No cloud dependency, no per-operation fees.

Battle-Tested Primitives

AES-256-GCM / AES-256-GCM-SIV, Argon2id, HKDF, HMAC-SHA256. No custom cryptography — only proven standards. Startup Known-Answer Tests cover all 10 active primitives. 274 tests (180 Python + 94 TypeScript), 80%+ coverage enforced in CI.

Compliance-Ready

HMAC-chained audit logs spanning rotation boundaries, caller ID tracking, cross-rotation verify_across_rotations(), and SIEM export provide evidence for SOC 2, PCI-DSS, HIPAA, NIST SP 800-57, ISO 27001, and GDPR auditors. See COMPLIANCE.md for the full framework coverage matrix.

Memory Safety

Key material is deterministically zeroized byte-by-byte after every operation. Sensitive buffers never linger in heap memory waiting for garbage collection — a requirement auditors look for and most libraries skip entirely.

Process Isolation

Run PyHSM as a separate OS process communicating over a Unix socket with mutual HMAC authentication. Your application code never holds raw key material in its own memory space — the attack surface shrinks to the IPC boundary.

Frequently Asked Questions

Architecture, security, and design questions about PyHSM.

v2.0.0 is a major release. Existing keystores are readable — PyHSM migrates the KDF automatically on the next write. The code changes are minimal but required:

  1. Upgrade: pip install --upgrade vectorguard-pyhsm
  2. Constructor now requires keyword arguments. Change PyHSM("path", "pw") to PyHSM(storage_path="path", master_password="pw").
  3. decrypt() always returns bytes. If you pass the result to a string API, add .decode("utf-8").
  4. get_audit_log() returns an AuditLog object. Replace any direct list access with .export_jsonl().
  5. allow_export defaults to False. Add "allow_export": True to key policies where JWK export is needed.
  6. CLI generate --type is now required. The implicit aes-256 default has been removed.
  7. TypeScript: use PyHSM.create() in production. The sync constructor is retained for testing but emits a deprecation warning.

See the full migration guide in the documentation for a detailed breakdown.

v2.3.1 (released October 29, 2026) is a security hardening release with no breaking changes. It ships four targeted fixes and 10 new regression tests:

  • Thread-safe rate limiter. RateLimiter.allow(), usage(), and reset() now all acquire a threading lock before touching shared state. Previously, two concurrent threads could both pass the rate-limit check before either recorded the operation, admitting up to 2× the configured limit under burst load. The fix makes every check-then-record atomic.
  • Constant-time HMAC comparisons throughout. All HMAC string comparisons in audit.verify(), audit.verify_across_rotations(), and the legacy Shamir 4-byte checksum path have been replaced with hmac.compare_digest(). Plain != on HMAC-derived strings is a timing oracle — an adversary can guess values byte-by-byte by measuring response latency.
  • RSA key import size enforcement. import_key_jwk() now rejects RSA keys below 2048 bits with a clear error citing NIST SP 800-131A Rev. 2. Only 2048 and 4096 bits are accepted. Previously, any RSA key size (including 512 and 1024 bits) would be silently stored and used.
  • Stronger RSA-PSS and Ed25519 KATs. The RSA-2048-PSS startup self-test now uses a fixed embedded private key (SHA-256 public-key fingerprint pinned) instead of a fresh ephemeral key. This catches library regressions that produce invalid signatures which still verify against the same buggy path. Both the RSA-PSS and Ed25519 KATs now include a wrong-key rejection check — the signature must not verify under a different key.

Total test count is now 274 tests (180 Python + 94 TypeScript).

v2.3.0 shipped two changes:

October 28 — License change: MIT → AGPL-3.0. The license was updated from MIT to the GNU Affero General Public License v3.0 (AGPL-3.0). There are no code changes in this stage — the version bump exists solely to mark the license transition clearly in the package history.

October 29 — Offline JWT license enforcement and runtime notices. The following features were added:

  • Runtime license notice. Every PyHSM() instantiation emits an INFO-level log entry stating the AGPL-3.0 terms and commercial licensing contact. The CLI prints a one-line banner to stderr on every subcommand.
  • Offline JWT license token verification. Commercial licensees receive a signed RS256 JWT. The token is verified locally against an embedded RSA-2048 public key — no network call, air-gap safe. Invalid, expired, or tampered tokens raise LicenseError before any key operation is permitted.
  • Per-tier key limits. The Startup tier enforces a maximum of 5 keys per instance. Growth and Scale tiers are unlimited. AGPL sessions are unrestricted.
  • New license CLI subcommand that shows the active tier, licensee, expiry, and limits.
  • New get_license_info() API method returning a LicenseInfo dataclass.
  • License state audited — a license_verified or agpl_mode event is written to the HMAC-chained audit log at every session open.

What the AGPL change means for you:

  • Open-source projects and personal use: No change. You can continue using PyHSM freely under AGPL-3.0.
  • Proprietary products or network services: If you embed PyHSM in a product you distribute, or use it to provide a network service without releasing your application source under AGPL, you need a commercial license. See COMMERCIAL_LICENSE.md in the repository or contact pavon@vectorguardlabs.com.

Users who had v2.2.5 already installed in their infrastructure prior to October 28, 2026 are unaffected by this change.

v2.2.0 was a targeted security-hardening release. v2.2.5 reflects the current stable version with all hardening in place. There are no breaking changes from v2.1.0 except that --ipc-secret is now required for the IPC server.

Security fixes in v2.2.0 (all present in v2.2.5):

  • IPC mutual authentication is now mandatory. --ipc-secret (or PYHSM_IPC_SECRET) must be set. The server refuses to start without it. Any client connecting to the HSM process must present a valid HMAC-SHA256 token on every request; unauthenticated connections are rejected before any operation is processed.
  • IPC global lock eliminated. The single threading lock wrapping all operations has been replaced with per-key locking (parallel operations on different keys), no lock for read-only ops, and a 30-second connection timeout that drops stalled clients.
  • Shamir share integrity upgraded. The 4-byte truncated checksum has been replaced with per-share HMAC-SHA256 (256-bit, index-bound) plus a reconstruction HMAC covering the full secret. Old shares with only a checksum field remain backward-compatible.
  • Startup KATs expanded from 4 to 10 primitives. Known-Answer Tests now cover all active cryptographic primitives: AES-256-GCM, PBKDF2-SHA256, HMAC-SHA256, CSPRNG, Argon2id, HKDF-SHA256, AES-256-KWP, ECDSA P-256, RSA-2048-PSS, and Ed25519 — verified against published NIST/RFC test vectors before any key operation is accepted.
  • Audit log chain now spans rotation boundaries. rotationSeal/rotationOpen entries cryptographically stitch the HMAC chain across file boundaries. The new verify_across_rotations() method verifies the full chain across all retained rotated files — deletion of any rotated file is detectable.

Migration from v2.1.0: The only change required is supplying --ipc-secret to the IPC server. Generate it once with openssl rand -hex 32, store it securely (ideally on a tmpfs mount, chmod 600), and pass it at startup. Existing Shamir shares continue to work — new splits automatically use the stronger HMAC scheme.

Vault solves a different problem at a different scale. It's a secrets management platform — it manages database credentials, API tokens, PKI certificates, and dynamic secrets across an organization. It requires Consul for HA, needs dedicated operators, and the Enterprise license for features like HSM auto-unseal costs six figures.

PyHSM is a focused key management library. You pip install it, import it in your code, and you have key generation, signing, encryption, and audit logging in five lines. There's no cluster to manage, no Consul dependency, no operator UI to secure, no HCL policy language to learn.

Use Vault if: You need org-wide secrets management with 50 services and dynamic credential rotation.

Use PyHSM if: You need to encrypt data or sign transactions from your application with proper key lifecycle management — without the operational overhead.

Additional distinction: Vault's Transit engine doesn't support secp256k1. If you're signing Ethereum or Bitcoin transactions, Vault can't do it natively. PyHSM can.

Short answer: PyHSM doesn't claim the key is impossible to extract. It makes extraction as difficult as possible within software constraints, and makes all access auditable.

The layers of defense:

  1. Double-encrypted at rest. The keystore file is AES-256-GCM encrypted (master password → Argon2id → HKDF → encryption key). Each individual key inside is additionally wrapped with AES-KWP (RFC 5649).
  2. Never in memory as immutable objects. Key material is stored as mutable bytearray (Python) or Buffer (Node.js) and deterministically zeroized byte-by-byte after each operation. It never sits in a string pool waiting for garbage collection.
  3. The API never returns raw key bytes. You call hsm.sign() and get a signature back. hsm.decrypt() returns the plaintext as bytes — the private key itself is unwrapped internally, used, and zeroized.
  4. Process isolation mode puts the keys in a separate OS process. Even a full RCE in your application can't read the HSM process's address space without a kernel exploit.
  5. Export is explicit and audited. export_jwk() exists for interoperability, but it's a deliberate call that gets logged in the audit trail. Keys require "allow_export": True in their policy — this is False by default.

What PyHSM cannot prevent: A root-level attacker with memory debugging tools attached to the running HSM process during the microseconds of signing. That's what hardware HSMs solve with a physical trust boundary.

Key rotation generates a new key version and archives the old one. Old ciphertexts remain decryptable because the version number is embedded in the ciphertext format.

Mechanically:

  1. New random key material is generated (os.urandom(32))
  2. It's wrapped with AES-KWP before storage
  3. The previous version is marked archived: true
  4. New encrypt() calls use the new version
  5. decrypt() reads the version prefix from the ciphertext header and selects the matching key version automatically

This means you can rotate keys without re-encrypting your entire database. Old data decrypts with the old version, new data uses the new version.

Automatic rotation: Set "rotate_every_days": 90 in the key policy and PyHSM rotates the key automatically on the next encrypt() call after the interval expires — no cron jobs or external schedulers needed.

Rotation is currently supported for AES keys. Asymmetric keys (EC, RSA, Ed25519) don't rotate in-place — you generate a new key ID and transition callers.

Each audit entry contains an HMAC that chains to the previous entry — similar to a blockchain of log records.

To tamper with entry N, an attacker would need to recompute the HMAC for entries N, N+1, N+2, ... all the way to the end. And to compute any HMAC, they need the audit HMAC key — which is derived from the master password via HKDF.

Verification walks the chain and checks every HMAC against its predecessor. If any entry was modified, inserted, or deleted, the chain breaks and verification reports the exact sequence number where corruption occurred.

Cross-rotation chain integrity (v2.2.0+): When the log rotates, a rotationSeal entry is written to the closing file containing the chain's final HMAC. The new file starts with a rotationOpen entry recording that seal. verify_across_rotations() stitches all retained rotated files oldest-to-newest, so deletion of any rotated file is detectable — an attacker cannot erase evidence without breaking the provable chain.

The log is append-only — entries are never modified or deleted during normal operation. Every operation (including denied ones) is recorded with timestamp, operation type, key ID, caller ID, and success/failure status.

PyHSM provides the following guarantees:

  • Confidentiality at rest: AES-256-GCM with hybrid nonce (Python) / AES-256-GCM-SIV nonce-misuse resistant (TypeScript) — NIST SP 800-38D / RFC 8452
  • Key wrapping: AES-KWP (RFC 5649)
  • Key derivation: Argon2id required (64MB/3 passes) → HKDF-Expand (RFC 9106 / RFC 5869). PBKDF2 only via explicit PYHSM_ALLOW_PBKDF2_FALLBACK=1 for migration
  • Salt-bound KEK: dedicated salt inside the encrypted envelope, derived through full Argon2id → HKDF path
  • Authentication: Encrypt-then-MAC (HMAC-SHA256) — proven-secure composition
  • Nonce safety: Hybrid nonce (random+counter+random) eliminates birthday-bound collisions (Python); GCM-SIV is nonce-misuse resistant (TypeScript)
  • Ciphertext binding: AAD = key_id + version — prevents cross-key confusion attacks
  • Input validation: 64 MB cap enforced on both encrypt and decrypt paths
  • Signing (ECDSA): SHA-256 (P-256, secp256k1), SHA-384 (P-384), SHA-512 (P-521) — FIPS 186-5
  • Signing (EdDSA): Ed25519 (RFC 8032)
  • Constant-time comparison: Timing side-channel resistant on all HMAC paths
  • Audit integrity: HMAC-chained append-only log spanning rotation boundaries (rotationSeal/rotationOpen + verify_across_rotations()) — tamper-evident across all retained files
  • Shamir share integrity (v2.2.0+): Per-share HMAC-SHA256 (256-bit, index-bound) replaces 4-byte checksum — forgery resistance and index-binding prevent share transplantation

What is explicitly NOT guaranteed: Protection against kernel-level access, CPU side-channel attacks, physical access to hardware, or compromised underlying crypto libraries.

PyHSM defends against five classes of threat actors:

  • T1: Network attacker — Process isolation; app never holds keys
  • T2: App-level code execution — Rate limiting, ACLs, operation caps, process isolation
  • T3: Filesystem read access — Double-encryption (AES-GCM + AES-KWP), Argon2id
  • T4: Malicious insider — Shamir M-of-N, tamper detection, audit log
  • T5: Root/kernel access — LIMITED — deterministic zeroization reduces window only

In-scope: Filesystem theft, application compromise, unauthorized key usage, silent key abuse, insider threats with partial access, keystore tampering, audit log manipulation.

Out-of-scope (acknowledged): Root attacker with debugger during signing, CPU side-channels, physical hardware access, compromised OS kernel.

It depends on your constraints. Neither is universally better.

Use AWS KMS if: You're in a regulated environment requiring FIPS certification, you're already all-in on AWS, you don't need secp256k1/Ed25519, and you're fine with per-operation costs and network latency.

Use PyHSM if: You need secp256k1/Ed25519 signing (blockchain), you want to avoid per-operation costs at scale, you require data sovereignty, you want zero vendor lock-in, or your latency budget can't tolerate network round-trips for every operation.

Key differences: AWS KMS costs $1/key/month + $0.03/10K API calls and adds 5–50ms latency per operation. PyHSM is free for open-source use (AGPL-3.0), runs locally with sub-millisecond crypto latency, supports secp256k1 and Ed25519 (which KMS doesn't), and has no vendor lock-in — you can export keys in standard JWK format (allow_export: true in the key policy).

They're not mutually exclusive: Some teams use AWS KMS for general infrastructure encryption and PyHSM specifically for blockchain transaction signing where AWS has no native support.

Process isolation is the highest security configuration PyHSM offers within software constraints. It separates key material into a dedicated OS process that communicates with your application over a Unix domain socket (IPC).

Why it matters: Without isolation, an attacker who achieves Remote Code Execution (RCE) in your application can read anything in that process's memory — including any key material that happens to be unwrapped at that moment. With process isolation, the keys live in a separate address space. The attacker would need a kernel exploit to cross that boundary.

How it works:

  1. The HSM process starts independently, loads the encrypted keystore, and listens on a Unix socket (e.g., /run/pyhsm/pyhsm.sock).
  2. Your application connects as a client and sends operation requests (encrypt, sign, rotate, etc.) over the socket.
  3. The HSM process unwraps the key, performs the cryptographic operation, zeroizes the key from memory, and returns only the result (ciphertext, signature, etc.).
  4. Your application never sees raw key bytes — it only receives operation outputs.

Authentication: As of v2.2.0, IPC mutual authentication is mandatory — the server refuses to start without a configured --ipc-secret (or PYHSM_IPC_SECRET environment variable). The client authenticates each request using an HMAC-SHA256 over the message payload. Unauthenticated connections are rejected before any operation is processed.

Performance (v2.2.0+): The previous global lock has been replaced with per-key locking. Read-only operations hold no lock at all. A 30-second per-connection timeout drops stalled clients rather than blocking other operations.

Trade-offs: Process isolation adds IPC latency (typically <1ms on localhost) and operational complexity (two processes to manage instead of one). For most deployments, this is negligible compared to the security gain.

In production, you don't want a single person (or a single .env file) holding the master password that unlocks all keys. PyHSM implements Shamir's Secret Sharing over GF(256) to split the master password into N shares where any K shares can reconstruct it — but K-1 or fewer reveal zero information about the secret.

Typical setup (3-of-5):

  1. During initial deployment, the security team generates 5 shares from the master password.
  2. Each share is distributed to a different key custodian (stored offline, in separate locations, or in separate password managers).
  3. At startup, 3 custodians provide their shares to unlock the HSM. No single custodian can unlock it alone.

Security properties:

  • K-1 shares reveal nothing. Even if an attacker steals 2 of 5 shares (in a 3-of-5 scheme), they gain zero knowledge about the master password. This is mathematically proven, not just computationally hard.
  • Per-share HMAC-SHA256 integrity (v2.2.0+). Each share carries an HMAC-SHA256 tag keyed on a random salt and bound to the share's index. This prevents a corrupted or adversarially crafted share from being accepted during reconstruction, and prevents a tag from one share being transplanted to another.
  • Intermediate buffers are zeroized. After reconstruction, the share data and intermediate polynomial evaluation buffers are deterministically cleared from memory.
  • Works with environment variables. In containerized deployments, shares can be injected via PYHSM_SHARES as a comma-separated list of JSON objects — each provided by a different secrets manager or operator.

When to use Shamir: Any deployment where the master password represents a single point of failure — production systems, multi-team environments, regulatory contexts requiring separation of duties, or anywhere you need to survive the "hit by a bus" scenario.

This is by design — it's a security property of Shamir Secret Sharing, not a bug.

Why the shares differ each time: Shamir Secret Sharing works by generating a random polynomial of degree K-1 (where K is your threshold). The secret is the constant term (the y-intercept), but the other coefficients are randomly chosen each time you run split.

For a 3-of-5 split, PyHSM generates a random degree-2 polynomial:

f(x) = secret + a1*x + a2*x² (over GF(256))

  • secret is fixed (your master password bytes)
  • a1 and a2 are randomly generated each time

The shares are points on this polynomial: Share 1 = f(1), Share 2 = f(2), etc. Different random coefficients produce a different polynomial, which produces different shares — but the same y-intercept (your secret).

Why reconstruction still works: Any 3 points on a degree-2 polynomial uniquely determine that polynomial, and therefore uniquely determine f(0) — the secret. Your shares and someone else's shares are from different polynomials that pass through the same point at x=0.

Visual intuition:

  • Run 1: f(x) = secret + 7x + 3x² → shares A, B, C, D, E
  • Run 2: g(x) = secret + 2x + 9x² → shares V, W, X, Y, Z

Both f(0) = g(0) = secret. But f(1) ≠ g(1), f(2) ≠ g(2), etc.

Why this matters for security: This randomness is critical. If the same secret always produced the same shares, an attacker who saw shares from two different split ceremonies could cross-reference them. The random coefficients ensure that every split is independent — even if you split the same secret 1,000 times, the shares are completely different each time and reveal nothing about each other.

Yes — blockchain signing is one of PyHSM's primary use cases and a key differentiator from cloud KMS solutions like AWS KMS or Google Cloud KMS, which don't support secp256k1 or Ed25519.

Supported blockchain ecosystems:

  • Ethereum / EVM chains (secp256k1 + ECDSA): Sign raw transaction hashes for Ethereum, Polygon, Arbitrum, Optimism, BSC, and any EVM-compatible network.
  • Bitcoin (secp256k1 + ECDSA): Sign Bitcoin transactions with the same curve and algorithm Bitcoin uses natively.
  • Solana / Cosmos (Ed25519 + EdDSA): High-performance 64-byte signatures for Solana transactions, Cosmos SDK messages, and any Ed25519-based protocol.

How it works in practice:

  1. Import your existing private key (e.g., from MetaMask export) via JWK, or generate a fresh wallet key directly in PyHSM.
  2. The private key is AES-KWP double-encrypted at rest — it never sits in plaintext on disk.
  3. Your application builds and hashes the transaction, then calls hsm.sign("eth-wallet", tx_hash).
  4. PyHSM unwraps the key, signs, zeroizes the key from memory, and returns only the signature.
  5. Your application parses the DER-encoded signature into (r, s, v) and broadcasts.

Security advantages over raw key files:

  • The key is never in a .env file — it's double-encrypted and requires the master password (or M-of-N Shamir shares) to unlock.
  • Every signing operation is recorded in the HMAC-chained audit log with caller ID, timestamp, and key version.
  • Rate limiting and max_operations policies prevent unlimited signing even if the application is compromised.
  • Process isolation mode ensures key material lives in a separate address space from your application.

Production considerations: Latency is ~103ms per sign operation (dominated by keystore persistence). For high-frequency use cases, consider the TypeScript layer's deferred persistence mode. For cold wallets or treasury keys, combine with Shamir 3-of-5 so no single operator can unlock the signing key.

Get in Touch

Ready to secure your key management? Let's talk about which plan fits your team.

Response Time

We respond to all inquiries within 1 business day.