Authority Crypto Provider Contract
Status: APPROVED Version: 1.1.0 Last Updated: 2026-05-30 Owner: Authority Core Guild Canonical sources:
src/__Libraries/StellaOps.Cryptography/(provider abstractions),src/Authority/StellaOps.Authority/StellaOps.Authority/Signing/(Authority signing + JWKS),src/__Libraries/StellaOps.Configuration/AuthoritySigningOptions.cs(config),src/__Libraries/StellaOps.Cryptography.Kms/(KMS clients).
Reconciliation note (2026-05-30): This document was reconciled against the implemented code. The pluggable crypto abstraction in Stella Ops is
ICryptoProvider(withICryptoSigner,ICryptoHasher, andICryptoProviderRegistry), not a singleISigningProviderfacade. The Authority JWKS endpoint is/jwks(advertised through OIDC discovery), not/.well-known/jwks.json. Items that are genuinely proposed/roadmap (e.g. a unifiedISigningProviderfacade, Azure Key Vault backend, per-tenant JWKS views) are explicitly marked NOT IMPLEMENTED below.
Overview
This contract defines the Authority signing/crypto provider model for Stella Ops, enabling pluggable cryptographic backends including:
- Software keys (default) — ECDSA P-256/P-384/P-521, RSA (RS/PS), Ed25519/EdDSA
- KMS / HSM integration — File KMS (envelope), AWS KMS, GCP KMS, PKCS#11, FIDO2
- Regional / sovereign compliance — GOST R 34.10/34.11-2012 (CryptoPro/OpenSSL), SM2/SM3 (CN), eIDAS/ETSI (EU), FIPS 140-3 hashing profile, KCMVP (KR)
- Post-quantum (experimental) — Dilithium3, Falcon-512 (software plugin)
The crypto abstraction lives in the shared library StellaOps.Cryptography and is consumed by Authority’s signing pipeline. Providers are discovered through ICryptoProviderRegistry and selected by policy/compliance profile. Authority’s own token issuance, revocation-bundle signing, DSSE statement signing, Vulnerability Explorer tokens/permalinks, and notification ack tokens all flow through the same registry that backs the /jwks export.
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ Stella Ops Crypto Provider model │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ ICryptoProviderRegistry (discovery + policy) ││
│ │ + Providers ││
│ │ + TryResolve(preferredProvider) → ICryptoProvider ││
│ │ + ResolveOrThrow(capability, algorithmId) → ICryptoProvider ││
│ │ + ResolveSigner(capability, algorithmId, keyRef, ?provider) ││
│ │ + ResolveHasher(algorithmId, ?provider) ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │ resolves │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ ICryptoProvider ││
│ │ + Name ││
│ │ + Supports(CryptoCapability, algorithmId) ││
│ │ + GetSigner(algorithmId, CryptoKeyReference) → ICryptoSigner ││
│ │ + GetHasher(algorithmId) → ICryptoHasher ││
│ │ + GetPasswordHasher(algorithmId) ││
│ │ + CreateEphemeralVerifier(algorithmId, publicKeyBytes) ││
│ │ + UpsertSigningKey / RemoveSigningKey / GetSigningKeys ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Default │ │ Plugin │ │ KMS-backed │ │
│ │ (BCL/soft) │ │ providers │ │ key source │ │
│ │ │ │ │ │ │ │
│ │ • File keys │ │ • GOST │ │ • File KMS │ │
│ │ • Ephemeral │ │ • SM (SM2/3) │ │ • AWS KMS │ │
│ │ verifier │ │ • eIDAS/FIPS │ │ • GCP KMS │ │
│ │ • Libsodium │ │ • HSM/PKCS11 │ │ • PKCS#11 │ │
│ │ │ │ • PQ (plugin)│ │ • FIDO2 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
1. Provider and Signer Interfaces
The real abstraction is split into a provider, a signer, a hasher, and a registry. There is no single ISigningProvider interface with Sign(data, keyId)/CreateKey/RotateKey/ExportJWKS methods — that shape is NOT IMPLEMENTED. Use the interfaces below.
1.1 ICryptoProvider
Grounded in src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs.
public interface ICryptoProvider
{
string Name { get; }
bool Supports(CryptoCapability capability, string algorithmId);
IPasswordHasher GetPasswordHasher(string algorithmId);
// Content hasher (e.g. SHA-256, GOST-R-34.11-2012-256).
ICryptoHasher GetHasher(string algorithmId);
// Signer for an algorithm + stored key handle.
ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference);
// Verification-only signer built from raw SubjectPublicKeyInfo bytes (DSSE inline keys).
// Default throws NotSupportedException unless the provider opts in.
ICryptoSigner CreateEphemeralVerifier(string algorithmId, ReadOnlySpan<byte> publicKeyBytes);
void UpsertSigningKey(CryptoSigningKey signingKey);
bool RemoveSigningKey(string keyId);
IReadOnlyCollection<CryptoSigningKey> GetSigningKeys();
}
public enum CryptoCapability
{
PasswordHashing,
Signing,
Verification,
SymmetricEncryption,
KeyDerivation,
ContentHashing
}
// Identifies a stored key/certificate handle.
public sealed record CryptoKeyReference(string KeyId, string? ProviderHint = null);
1.2 ICryptoSigner
Grounded in src/__Libraries/StellaOps.Cryptography/ICryptoSigner.cs.
public interface ICryptoSigner
{
string KeyId { get; }
string AlgorithmId { get; } // e.g. "ES256"
ValueTask<byte[]> SignAsync(
ReadOnlyMemory<byte> data,
CancellationToken cancellationToken = default);
ValueTask<bool> VerifyAsync(
ReadOnlyMemory<byte> data,
ReadOnlyMemory<byte> signature,
CancellationToken cancellationToken = default);
// Public JWK (no private components) — this is what /jwks serves.
Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey();
}
1.3 ICryptoProviderRegistry
Grounded in src/__Libraries/StellaOps.Cryptography/CryptoProvider.cs.
public interface ICryptoProviderRegistry
{
IReadOnlyCollection<ICryptoProvider> Providers { get; }
bool TryResolve(string preferredProvider, out ICryptoProvider provider);
ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId);
CryptoSignerResolution ResolveSigner(
CryptoCapability capability,
string algorithmId,
CryptoKeyReference keyReference,
string? preferredProvider = null);
CryptoHasherResolution ResolveHasher(string algorithmId, string? preferredProvider = null);
}
public sealed record CryptoSignerResolution(ICryptoSigner Signer, string ProviderName);
public sealed record CryptoHasherResolution(ICryptoHasher Hasher, string ProviderName);
1.4 ICryptoHasher
public interface ICryptoHasher
{
string AlgorithmId { get; } // e.g. "SHA256", "GOST3411-2012-256"
byte[] ComputeHash(ReadOnlySpan<byte> data);
string ComputeHashHex(ReadOnlySpan<byte> data);
}
1.5 Key material — CryptoSigningKey
Grounded in src/__Libraries/StellaOps.Cryptography/CryptoSigningKey.cs. This is the descriptor a provider holds in its in-memory store; there is no KeyInfo/KeySpec/KeyFilter/KeyState record set as previously documented (NOT IMPLEMENTED).
public enum CryptoSigningKeyKind { Ec, Raw }
public sealed class CryptoSigningKey
{
public CryptoKeyReference Reference { get; } // KeyId + provider hint
public string AlgorithmId { get; }
public ECParameters PrivateParameters { get; } // EC-backed keys
public ECParameters PublicParameters { get; }
public CryptoSigningKeyKind Kind { get; } // Ec | Raw
public ReadOnlyMemory<byte> PrivateKey { get; } // raw-key material (empty for EC keys)
public ReadOnlyMemory<byte> PublicKey { get; }
public DateTimeOffset CreatedAt { get; }
public DateTimeOffset? ExpiresAt { get; }
public IReadOnlyDictionary<string, string?> Metadata { get; } // "use", "status", "tenant_scope", …
}
Verification-only keys are constructed with the verificationOnly: true overload (public EC parameters, no private scalar).
2. Supported Algorithms
2.1 Signature algorithm identifiers
Grounded in src/__Libraries/StellaOps.Cryptography/SignatureAlgorithms.cs. These are the string identifiers used by the code, not OIDs. (The previous OID-keyed table was illustrative and used identifiers that do not match the codebase; identifiers below are authoritative.)
Identifier (SignatureAlgorithms.*) | Constant value | Notes |
|---|---|---|
Es256 | ES256 | ECDSA P-256. Default Authority signing algorithm. |
Es384 | ES384 | ECDSA P-384. |
Es512 | ES512 | ECDSA P-521. |
Rs256 | RS256 | RSA PKCS#1 v1.5. |
Rs384 | RS384 | RSA PKCS#1 v1.5. |
Rs512 | RS512 | RSA PKCS#1 v1.5. |
Ps256 | PS256 | RSA-PSS. |
Ps384 | PS384 | RSA-PSS. |
Ps512 | PS512 | RSA-PSS. |
Ed25519 | ED25519 | EdDSA over Curve25519. |
EdDsa | EdDSA | JWS EdDSA alg name. |
GostR3410_2012_256 | GOST12-256 | GOST R 34.10-2012 (256-bit), CryptoPro/OpenSSL plugins. |
GostR3410_2012_512 | GOST12-512 | GOST R 34.10-2012 (512-bit). |
Sm2 | SM2 | SM2 (CN). |
Dilithium3 | DILITHIUM3 | Post-quantum (experimental software plugin). |
Falcon512 | FALCON512 | Post-quantum (experimental software plugin). |
2.2 Hash algorithm identifiers
Grounded in src/__Libraries/StellaOps.Cryptography/HashAlgorithms.cs.
Identifier (HashAlgorithms.*) | Constant value | Compliance |
|---|---|---|
Sha256 | SHA256 | FIPS / eIDAS / KCMVP |
Sha384 | SHA384 | FIPS / eIDAS |
Sha512 | SHA512 | FIPS / eIDAS |
Gost3411_2012_256 | GOST3411-2012-256 | GOST (RU) |
Gost3411_2012_512 | GOST3411-2012-512 | GOST (RU) |
Blake3_256 | BLAKE3-256 | World/default graph content-addressing (not FIPS-approved) |
Sm3 | SM3 | GB/T 32905-2016 (CN) |
2.3 Compliance profiles
Compliance profiles are hash/HMAC-purpose mappings, not signing-provider switches. Grounded in src/__Libraries/StellaOps.Cryptography/ComplianceProfiles.cs (ComplianceProfiles.All).
ProfileId | Standard name |
|---|---|
world | ISO/Default (BLAKE3 graph hashing, SHA-256 elsewhere) |
fips | FIPS 140-3 |
gost | GOST R 34.11-2012 (Stribog) |
sm | GB/T (SM3) |
kcmvp | KCMVP (Korea) |
eidas | eIDAS / ETSI TS 119 312 |
Each profile maps HashPurpose keys (Graph, Symbol, Content, Merkle, Attestation, Interop, Secret) to algorithm identifiers, and HmacPurpose keys to HMAC algorithms. Interop purposes fall back to SHA-256 for external tool compatibility even in GOST/SM profiles (AllowInteropOverride = true).
2.4 Default configuration
Authority signing configuration lives under the top-level signing: section of etc/authority.yaml (bound to AuthoritySigningOptions). There is no top-level crypto: block with software/pkcs11/aws_kms/azure_keyvault/gcp_kms sub-keys as previously documented — that shape is NOT IMPLEMENTED. The authoritative keys are:
# etc/authority.yaml
signing:
enabled: true # AuthoritySigningOptions.Enabled
activeKeyId: "authority-signing-dev" # active kid
keyPath: "../certificates/authority-signing-dev.pem" # PEM private key (file source)
algorithm: "ES256" # default ES256
keySource: "file" # "file" | "kms" | "vault"
provider: null # optional ICryptoProviderRegistry hint
jwksCacheLifetime: "00:05:00" # 0 < value <= 1h (default 15m in code)
additionalKeys: [] # retired keys retained for verification
# additionalKeys:
# - keyId: "authority-signing-2025"
# path: "../certificates/authority-signing-2025.pem"
# source: "file" # defaults to top-level keySource
Notes (grounded in AuthoritySigningOptions.Validate):
- When
enabled: true, bothactiveKeyIdandkeyPathare required. algorithmdefaults toES256;keySourcedefaults tofileif blank.jwksCacheLifetimemust be> 0and<= 1 hour, otherwise startup fails.keyPassphraseexists on the options object but is not yet supported (no-op).
2.5 Key sources
signing.keySource selects an IAuthoritySigningKeySource (src/Authority/.../Signing/):
file→FileAuthoritySigningKeySource(PEM key material atkeyPath).kms→KmsAuthoritySigningKeySource, which loads/exports material from anIKmsClient(KmsAuthoritySigningKeySource.cs). KMS keys carrykms.versionmetadata.
The KMS client backends implemented in src/__Libraries/StellaOps.Cryptography.Kms/ are: File KMS (envelope, default offline), AWS KMS, GCP KMS, PKCS#11, and FIDO2.
NOT IMPLEMENTED: There is no Azure Key Vault KMS client. References to
azure-keyvault/ Azure KV in earlier drafts are roadmap only. On-prem/offline-first deployments should prefer the File KMS or PKCS#11/HSM backends; cloud KMS backends (AWS/GCP) exist but are not the default posture.
3. JWKS Export
3.1 JWKS endpoint
The Authority service exposes JWKS at:
GET /jwks
Grounded in src/Authority/.../Program.cs (app.MapGet("/jwks", …) and options.SetJsonWebKeySetEndpointUris("/jwks")) and AuthorityJwksService.
CORRECTION: The endpoint is
/jwks, advertised through the OIDC discovery document (/.well-known/openid-configuration→jwks_uri). It is not/.well-known/jwks.json. A custom middleware intercepts/jwksbefore OpenIddict’s built-in handler so theAuthorityJwksServicedocument (which correctly enumerates provider keys) wins.
Response is camelCase JSON with null fields omitted (AuthorityJwksService.SerializerOptions). Each key entry (JwksKeyEntry) carries: kty, use, kid, alg, crv, x, y, and an optional status (active by default; omitted when null).
{
"keys": [
{
"kty": "EC",
"use": "sig",
"kid": "authority-signing-dev",
"alg": "ES256",
"crv": "P-256",
"x": "base64url-encoded-x",
"y": "base64url-encoded-y",
"status": "active"
}
]
}
CORRECTION: The implemented JWKS entry does not emit
key_opsorx5t#S256. The fields above (kty/use/kid/alg/crv/x/y/status) are whatJwksKeyEntryserializes.
The endpoint sets a strong ETag (SHA-256 over the serialized document + expiry) and a Cache-Control: public, max-age=<jwksCacheLifetime seconds> header. Responses are cached in-process for signing.jwksCacheLifetime.
3.2 Which keys are exported
Grounded in AuthorityJwksService.BuildKeys / GetConfiguredJwksKeyIds:
- Only keys whose
kidmatches a configured Authority signing or ack-token key are exported:signing.activeKeyId, eachsigning.additionalKeys[].keyId, and — whennotifications.ackTokens.enabled— the ack-token active/additional key ids. - Keys carrying
tenant_scope/tenant_scopesmetadata are withheld from the global/jwksview (a per-tenant JWKS endpoint does not yet exist — NOT IMPLEMENTED). - Entries are sorted by
kid(ordinal ascending) for deterministic, offline-verifiable output.
3.3 Key rotation
Grounded in AuthoritySigningKeyManager.Rotate and the JWKS export rules:
- Rotation promotes a new
activeKeyId; previously active keys are retained as additional (verification-only) keys so unexpired tokens still validate. /jwksincludes the active key and all configured additional keys during the transition.- Retired keys remain exported until removed from
signing.additionalKeys(there is no automaticrotation_grace_periodsetting — removal is operator-driven via config). The previously documented automatic 7-day grace period and JWKS webhook push are NOT IMPLEMENTED; consumers refresh JWKS by polling (respectingCache-Control/ETag).
NOTE on
kid:kidis taken from the signer’s exported JWK (ExportPublicJsonWebKey().Kid), which for the file source is the configuredkeyId. The “deterministickid = sha256(spki || profile_id)” recipe in the canonical module dossier (docs/modules/authority/crypto-provider-contract.md) is a recommended convention, not a hard guarantee of the current file-source implementation.
3.4 Key discovery flow
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Scanner │ │ Authority │ │ Attestor │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ GET /jwks │ │
│───────────────>│ │
│<───────────────│ │
│ JWKS │ │
│ │ │
│ (verify Authority-issued tokens / DSSE statements locally)
│ │ │
│ │ GET /jwks │
│ │<────────────────│
│ │────────────────>│
│ │ JWKS │
│ │ ✓ Valid │
Note: signing of SBOM/DSSE material is performed by the Scanner/Attestor/Signer services using their own keys/Signer scopes; Authority is the trust-root JWKS issuer, not a remote Sign(payload) RPC for arbitrary callers. The previous “Scanner → Authority Sign(SBOM) → Signature” RPC flow is NOT IMPLEMENTED at the Authority surface.
4. Provider Registration
4.1 Service registration
The DI registration is not the services.AddAuthoritySigningProvider(options => …) switch over software/pkcs11/aws-kms/gcp-kms/azure-keyvault shown in earlier drafts (NOT IMPLEMENTED in that form). In practice:
- The crypto provider registry (
ICryptoProviderRegistry) and the default/plugin providers are registered by theStellaOps.Cryptographyregistration extensions; regional/HSM/PQ providers ship as plugins undersrc/Cryptography/StellaOps.Cryptography.Plugin.*(Eidas,Fips,Gost,Hsm,OpenPgp,Sm). - Authority registers
AuthorityJwksService, the signing key sources (file,kms), and the key managers; the active provider is selected persigning.provider(a registry hint) and per compliance/tenant policy viaICryptoProviderRegistry.ResolveSigner(...). - KMS backends are wired through
IKmsClientimplementations (File/AWS/GCP/PKCS#11/FIDO2) with their ownServiceCollectionExtensions.*registration helpers inStellaOps.Cryptography.Kms.
4.2 Authority signing/JWKS binding contract
Authority’s token issuance, revocation-bundle signing, DSSE statements, Vulnerability Explorer tokens/permalinks, and notification ack tokens all use the same signing registry as /jwks (grounded in docs/modules/authority/crypto-provider-contract.md §5.1 and the Signing/ sources):
signing.activeKeyIdselects the activekid.signing.keySourceloads/registers the key throughIAuthoritySigningKeySource.signing.providerselects the crypto provider throughICryptoProviderRegistry.ICryptoSigner.ExportPublicJsonWebKey()returns the public JWK served by/jwks, with matchingkid,alg,kty.- Every Authority signing path fails closed if the resolved signer’s
KeyId/AlgorithmIddiffers from the configured key/algorithm, and a missing/unsupported configured provider fails startup, rotation, or issuance rather than silently falling back. Authority never substitutes an ephemeral OpenIddict signing key outside Development/Testing.
5. Crypto-related scopes
Grounded in src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOpsScopes.cs. There are no CRYPTO_001…008 provider-API error codes in the codebase (the earlier table was illustrative and is NOT IMPLEMENTED). The actual authorization surface relevant to crypto/signing is:
| Scope constant | Value | Purpose |
|---|---|---|
PlatformCryptoRead | crypto:read | Read crypto provider catalogue/preferences |
PlatformCryptoAdmin | crypto:admin | Mutate crypto provider catalogue |
PlatformCryptoProfileAdmin | crypto:profile:admin | Manage compliance profiles |
PlatformCryptoKekRead | crypto:kek:read | Read KEK control-plane state |
PlatformCryptoKekRotate | crypto:kek:rotate | Rotate KEKs (granted separately from crypto:admin) |
TrustRead | trust:read | Read trust/signing state |
TrustWrite | trust:write | Mutate trust/signing configuration |
TrustAdmin | trust:admin | Administer trust/signing operations |
SignerRead | signer:read | Read Signer config and key metadata |
SignerSign | signer:sign | Create signatures |
SignerRotate | signer:rotate | Rotate Signer keys |
SignerAdmin | signer:admin | Administer Signer configuration |
SmRemoteHash / SmRemoteEncrypt / SmRemoteDecrypt / SmRemoteSign / SmRemoteVerify | sm-remote:hash … sm-remote:verify | SM cryptography remote service operations |
6. Observability
Grounded in src/__Libraries/StellaOps.Cryptography/CryptoProviderMetrics.cs. The crypto registry emits metrics under the meter stellaops.crypto (v1.0.0):
crypto_provider_resolutions_total— successful provider resolutions.crypto_provider_resolution_failures_total— failed provider resolutions.
7. Related docs
- Canonical Authority crypto/JWKS contract:
docs/modules/authority/crypto-provider-contract.md - Crypto provider registry contract:
docs/contracts/crypto-provider-registry.md - Platform cryptography & compliance:
docs/modules/platform/cryptography-and-compliance.md - Regional crypto route matrix:
docs/modules/platform/regional-crypto-route-matrix.md - SM Remote crypto service:
docs/features/checked/smremote/sm-remote-crypto-service.md
8. Changelog
| Date | Version | Change |
|---|---|---|
| 2025-12-06 | 1.0.0 | Initial contract draft (proposed ISigningProvider facade, KMS/regional support). |
| 2026-05-30 | 1.1.0 | Reconciled against implemented code: replaced the proposed ISigningProvider facade with the real ICryptoProvider/ICryptoSigner/ICryptoProviderRegistry interfaces and CryptoSigningKey; corrected JWKS endpoint to /jwks and JWKS entry fields; corrected algorithm/hash identifiers and compliance profile ids; corrected config to the signing: block and file/kms key sources; documented real KMS backends (File/AWS/GCP/PKCS#11/FIDO2) and marked Azure KV as not implemented; replaced fabricated CRYPTO_00x error codes and Tasks-Unblocked table with the real crypto:*/trust:*/signer:*/sm-remote:* scopes and stellaops.crypto metrics. |
