Craton HSM

Windows CNG Backend

Windows CNG Backend

The craton-hsm-cng crate implements the CryptoBackend trait on top of the native Windows Cryptography API: Next Generation (CNG, the BCrypt* family). In FIPS mode, algorithm providers are opened with BCRYPT_PROV_DISPATCH, restricting operations to Windows-validated FIPS-approved algorithms.

  • Crate: craton-hsm-cng
  • Backend type: CngBackend
  • License: BSL 1.1
  • MSRV: Rust 1.75
  • Platform: Windows only (Windows 10 / Windows Server 2016 or newer)
  • Status: production-ready

Platform and Cross-Compilation

The full implementation is gated behind #[cfg(windows)]. On non-Windows targets the crate compiles to a minimal stub whose new_fips() returns HsmError::FunctionNotSupported, so portable applications can select backends by target without sprinkling #[cfg] blocks through call sites.

When cross-compiling a workspace build from a non-Windows host, exclude the crate:

cargo build --workspace \
  --exclude craton-hsm-cng \
  --target aarch64-unknown-linux-gnu

This crate exposes no Cargo features. The Windows-specific windows-sys dependency activates automatically via target triple.

Build Requirements

  • Windows 10 / Windows Server 2016 or later.
  • Rust 1.75+.
  • Visual Studio 2022 with the "Desktop development with C++" workload, or an equivalent MSVC toolchain.

Algorithms

  • AES in GCM, CBC, CTR, and key-wrap modes (128 / 192 / 256-bit).
  • RSA PKCS#1 v1.5, PSS, and OAEP on 2048-, 3072-, and 4096-bit moduli.
  • ECDSA and ECDH on P-256 and P-384.
  • Ed25519 via RustCrypto's ed25519-dalek. CNG does not expose Ed25519 directly, so this one operation is outside the CNG FIPS boundary and is rejected in FIPS mode.
  • SHA-2 via BCryptCreateHash.
  • Hardware RNG via BCryptGenRandom.

Usage

# #[cfg(windows)]
# {
use craton_hsm_cng::CngBackend;
use craton_hsm::crypto::backend::CryptoBackend;

// Non-FIPS: any Windows CNG-supported algorithm.
let backend = CngBackend::new(false);

// FIPS mode: providers opened with BCRYPT_PROV_DISPATCH so only
// FIPS-approved algorithms succeed.
let backend = CngBackend::new_fips()?;

let key = backend.generate_aes_key(32, true)?;
let ct  = backend.aes_256_gcm_encrypt(key.as_ref(), b"plaintext")?;
# }
# Ok::<(), craton_hsm::error::HsmError>(())

FIPS via CNG

CngBackend::new_fips() opens providers with BCRYPT_PROV_DISPATCH. That flag restricts which algorithms succeed, but it does not change the OS FIPS policy. For the deployment to be genuinely FIPS-compliant, the Windows FIPS-mode Group Policy must also be enabled:

  • Computer Configuration → Windows Settings → Security Settings → Local Policies → Security Options → "System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing", or
  • equivalently, the registry value HKLM\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled = 1.

Without that policy set, calling new_fips() restricts the algorithm menu but the CNG module is not running inside its validated FIPS configuration. Full FIPS 140-3 compliance with this backend is a Windows property, not a crate property.

For a self-contained FIPS boundary that does not depend on OS policy, use craton-hsm-awslc with the fips feature.

Safety (FFI / unsafe)

Every CNG / BCrypt call is unsafe extern FFI. The crate sets #![forbid(unsafe_op_in_unsafe_fn)], so every FFI call site sits in an explicit unsafe block with a SAFETY: comment naming the invariant being upheld.

Three RAII wrappers — AlgHandle, KeyHandle, HashHandle — guarantee that BCryptCloseAlgorithmProvider, BCryptDestroyKey, and BCryptDestroyHash run on drop, so handle leaks on error paths are not possible. Private-key material is wrapped in Zeroizing buffers.

NTSTATUS values returned by CNG are mapped to HsmError through a typed mapping that covers 13+ documented codes (INVALID_HANDLE, INSUFFICIENT_RESOURCES, NOT_FOUND, ACCESS_DENIED, INVALID_KEY, BUFFER_TOO_SMALL, DEVICE_BUSY, and others) — no error-text string matching.

Limitations

  • Ed25519 is not FIPS under this backend. It runs in ed25519-dalek because CNG does not expose it, so FIPS mode rejects it.
  • Prehashed signing uses RustCrypto and is outside the CNG FIPS boundary, matching the craton-hsm-awslc contract.
  • Non-Windows compilation produces a stub. Every constructor on a non-Windows target either returns FunctionNotSupported directly or a backend that rejects every operation. Portable builds must pick a different backend on those targets.
  • AES-GCM AAD is not threaded through the CryptoBackend trait.