Provider Architecture#
The daemon hosts two parallel provider families — Score (software-oriented,
currently backed by OpenSSL) and PKCS#11 (hardware token / SoftHSM). Both
families implement the same IProvider / IProviderFactory interfaces and
are registered with ProviderManager through an identical visitor-pattern
bootstrapping sequence.
Fig. 8 Daemon Provider Architecture — Score and PKCS#11 families, handler hierarchy, and configuration flow.#
Provider Families#
Score Provider Family#
The score family (provider/score_provider/) provides typed abstract handler
bases that sit between the generic IHandler interface and concrete
implementations:
ScoreHashHandlerowns aHashExecutorthat drives the stream state machine (HASH_INIT→HASH_UPDATE→HASH_FINALIZE).ScoreMacHandlerowns aMacExecutorwith the equivalent MAC state machine.ScoreKeyManagementHandlerdelegates to a sharedKeyManagementExecutor(fromprovider/executors/).
Concrete providers (e.g. openssl/) inherit from these bases and override
only the typed crypto primitive methods (InitHash, UpdateHash,
FinalizeHash). They do not re-implement the state machine logic.
PKCS#11 Provider Family#
The PKCS#11 family (provider/pkcs11/) implements IHandler directly.
Each handler translates generic operation requests into PKCS#11 C API calls
against a token. Shared key management logic is provided via the same
KeyManagementExecutor used by the score family.
Hash Algorithm Contract#
Hash contexts expose a provider-neutral interface. The daemon resolves the
requested algorithm against the provider selected for the deployment and
returns CryptoErrorCode::kUnsupportedAlgorithm when that provider cannot
perform it. The daemon does not silently substitute another algorithm or digest
size. Algorithm availability can therefore differ between deployments without
changing the public hash interface.
The digest size required by a resolved algorithm is exposed through the hash context, allowing callers to allocate an output buffer without relying on provider-specific metadata.
Both streaming and single-shot operations accept empty input. A streaming
caller may invoke Init() followed directly by Finalize() without an
intermediate Update(); the result is the standard digest of the empty byte
sequence.
SHA-1 and MD5 are not exposed by the high-level Crypto API because no supported use case requires these cryptographically weak algorithms.
An insufficient output buffer is a retryable finalization error. Provider adapters must either detect this condition before consuming the digest state or preserve the active provider operation when reporting it. Other provider or transport errors must be handled according to their returned error code; callers must not assume that every failure preserves a streaming operation.
Provider Configuration#
Provider configuration is split into three layers. Each layer is owned by a separate config type and resolved at a different point during daemon startup.
Provider-family topology — build time The set of provider families that can exist in a given daemon binary is decided by compile-time bool flags. The master family flags are
score_crypto_score_backend_enabledfor the Score provider family andscore_crypto_pkcs11_backend_enabledfor the PKCS#11 provider family. Their corresponding config settings are exposed to the code asSCORE_CRYPTO_SCORE_BACKEND_ENABLEDandSCORE_CRYPTO_PKCS11_BACKEND_ENABLED. The Score family also has the per-backend flagscore_crypto_score_openssl_enabled, exposed asSCORE_BACKEND_OPENSSL_ENABLED, which controls whether the OpenSSL backend is included in the active Score backend list. A flag can be overridden at build time to exclude a family or backend from the daemon binary.ProviderManagerFactorycreates the corresponding backend factory for every configured family that is compiled in.Provider-specific parameters — config file / defaults Each family parses its own parameters from the daemon configuration:
ScoreProviderConfig(score_provider/score_provider_config.hpp) holds oneScoreProviderEntryper score backend. An entry contains the provider name, the backend implementation tag (for example"openssl"), and the provider type (SOFTWARE,HARDWARE,SPECIALIZED).ScoreProviderConfig::ParseConfig(config)populates entries from the config file; when no config is present it falls back to the active backends discovered at compile time.Pkcs11Config(pkcs11/pkcs11_token_config.hpp) holds onePkcs11TokenEntryper token. An entry contains the token label, model, user PIN, provider name, provider type, and session cleanup strategy.Pkcs11Config::ParseConfig(config)reads these values from the daemon config.
Runtime enablement and type mapping —
ProviderInitConfigAfter all factories have created and registered their providers,ProviderManager::Initialize()appliesProviderInitConfigto decide:Which registered providers are eligible. Disabled providers are excluded before backend registration.
Which provider is the default for each
CryptoProviderType(DEFAULT,SOFTWARE,HARDWARE,SPECIALIZED).
ProviderInitConfigidentifies providers by their stableProviderName(for example"OPENSSL"or"hsm_slot_1"), not by the runtimeProviderIdassigned during registration. This keeps the configuration stable across restarts and independent of registration order.If the daemon config does not supply a
ProviderInitConfig,ProviderManagercreates a default one that enables every registered provider and selects defaults using the preference orderHARDWARE→SOFTWARE.
Registration and availability are separate states. Registration assigns a
stable ProviderId exactly once. ProviderManager::Initialize() makes
an initial initialization attempt, but an individual provider may remain
unavailable without preventing optional providers from being registered.
A provider lookup retries initialization using the same ProviderId and
serializes concurrent attempts. IProvider::IsInitialized() reports state
only; it does not initialize the provider.
Configuration flow#
Config::ParseConfig()
│
├── ScoreProviderConfig::ParseConfig(config) ──► ScoreProviderEntry list
│
└── Pkcs11Config::ParseConfig(config) ──► Pkcs11TokenEntry list
│
ProviderManagerFactory::Create(config)
│
├── CreateScoreProviderFactory(config)
│ ScoreProviderFactory(ScoreProviderFactoryConfig)
│ CreateAndRegister(manager) ──► ProviderId assigned once
│
├── CreatePkcs11ProviderFactory(config)
│ Pkcs11ProviderFactory(Pkcs11ProviderFactoryConfig)
│ CreateAndRegister(manager) ──► ProviderId assigned once
│
└── provider_manager->Initialize()
│
├── InitializeAll()
│ provider->Initialize(context)
│
└── BuildTypeMappings(ProviderInitConfig.typeToProviderName)
resolve registered names → runtime ProviderId
On a later request, ProviderManager::GetProvider(...) checks
IsInitialized() and retries Initialize(context) when necessary.
Failed providers remain registered, but unavailable providers are not
returned to callers.
Directory Layout#
provider/
├── i_provider.hpp ← IProvider interface
├── i_provider_factory.hpp ← IProviderFactory interface
├── provider_manager.hpp/.cpp ← Provider registry & lifecycle
├── provider_manager_factory.hpp/.cpp ← Build-time factory wiring
├── handler/
│ ├── i_handler.hpp ← Handler interface
│ ├── i_crypto_handler_factory.hpp ← Factory interface
│ ├── handler_init_params.hpp
│ ├── context_data_node.hpp
│ ├── operations/
│ │ ├── hash_handler_operations.hpp ← Shared hash OperationAction constants
│ │ └── mac_handler_operations.hpp ← Shared MAC OperationAction constants
│ └── src/
│ ├── handler_utils.hpp/.cpp
│ └── context_data_node.cpp
├── executors/
│ ├── key_mgmt_executor.hpp/.cpp ← Shared KM executor (both families)
│ ├── key_mgmt_context.hpp
│ └── key_mgmt_request_parser.hpp
├── score_provider/
│ ├── score_provider_config.hpp/.cpp ← Config / visitor
│ ├── score_provider_factory.hpp/.cpp
│ ├── score_provider.hpp ← Abstract base provider
│ ├── score_backend_adapter.hpp ← Backend adapter interface
│ ├── operations/ ← Score*Handler + *Executor bases
│ │ ├── hash/
│ │ ├── mac/
│ │ ├── key_management/
│ │ └── factory/
│ └── openssl/ ← OpenSSL concrete provider
│ ├── provider_openssl.hpp/.cpp
│ ├── operations/ ← OpenSsl*Handler implementations
│ ├── key_management/ ← OpenSslKeyHandler, OpenSslKeyFactory
│ └── detail/
└── pkcs11/ ← PKCS#11 provider family
├── pkcs11_token_config.hpp/.cpp ← Token config / visitor
├── pkcs11_provider_factory.hpp/.cpp
├── pkcs11_provider.hpp/.cpp
├── pkcs11_module.hpp/.cpp
├── pkcs11_session_guard.hpp
├── operations/
├── key_management/
└── detail/