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 (with ICryptoSigner, ICryptoHasher, and ICryptoProviderRegistry), not a single ISigningProvider facade. The Authority JWKS endpoint is /jwks (advertised through OIDC discovery), not /.well-known/jwks.json. Items that are genuinely proposed/roadmap (e.g. a unified ISigningProvider facade, 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:

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 valueNotes
Es256ES256ECDSA P-256. Default Authority signing algorithm.
Es384ES384ECDSA P-384.
Es512ES512ECDSA P-521.
Rs256RS256RSA PKCS#1 v1.5.
Rs384RS384RSA PKCS#1 v1.5.
Rs512RS512RSA PKCS#1 v1.5.
Ps256PS256RSA-PSS.
Ps384PS384RSA-PSS.
Ps512PS512RSA-PSS.
Ed25519ED25519EdDSA over Curve25519.
EdDsaEdDSAJWS EdDSA alg name.
GostR3410_2012_256GOST12-256GOST R 34.10-2012 (256-bit), CryptoPro/OpenSSL plugins.
GostR3410_2012_512GOST12-512GOST R 34.10-2012 (512-bit).
Sm2SM2SM2 (CN).
Dilithium3DILITHIUM3Post-quantum (experimental software plugin).
Falcon512FALCON512Post-quantum (experimental software plugin).

2.2 Hash algorithm identifiers

Grounded in src/__Libraries/StellaOps.Cryptography/HashAlgorithms.cs.

Identifier (HashAlgorithms.*)Constant valueCompliance
Sha256SHA256FIPS / eIDAS / KCMVP
Sha384SHA384FIPS / eIDAS
Sha512SHA512FIPS / eIDAS
Gost3411_2012_256GOST3411-2012-256GOST (RU)
Gost3411_2012_512GOST3411-2012-512GOST (RU)
Blake3_256BLAKE3-256World/default graph content-addressing (not FIPS-approved)
Sm3SM3GB/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).

ProfileIdStandard name
worldISO/Default (BLAKE3 graph hashing, SHA-256 elsewhere)
fipsFIPS 140-3
gostGOST R 34.11-2012 (Stribog)
smGB/T (SM3)
kcmvpKCMVP (Korea)
eidaseIDAS / 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):

2.5 Key sources

signing.keySource selects an IAuthoritySigningKeySource (src/Authority/.../Signing/):

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-configurationjwks_uri). It is not /.well-known/jwks.json. A custom middleware intercepts /jwks before OpenIddict’s built-in handler so the AuthorityJwksService document (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_ops or x5t#S256. The fields above (kty/use/kid/alg/crv/x/y/status) are what JwksKeyEntry serializes.

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:

3.3 Key rotation

Grounded in AuthoritySigningKeyManager.Rotate and the JWKS export rules:

  1. Rotation promotes a new activeKeyId; previously active keys are retained as additional (verification-only) keys so unexpired tokens still validate.
  2. /jwks includes the active key and all configured additional keys during the transition.
  3. Retired keys remain exported until removed from signing.additionalKeys (there is no automatic rotation_grace_period setting — 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 (respecting Cache-Control/ETag).

NOTE on kid: kid is taken from the signer’s exported JWK (ExportPublicJsonWebKey().Kid), which for the file source is the configured keyId. The “deterministic kid = 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:

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):

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 constantValuePurpose
PlatformCryptoReadcrypto:readRead crypto provider catalogue/preferences
PlatformCryptoAdmincrypto:adminMutate crypto provider catalogue
PlatformCryptoProfileAdmincrypto:profile:adminManage compliance profiles
PlatformCryptoKekReadcrypto:kek:readRead KEK control-plane state
PlatformCryptoKekRotatecrypto:kek:rotateRotate KEKs (granted separately from crypto:admin)
TrustReadtrust:readRead trust/signing state
TrustWritetrust:writeMutate trust/signing configuration
TrustAdmintrust:adminAdminister trust/signing operations
SignerReadsigner:readRead Signer config and key metadata
SignerSignsigner:signCreate signatures
SignerRotatesigner:rotateRotate Signer keys
SignerAdminsigner:adminAdminister Signer configuration
SmRemoteHash / SmRemoteEncrypt / SmRemoteDecrypt / SmRemoteSign / SmRemoteVerifysm-remote:hashsm-remote:verifySM 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):

8. Changelog

DateVersionChange
2025-12-061.0.0Initial contract draft (proposed ISigningProvider facade, KMS/regional support).
2026-05-301.1.0Reconciled 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.