Skip to main content

Overview

The Erst simulator supports Hardware Security Module (HSM) integration for cryptographic operations through a generic Signer interface. This allows for secure key management and signing operations using both software-based keys and hardware-backed keys via PKCS#11.

Architecture

Generic signer interface

The HSM integration is built around a generic Signer trait that abstracts cryptographic operations:
#[async_trait]
pub trait Signer: Send + Sync {
    async fn sign(&self, data: &[u8]) -> Result<Signature, SignerError>;
    async fn public_key(&self) -> Result<PublicKey, SignerError>;
    fn signer_info(&self) -> SignerInfo;
}

Supported implementations

Uses local Ed25519/secp256k1 keys for development and testing environments.
Interfaces with hardware security modules for production environments requiring enhanced security.

Quick start

Software signer

use erst::hsm::{SoftwareSigner, SignerFactory};

// Generate a new key pair
let (signer, pem_data) = SoftwareSigner::generate()?;

// Use the signer
let data = b"Hello, world!";
let signature = signer.sign(data).await?;
let public_key = signer.public_key().await?;

PKCS#11 HSM signer

use erst::hsm::{SignerFactory, SignerConfig};

// Configure from environment
let signer = SignerFactory::create_from_env().await?;

// Use the HSM signer
let data = b"Secure transaction";
let signature = signer.sign(data).await?;

Configuration

Environment variables

Software signer

export ERST_SIGNER_TYPE=software
export ERST_SIGNER_ALGORITHM=ed25519
export ERST_SOFTWARE_PRIVATE_KEY_PATH=/path/to/private_key.pem
# OR
export ERST_SOFTWARE_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----..."

PKCS#11 signer

export ERST_SIGNER_TYPE=pkcs11
export ERST_SIGNER_ALGORITHM=ed25519
export ERST_PKCS11_MODULE=/usr/lib/libykcs11.so
export ERST_PKCS11_PIN=123456
export ERST_PKCS11_TOKEN_LABEL="YubiKey PIV"

# OR specify slot/key identifiers
export ERST_PKCS11_SLOT=0
export ERST_PKCS11_KEY_LABEL=MySigningKey
# OR
export ERST_PKCS11_KEY_ID=01
# OR
export ERST_PKCS11_PIV_SLOT=9a

Programmatic configuration

use erst::hsm::{SignerConfig, SoftwareSignerConfig, Pkcs11SignerConfig};

let config = SignerConfig {
    signer_type: "pkcs11".to_string(),
    algorithm: "ed25519".to_string(),
    software: None,
    pkcs11: Some(Pkcs11SignerConfig {
        module_path: "/usr/lib/libykcs11.so".to_string(),
        pin: "123456".to_string(),
        token_label: Some("YubiKey PIV".to_string()),
        slot_index: None,
        key_label: None,
        key_id_hex: Some("01".to_string()),
        piv_slot: None,
        public_key_pem: None,
    }),
};

let signer = SignerFactory::create_from_config(&config).await?;

Supported hardware

YubiKey

YubiKey devices with PIV support are fully compatible:
# YubiKey PKCS#11 module (usually installed with yubikey-manager)
export ERST_PKCS11_MODULE=/usr/lib/x86_64-linux-gnu/libykcs11.so

# Common PIV slots
export ERST_PKCS11_PIV_SLOT=9a  # PIV Authentication
export ERST_PKCS11_PIV_SLOT=9c  # Digital Signature
export ERST_PKCS11_PIV_SLOT=9d  # Key Management

Generic PKCS#11 modules

Any PKCS#11-compliant HSM should work:
# Software-based HSM for testing
export ERST_PKCS11_MODULE=/usr/lib/softhsm/libsofthsm2.so
export ERST_PKCS11_MODULE=/usr/lib/libnitrokey.so
export ERST_PKCS11_MODULE=/opt/cloudhsm/lib/libcloudhsm_pkcs11.so

Key management

Generating keys

Software keys

use erst::hsm::software::SoftwareSigner;

let (signer, pem_data) = SoftwareSigner::generate()?;
println!("Private key:\n{}", pem_data);

HSM keys

HSM keys must be generated using the HSM’s native tools:
# Using yubico-piv-tool
yubico-piv-tool -a generate -s 9a -o public_key.pem -A ECCP256
# Using softhsm2-util
softhsm2-util --init-token --free --label mytoken --pin 123456 --so-pin 123456
softhsm2-util --generate-key --algorithm ed25519 --label mykey --token mytoken

Key formats

Ed25519 keys

  • Private: PKCS#8 PEM format
  • Public: SPKI DER format

secp256k1 keys

  • Private: PKCS#8 PEM format
  • Public: SPKI DER format

Security considerations

Key protection

1

HSM keys

Private keys never leave the HSM device, providing the highest level of security.
2

Software keys

Store securely with restricted file permissions (chmod 600).
3

PIN management

Use strong PINs and avoid hardcoding them in source code. Use environment variables or secure vaults.

Best practices

Hardware keys provide better security than software keys for production environments.
Maintain secure backups of critical keys in encrypted storage.
Regularly rotate signing keys according to your security policy.
Monitor and log all key usage for security auditing.

Threat mitigation

ThreatMitigation
Key extractionHSM prevents private key extraction
Side channel attacksUse hardware with side-channel protection
Physical securitySecure HSM devices in physically protected locations

Performance

Benchmarks

Signer typeAlgorithmSign operationKey generation
SoftwareEd25519~0.1ms~1ms
Softwaresecp256k1~0.5ms~5ms
HSMEd25519~10msN/A
HSMsecp256k1~15msN/A
HSM times vary by device and connection type (USB, network, etc.)

Optimization tips

  • Reuse sessions: For HSM, reuse PKCS#11 sessions when possible
  • Batch operations: Group multiple signing operations together
  • Connection pooling: For network-connected HSMs, use connection pooling
  • Caching: Cache public keys to avoid repeated HSM calls

Troubleshooting

Common issues

Error: Failed to load PKCS#11 module: No such file or directorySolution:
# Verify module path
ls -la /usr/lib/libykcs11.so

# Install YubiKey manager
sudo apt-get install yubikey-manager
# or
brew install yubikey-manager
Error: No slots foundSolution:
# Check if HSM is connected
yubico-piv-tool -a status

# Verify permissions
sudo usermod -a -G plugdev $USER
# Log out and back in
Error: Failed to login: 0x1000Solution:
# Verify PIN
yubico-piv-tool -a verify-pin -P 123456

# Reset PIN if needed (requires PUK)
yubico-piv-tool -a change-pin -P 123456 -N 654321
Error: Private key not found in HSMSolution:
# List available keys
yubico-piv-tool -a list

# Generate key if needed
yubico-piv-tool -a generate -s 9a -o pubkey.pem -A ECCP256

Debug mode

Enable debug logging for troubleshooting:
use tracing_subscriber;

tracing_subscriber::fmt()
    .with_max_level(tracing::Level::DEBUG)
    .init();

Migration guide

From in-memory keys to HSM

1

Extract current keys

Export existing keys to PEM format for backup.
2

Generate HSM keys

Create new keys in your HSM device using the appropriate tools.
3

Update configuration

Switch from software signer to PKCS#11 signer in your environment variables.
4

Test verification

Ensure signatures verify correctly with the new HSM keys.

Example migration

// Before (in-memory)
let private_key = generate_key();
let signature = sign_data(private_key, data);

// After (HSM)
let signer = SignerFactory::create_from_env().await?;
let signature = signer.sign(data).await?;

Testing

Unit tests

cargo test hsm --lib

Integration tests

# Software signer tests
cargo test hsm_integration_tests --test hsm_integration_tests

# HSM tests (requires actual HSM)
ERST_PKCS11_MODULE=/usr/lib/libykcs11.so ERST_PKCS11_PIN=123456 \
cargo test pkcs11_integration --test hsm_integration_tests

Example usage

cargo run --example hsm_integration
Never commit HSM PINs or private keys to version control. Always use environment variables or secure secret management systems.