Craton HSM

Authentication and RBAC

Authentication and RBAC

The craton-hsm-auth crate provides enterprise identity, role-based access control, multi-factor re-auth for destructive operations, dual- control approvals, and per-tenant key isolation. It sits in front of any CryptoBackend and enforces who can do what with which key.

  • Crate: craton-hsm-auth
  • License: BSL 1.1
  • MSRV: Rust 1.75
  • Status: production-ready
  • Safety: no unsafe; clippy::unwrap_used, expect_used, panic, unreachable, and indexing_slicing denied in non-test builds.

Public Modules

  • auth — providers, credential types, AuthManager.
  • rbac — roles, permissions, ACL evaluation, dual-control approval.
  • tenant — tenant manager, per-tenant quotas, atomic try_reserve_key quota enforcement.
  • error — the AuthError type.

Feature Flags

FlagAddsExtra deps
ldap-authLDAP / Active Directory providerldap3, tokio
oidc-authOpenID Connect providerjsonwebtoken, reqwest
cert-authX.509 client certificate providerx509-parser

All features are off by default. Enable them in your Cargo.toml:

[dependencies]
craton-hsm-auth = { path = "../craton-hsm-auth", features = ["ldap-auth", "oidc-auth"] }

Authentication Providers

Local PIN (dev / test only)

Wraps PKCS#11 PIN-based login in a pluggable provider. PINs are salted and hashed; comparison is constant-time. Not supported in production — use LDAP, OIDC, or certificate auth instead.

use craton_hsm_auth::auth::{AuthConfig, manager::AuthManager, provider::AuthCredentials};
use zeroize::Zeroizing;

let config = AuthConfig::default();
let mgr = AuthManager::new(&config)?;

let creds = AuthCredentials::Pin {
    user_type: 1,
    pin: Zeroizing::new(b"my-pin".to_vec()),
};
let identity = mgr.authenticate(&creds)?;
# Ok::<(), craton_hsm_auth::error::AuthError>(())

LDAP / Active Directory (ldap-auth)

Bind-based authentication with:

  • LDAPv3 over TLS (LDAPS or StartTLS — TLS is mandatory).
  • Injection-safe DN and filter escaping.
  • A connection pool.
  • Per-source rate limit with lockout.

Per-process state only: the rate limiter does not coordinate across nodes in a multi-node deployment.

OpenID Connect (oidc-auth)

OIDC Core 1.0 token validation:

  • JWKS cache with stale-serve fallback if the issuer is briefly unavailable.
  • Accepted algorithms: RS256, RS384, RS512, ES256, ES384.
  • alg: none is rejected.

X.509 Certificate (cert-auth)

Client certificate authentication:

  • Full chain validation against configured trust anchors.
  • PKCS#7 / PEM / DER input formats.
  • Static (pre-loaded) DER-encoded CRLs. OCSP and HTTP CRL Distribution Point fetching are not implemented in 0.1.x. CRL parse failure fails closed (returns an error rather than warn-and-skip).

Multi-Factor (TOTP)

RFC 6238 TOTP challenge/response before destructive operations:

  • SHA-1 (legacy) and SHA-256.
  • Constant-time comparison via subtle::ConstantTimeEq.
  • OsRng (not thread_rng) for challenge IDs, approval IDs, and PIN salts.

RBAC and Dual-Control

Per-operation ACLs gate every dispatched action. For high-impact operations (key destruction, tenant deletion, policy change), a dual-control approval workflow requires two distinct identities:

  • Requester and approver must both have non-empty user IDs. Empty strings and None are rejected.
  • Self-approval is rejected.
  • Approvals are consumed through TOCTOU-safe consume_approved; each approval is single-use.

Multi-Tenancy

craton-hsm-auth implements per-tenant isolation:

  • Each tenant has a unique ID, display name, and status (Active / Suspended).
  • Per-tenant key quotas enforce resource limits via atomic try_reserve_key. Concurrent reservation attempts that would cross the quota boundary are rejected without double-counting.
  • Tenant context is propagated through sessions. Key operations are scoped to the authenticated tenant.
  • Suspended tenants cannot perform cryptographic operations.
  • RBAC policies are evaluated per-tenant: a role grant in tenant A does not confer access in tenant B.

Configuration Examples

LDAPS authentication

[auth.ldap]
enabled          = true
url              = "ldaps://ldap.example.internal:636"
bind_dn_template = "uid={user},ou=people,dc=example,dc=internal"
ca_bundle_path   = "/etc/craton-hsm/ldap-ca.pem"
pool_size        = 8

[auth.ldap.rate_limit]
max_attempts  = 5
window_secs   = 60
lockout_secs  = 900

OIDC authentication

[auth.oidc]
enabled      = true
issuer       = "https://idp.example.com"
audiences    = ["craton-hsm"]
jwks_uri     = "https://idp.example.com/.well-known/jwks.json"
algorithms   = ["RS256", "ES256"]
leeway_secs  = 30

X.509 client-cert authentication

[auth.cert]
enabled          = true
trust_anchors    = ["/etc/craton-hsm/ca/root.pem", "/etc/craton-hsm/ca/intermediate.pem"]
static_crl_paths = ["/etc/craton-hsm/crl/example.crl"]
subject_regex    = "^CN=([^,]+),OU=hsm-operators,DC=example,DC=com$"

Per-tenant quota

[[tenants]]
id            = "acme"
display_name  = "ACME Corp"
status        = "active"
max_keys      = 10000

[[tenants]]
id            = "widgets"
display_name  = "Widgets Ltd"
status        = "active"
max_keys      = 500

Error Reporting

All provider and RBAC failures surface as craton_hsm_auth::error::AuthError. See the module docs for the full variant list.

Limitations

  • Local PIN store is for testing and development only.
  • Certificate revocation supports only static, pre-loaded DER CRLs. HTTP CRL Distribution Points and OCSP are not implemented in the 0.1.x series.
  • Rate limiter state is per-process. In multi-node deployments the limiter does not coordinate between nodes; attackers distributing attempts across nodes can stretch the per-node budget.
  • Cluster and replication — tenants and roles are replicated via the Raft state machine.
  • KMIP server — delegates authentication to craton-hsm-auth after mTLS termination.