FIPS / eIDAS / KCMVP Hardware Validation Runbook · 2025-12-11

Use this runbook to validate hardware-backed crypto for the Stella Ops FIPS, eIDAS, and KCMVP compliance profiles. It is written for security engineers and validation operators running certification evidence against real HSM/QSCD/KCMVP hardware. When hardware is unavailable, keep the “non-certified” label and use the simulator (devops/services/crypto/sim-crypto-service) to exercise the registry path.

For the profile system itself and current certification baselines, see crypto-compliance.md. For configuration knobs, see crypto-profile-configuration.md.

Common prerequisites

FIPS (baseline or certified)

  1. Select the FIPS compliance profile:
    StellaOps:
      Crypto:
        Compliance:
          ProfileId: fips          # forces SHA-256/384/512 + PBKDF2; BLAKE3 is not FIPS-approved
          StrictValidation: true   # fail closed on non-compliant algorithm usage
    
    FIPS mode itself is detected from the host, not a Stella config flag: OperatingSystemFipsModeProbe reads HKLM\SYSTEM\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled on Windows and /proc/sys/crypto/fips_enabled on Linux (see src/Cryptography/StellaOps.Cryptography.Plugin.Fips/OperatingSystemFipsModeProbe.cs). Enable the FIPS provider at the OS/runtime layer (Windows FIPS policy, or the OpenSSL FIPS provider on Linux) rather than via an in-app toggle. There is no Crypto:Fips:UseBclFipsMode config key.
  2. Keep crypto on-prem. The software-only FIPS provider fips.ecdsa.soft (FipsSoftCryptoProvider, ES256/384/512 + SHA-256/384/512) is gated by the FIPS_SOFT_ALLOWED env var and is disabled unless FIPS_SOFT_ALLOWED=1 (or true). When a certified module is present, leave FIPS_SOFT_ALLOWED unset (or 0) so the non-certified software provider stays off and a certified provider is resolved instead. Do not route signing through a cloud-managed KMS (no AWS/Azure/GCP KMS defaults); the AWS_USE_FIPS_ENDPOINTS flag referenced in earlier drafts is not implemented and is not part of the offline-first posture.
  3. Run signing tests (Authority/Signer/Attestor). For CI smoke without a certified module, opt in explicitly with FIPS_SOFT_ALLOWED=1; keep this off in any release/evidence-bearing run.
  4. Capture evidence:
    • openssl fipsinstall -module <path> output (if using the OpenSSL FIPS provider).
    • JWKS export (P-256/384/521).
    • Provider-resolution metric crypto_provider_resolutions_total{provider="fips.ecdsa.soft"} (or the certified provider name) from meter stellaops.crypto.
  5. Keep the “non-certified” label until CMVP evidence is attached; the simulator may be used for CI smoke only.

eIDAS (QSCD)

The eIDAS plugin (src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/) binds StellaOps:Crypto:Profiles:eidas (EidasOptions). It does not expose a direct PKCS#11 slot/PIN configuration; qualified signing is delegated to a remote QTSP/QSCD via the Tsp adapter (see “eIDAS TSP/QSCD HTTP adapter” below), or to a local PKCS#12 keystore via Local (LocalSigningOptions) for non-qualified AdES dev signing. Earlier drafts of this runbook configured an eidas:Pkcs11 block with LibraryPath/SlotId/PinEnvVar; that block is not implemented and must not be used.

  1. Select the eIDAS compliance profile and point the eIDAS plugin at your QTSP:
    StellaOps:
      Crypto:
        Compliance:
          ProfileId: eidas
        Profiles:
          eidas:
            SignatureLevel: QES          # AdES | AES | QES
            SignatureFormat: CAdES       # CAdES | XAdES | PAdES | JAdES
            DigestAlgorithm: SHA256
            EvidenceProfile: BaselineB   # BaselineB | BaselineT | BaselineLT | BaselineLTA
            Tsp:
              Endpoint: https://qtsp-adapter.example
              ApiKey: ${EIDAS_TSP_API_KEY}
              SignPath: /api/v1/sign
              VerifyPath: /api/v1/verify
            Keys:
              - KeyId: eu-qscd-signing
                Source: tsp                # "tsp" (remote) or "local" (PKCS#12)
    
  2. Import the qualified cert to the trust store; capture OCSP/CRL endpoints. EU Trusted List validation is controlled by StellaOps:Crypto:Profiles:eidas:TrustedList (TrustedListOptions).
  3. Export JWKS from Authority/Signer; verify kid and crv match the QSCD key.
  4. Sign a fixed validation message; archive signature + certificate chain.
  5. Evidence: QTSP/QSCD response artefacts, JWKS snapshot, QSCD audit logs (if available), provider-resolution metric crypto_provider_resolutions_total{provider="eu.eidas.soft"} (or the certified eIDAS provider name) from meter stellaops.crypto.
  6. If QSCD hardware/QTSP is unavailable, the software-only provider eu.eidas.soft (EidasSoftCryptoProvider, ES256/384 + SHA-256/384, non-certified, QSCD not enforced) is gated by EIDAS_SOFT_ALLOWED and is disabled unless EIDAS_SOFT_ALLOWED=1 (or true). Set it for CI coverage against the simulator; keep it off in evidence-bearing runs.

eIDAS TSP/QSCD HTTP adapter

The library provider at src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/ uses the configured StellaOps:Crypto:Profiles:eidas:Tsp endpoint for remote signing and verification. It no longer fabricates signatures or accepts every verification result. Configure:

StellaOps:
  Crypto:
    Profiles:
      eidas:
        DigestAlgorithm: SHA256
        SignatureLevel: QES
        SignatureFormat: CAdES
        Tsp:
          Endpoint: https://qtsp-adapter.example
          ApiKey: ${EIDAS_TSP_API_KEY}
          SignPath: /api/v1/sign
          VerifyPath: /api/v1/verify
        Keys:
          - KeyId: eu-qscd-signing
            Source: tsp

The adapter receives a digest-only contract for sign/verify requests (it sends the base64 digest computed by ComputeDigest, the configured DigestAlgorithm, SignatureLevel, and SignatureFormat). Vendor QTSP/QSCD API differences should be handled behind that adapter; Stella Ops fails closed on non-success (eidas.tsp.sign_failed), empty-signature (eidas.tsp.response_empty_signature), and malformed-base64 (eidas.tsp.response_invalid_signature_base64) responses.

Note: only EvidenceProfile: BaselineB is production-ready today. Requesting BaselineT/BaselineLT/BaselineLTA causes ValidateEvidenceProfile to throw eidas.lt.evidence_embedding_not_implemented after checking the returned evidence — i.e. timestamp/revocation/archive evidence embedding for QES-LT/LTA is not yet wired (see TrustServiceProviderClient.Signing.cs). Keep the profile at BaselineB until that path lands.

KCMVP

KCMVP support in the codebase is currently limited to: (a) the kcmvp compliance profile, which selects SHA-256 for all hash purposes (ComplianceProfiles.Kcmvp), and (b) the software-only hash-only provider kr.kcmvp.hash (KcmvpHashOnlyProvider, SHA-256 content hashing only — no signing/verification). There is no KCMVP signing plugin and no config-bound KCMVP options class; an eidas-style Kcmvp: block with LibraryPath/SlotId/PinEnvVar/Algorithm: kcdsa (and ARIA/SEED/KCDSA signing) is not implemented. A certified ARIA/SEED/KCDSA module would have to be integrated as a new crypto provider plugin before signing-side KCMVP validation is meaningful.

  1. Select the KCMVP compliance profile:
    StellaOps:
      Crypto:
        Compliance:
          ProfileId: kcmvp
    
  2. The hash-only provider is gated by KCMVP_HASH_ALLOWED, which — unlike the FIPS/eIDAS soft gates — is enabled by default (treated as on when the var is unset, 1, or true). Keep it on while running hash-only baseline evidence.
  3. Run hash tests for a fixed validation message; collect SHA-256 hashes and the provider-resolution metric crypto_provider_resolutions_total{provider="kr.kcmvp.hash"} from meter stellaops.crypto. Signing tests are not applicable until a certified signing provider is added.
  4. When a certified module/provider is present, set KCMVP_HASH_ALLOWED=0 to disable the hash-only provider and rerun tests to retire the hash-only label.

Evidence checklist

Publishing