ca

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: AGPL-3.0 Imports: 50 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultAgentProxyMaxValidity = 1 * time.Hour

DefaultAgentProxyMaxValidity is the default hard upper bound for agent-proxy certificate validity (1 hour).

View Source
const DefaultCACertURL = "https://curl.se/ca/cacert.pem"
View Source
const MaxAgentProxyValidity = 24 * time.Hour

MaxAgentProxyValidity is the spec-mandated hard ceiling for agent-proxy (authorized mode) certificate validity. Per AIC spec §3.4, agent sessions must not exceed 24 hours. M2 fix: this bound is enforced at the CA library layer so it cannot be silently bypassed by any caller-supplied configuration.

View Source
const MaxAuthorizationConstraints = 32

MaxAuthorizationConstraints caps the constraint list on a PrincipalAuthorization, mirroring types.MaxAuthorizationConstraints (the AIC extension enforces the same bound on its own copy).

View Source
const MaxGrantEntries = 256

MaxGrantEntries is the maximum number of entries in PrincipalAuthorization.grants (spec P1-A-24).

View Source
const MinRSAKeySize = 2048

MinRSAKeySize is the minimum RSA key length (bits) allowed for signing. Issuance is rejected below this value to prevent weak keys (NIST SP 800-57 / MLPS 2.0).

Variables

View Source
var (
	ErrNoResult = &AggregatorError{"no result"}
	ErrTimeout  = &AggregatorError{"timeout"}
)
View Source
var (
	OIDSigECDSAWithSHA256  = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
	OIDSigECDSAWithSHA384  = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
	OIDSigECDSAWithSHA512  = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
	OIDSigRSAWithSHA256    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
	OIDSigRSAWithSHA384    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
	OIDSigRSAWithSHA512    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
	OIDSigRSAPSSWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
	OIDSigEd25519          = asn1.ObjectIdentifier{1, 3, 101, 112}
)

Signature algorithm OID constants.

View Source
var (
	// ── 1.x Identity and Permission Extensions ──
	OIDIdentityExt = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1}

	// 1.1 AIC (core patent: AIC + natural person binding + capability protocolization)
	OIDAIC              = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 1}
	OIDAICAgentIdentity = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 1, 1}
	// DelegationAuthorization (.1.1.2, principal signature evidence; pre-v1.5 old name UserAuth).
	OIDAICDelegationAuthorization = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 1, 2}
	// DelegationDepthControl (.1.1.4, spec v1.7.2 §3.7, FUTURE delegation depth control).
	OIDDelegationDepthControl = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 1, 4}
	OIDDDCChainDepth          = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 1, 4, 1}
	OIDDDCMaxDepth            = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 1, 4, 2}

	// 1.2 PrincipalAuthorization (v1.5: replaces old UserPermission)
	OIDPrincipalAuthorization = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 2}

	// 1.6 RenewalToken (auto-renewal token, I-D §6)
	OIDRenewalToken = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 1, 6}

	// ── 3.x National/Industry Certification Extensions ──
	OIDCertificationExt = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 3}

	// 3.1 MarketAccessId (complete credential, companion patent: national certification)
	OIDMarketAccessId = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 3, 1}

	// 3.2 TrustLevel
	OIDTrustLevel = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 3, 2}

	// 3.3 CrossBorder (reserved placeholder)
	OIDCrossBorder = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 3, 3}

	// ── 5.x GM (Chinese National Cryptography) Extensions ──
	OIDGM        = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 5}
	OIDSM2Sig    = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 5, 1}
	OIDSM3Hash   = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 5, 2}
	OIDSM4Enc    = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 5, 3}
	OIDSM2SM3Sig = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 5, 4}

	// ── 6.x Certificate Transparency Extensions ──
	OIDCT    = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 6}
	OIDCTSCT = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 6, 1}
	OIDCTLog = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 66257, 6, 2}
)

IANA-assigned PEN: 66257, OID root: 1.3.6.1.4.1.66257

View Source
var (
	ErrSubCAAlreadyExists = errors.New("sub-CA already exists")
	ErrSubCANotFound      = errors.New("sub-CA not found")
	ErrSubCARevoked       = errors.New("sub-CA is revoked")
	ErrSubCAExpired       = errors.New("sub-CA is expired")
	ErrAdminCertRequired  = errors.New("admin certificate with keyCertSign usage required")
	ErrInvalidAdminCert   = errors.New("invalid admin certificate")
)
View Source
var ErrNoAIC = fmt.Errorf("certificate has no AIC extension")

ErrNoAIC is returned when a certificate does not contain an AIC extension.

View Source
var ErrRootCAImport = errors.New("root CA key must not be imported into database; keep it offline")
View Source
var HashAlgoOIDs = map[string]asn1.ObjectIdentifier{
	"sha256":   {2, 16, 840, 1, 101, 3, 4, 2, 1},
	"sha384":   {2, 16, 840, 1, 101, 3, 4, 2, 2},
	"sha512":   {2, 16, 840, 1, 101, 3, 4, 2, 3},
	"sha3-256": {2, 16, 840, 1, 101, 3, 4, 2, 8},
	"sha3-384": {2, 16, 840, 1, 101, 3, 4, 2, 9},
	"sha3-512": {2, 16, 840, 1, 101, 3, 4, 2, 10},
	"sm3":      {1, 2, 156, 10197, 1, 401},
}

HashAlgoOIDs maps hash algorithm name strings to OIDs.

View Source
var RevokeReasons = map[string]int{
	"unspecified":          0,
	"keyCompromise":        1,
	"cACompromise":         2,
	"affiliationChanged":   3,
	"superseded":           4,
	"cessationOfOperation": 5,
	"certificateHold":      6,
}

Functions

func BackfillAICFields

func BackfillAICFields(d *db.DB) error

BackfillAICFields scans all valid agent-proxy certificates that lack principal_uid/agent_id and backfills them from the AIC extension in cert_der. Idempotent — safe to call on every startup.

func BridgeTrustPEMs

func BridgeTrustPEMs(database *db.DB, bridges []TrustBridgePolicy, caCfgs map[string]struct {
	Cert string
	Key  string
}) ([]*db.CrossCertRecord, error)

func BuildAIC

func BuildAIC(cfg AICConfig) (pkix.Extension, error)

BuildAIC constructs the AIC X.509v3 extension from configuration (v1.7.1 spec: DA required, critical=false). Validation coverage: V6 keyHash length, V8 lifetime 1..86400, V10/V15 constraint schemeId whitelist, V16 non-empty authorization, R1/R2/R4/R5 Reason required and length, nonce 32 bytes.

func BuildChain

func BuildChain(leaf *x509.Certificate, src CertSource, maxDepth int) ([]*x509.Certificate, bool, error)

BuildChain builds a certificate path from leaf up to a trust anchor using the given source. It performs no trust/policy evaluation — only structural walks (issuer matching, cycle protection, depth bound). Returns the path leaf-first (chain[0]=leaf, chain[len-1]=root candidate) and whether the terminal cert is in the trust anchors.

func BuildFederatedTrustPool

func BuildFederatedTrustPool(database *db.DB, localCAs map[string]*x509.Certificate, crossCerts []*db.CrossCertRecord) (*x509.CertPool, error)

func BuildJWKSJSON

func BuildJWKSJSON(certs []*x509.Certificate) ([]byte, error)

BuildJWKSJSON returns the marshaled JWKS for a certificate set.

func BuildPrincipalAuthorizationExtension

func BuildPrincipalAuthorizationExtension(cfg PrincipalAuthorizationConfig) (pkix.Extension, error)

BuildPrincipalAuthorizationExtension builds a PrincipalAuthorization X.509v3 extension from the configuration.

func BuildRenewalToken

func BuildRenewalToken(oldCertSerial []byte, principalUid PrincipalUid, newKeyHash []byte, store NonceStorer) (pkix.Extension, error)

BuildRenewalToken constructs a RenewalToken X.509v3 extension. oldCertSerial is the DER-encoded serial number of the certificate requesting renewal, principalUid is the principal identity identifier, newKeyHash is the SHA-256 hash of the new public key (32 bytes). If store is non-nil, the nonce is persisted (for one-time-use enforcement).

func CRLFilename

func CRLFilename(caName string, partition, total int) string

func CertToPEM

func CertToPEM(der []byte) []byte

func CheckLDAPGroupMembership

func CheckLDAPGroupMembership(entry *ldap.Entry, cfg *LDAPConfig, groupDN string) bool

func CheckPolicy

func CheckPolicy(sc *SignConfig) error

func CheckPublicKeyStrength

func CheckPublicKeyStrength(pub any) error

CheckPublicKeyStrength validates the strength of the public key to be signed, rejecting weak keys. Rules:

  • RSA < MinRSAKeySize: reject
  • EC curve not in NIST P-256/P-384/P-521 whitelist: reject
  • Ed25519: always accept
  • Unknown type: return warning (non-blocking, to prevent compatibility regression)

func ColdBackupCA

func ColdBackupCA(caName, certPath, keyPath, password, keyPassword, outPath string) error

ColdBackupCA reads the CA certificate and key, encrypts the key under password, and writes a tamper-evident JSON envelope to outPath. The key is loaded via ParsePrivateKey (supports encrypted PEM via keyPassword).

func DecryptKeyPKCS8

func DecryptKeyPKCS8(der []byte, password string) (crypto.Signer, error)

func DecryptPrivateKey

func DecryptPrivateKey(blob []byte, priv crypto.PrivateKey) ([]byte, error)

func DecryptPrivateKeyPEM

func DecryptPrivateKeyPEM(pemData []byte, password string) (crypto.Signer, error)

DecryptPrivateKeyPEM decrypts a PEM-encoded encrypted private key. Also handles unencrypted "PRIVATE KEY" and combined cert+key files.

func DefaultHashAlgo

func DefaultHashAlgo() asn1.ObjectIdentifier

DefaultHashAlgo returns the default SHA-256 OID.

func DefaultPolicyPath

func DefaultPolicyPath(cfgDir string) string

func EncryptKeyDERPKCS8

func EncryptKeyDERPKCS8(privDER []byte, password string) ([]byte, error)

func EncryptKeyPKCS8

func EncryptKeyPKCS8(priv crypto.Signer, password string) ([]byte, error)

func EncryptPrivateKey

func EncryptPrivateKey(privDER []byte, pub crypto.PublicKey) ([]byte, error)

func EncryptPrivateKeyPEM

func EncryptPrivateKeyPEM(key crypto.Signer, password string) ([]byte, error)

EncryptPrivateKeyPEM encrypts a private key with a password using PKCS#8 EncryptedPrivateKeyInfo (PBKDF2-SHA256 + AES-256-CBC, RFC 5958). Output is PEM-compatible with OpenSSL's "ENCRYPTED PRIVATE KEY" format:

openssl pkey -aes-256-cbc -pass pass:xxx -in key.pem -out enc-key.pem

func EstablishTrustBridge

func EstablishTrustBridge(database *db.DB, issuerCert *x509.Certificate, issuerKey crypto.Signer, issuerName string, subjectMeta *db.CAMeta, validity time.Duration) (*db.CrossCertRecord, error)

func ExportFederatedTrust

func ExportFederatedTrust(database *db.DB, source string) ([]byte, error)

func ExportSubCA

func ExportSubCA(database *db.DB, name string) (certPEM, keyPEM []byte, err error)

ExportSubCA exports a sub-CA as PEM certificates and private key.

func ExtractAdminScope

func ExtractAdminScope(cert *x509.Certificate) string

ExtractAdminScope extracts the sub-CA management scope from an admin certificate. Scope is stored in OID 1.3.6.1.4.1.66257.1.5.1 and/or SAN URIs (urn:pki:ca:<scope>). The two are merged and de-duplicated.

func ExtractCertFields

func ExtractCertFields(cert *x509.Certificate) (o, c, issuerDN, keyAlgo string, keySize int, sigAlgo, ski, aki, san string)

func ExtractPrincipalUID

func ExtractPrincipalUID(der []byte) (string, error)

ExtractPrincipalUID parses the AIC PrincipalUid out of a certificate's DER. Returns ErrNoAIC when the cert carries no AIC or its PrincipalUid is empty.

func ExtractSPKIHash

func ExtractSPKIHash(cert *x509.Certificate) string

func ExtractTrustAnchorFields

func ExtractTrustAnchorFields(cert *x509.Certificate) (o, c, keyAlgo string, keySize int, sha1fp string, pathLen int)

func FetchCACertBundle

func FetchCACertBundle(url string) ([]byte, error)

func FlushSignerCache

func FlushSignerCache(keyPath ...string)

func GenerateCRL

func GenerateCRL(cfg *CRLConfig) ([]byte, error)

func GenerateDeltaCRL

func GenerateDeltaCRL(cfg *CRLConfig, dcfg *DeltaCRLConfig) ([]byte, error)

GenerateDeltaCRL builds a Delta CRL (RFC 5280 §5.2.4) covering only the certificates revoked since the base CRL thisUpdate. It carries the Delta CRL Indicator (2.5.29.27) and Base CRL Number (2.5.29.31) extensions, which let clients merge the delta with the base CRL.

The source must implement RevokedEntriesSinceSource (both *db.DB and *engine.Engine do). When the configured source does not, a full-CRL query is used and entries are filtered in memory.

func GenerateKey

func GenerateKey(keyType string) (crypto.Signer, error)

func GenerateSubCAKey

func GenerateSubCAKey(keyType string) (crypto.Signer, error)

GenerateSubCAKey generates a private key for a sub-CA.

func GetSubCACertChain

func GetSubCACertChain(database *db.DB, subCAName string) ([]*x509.Certificate, error)

GetSubCACertChain returns the sub-CA certificate chain (sub-CA + parent CA).

func ImportExternalCA

func ImportExternalCA(database *db.DB, name string, certPEM, keyPEM []byte, password string) (*db.CAMeta, error)

func IsAutoRenewable

func IsAutoRenewable(certDER []byte, caCert *x509.Certificate) bool

func IsEncryptedPEM

func IsEncryptedPEM(data []byte) bool

IsEncryptedPEM checks whether a PEM block is an encrypted private key.

func IsRootCAProfile

func IsRootCAProfile(p Profile) bool

func JWSToAlg

func JWSToAlg(pub any) (string, error)

JWSToAlg derives the JOSE algorithm implied by a public key.

func KeyToPEM

func KeyToPEM(key crypto.Signer) ([]byte, error)

func LoadAdminPublicKey

func LoadAdminPublicKey(path string) (crypto.PublicKey, error)

func LoadPrivateKey

func LoadPrivateKey(keyPath string, keyPassword ...string) (crypto.Signer, error)

func LoadSigner

func LoadSigner(certPath, keyPath string, keyPassword ...string) (*x509.Certificate, crypto.Signer, error)

func LoadSignerAny

func LoadSignerAny(certPath, keyPath string, database *db.DB, caName, keyPassword string) (*x509.Certificate, crypto.Signer, error)

func LoadSignerFromDB

func LoadSignerFromDB(database *db.DB, caName, password string) (*x509.Certificate, crypto.Signer, error)

func LoadTrustPool

func LoadTrustPool(cfgCAs map[string]string, database *db.DB) (*x509.CertPool, error)

func LoadTrustPoolWithSources

func LoadTrustPoolWithSources(cfgCAs map[string]string, database *db.DB) (*x509.CertPool, map[string]string, error)

func LookupLDAP

func LookupLDAP(conn *ldap.Conn, cfg *LDAPConfig, username string) (*ldap.Entry, *pkix.Name, error)

func NewLDAPConn

func NewLDAPConn(cfg *LDAPConfig) (*ldap.Conn, error)

func NormalizeSerial

func NormalizeSerial(serial string) (string, error)

func ParseCSR

func ParseCSR(pemBytes []byte) (*x509.CertificateRequest, error)

func ParseCTLogPublicKey

func ParseCTLogPublicKey(s string) (crypto.PublicKey, error)

ParseCTLogPublicKey parses a CT log public key given as either a base64 DER SPKI string or a PEM block (PUBLIC KEY). It returns the parsed key.

func ParseHashAlgo

func ParseHashAlgo(s string) (asn1.ObjectIdentifier, error)

ParseHashAlgo parses a hash algorithm string (e.g. "sha256") to an OID. Returns nil if empty (caller should use default SHA-256).

func ParsePrivateKey

func ParsePrivateKey(keyPEM []byte, keyPassword ...string) (crypto.Signer, error)

func ParseRevokeReason

func ParseRevokeReason(s string) (int, error)

func ParseSubCACert

func ParseSubCACert(certPEM []byte) (*x509.Certificate, error)

ParseSubCACert parses a sub-CA certificate from PEM bytes.

func ParseSubCAKey

func ParseSubCAKey(keyPEM []byte, keyPassword ...string) (crypto.Signer, error)

ParseSubCAKey parses a sub-CA private key from PEM bytes. Supports both plain and encrypted (PKCS#8 PBES2) PEM. If key is encrypted, password can be provided; if empty, falls back to PKI_KEY_PASSWORD env.

func ProcessApproval

func ProcessApproval(database *db.DB, requestID int, approver, decision, comment string, signFn SignCSRCallback) (approved bool, issuedSerial string, err error)

func RejectRequest

func RejectRequest(database *db.DB, requestID int, reason string) error

func RemoteSignerConfig

func RemoteSignerConfig() *remotesigner.Config

RemoteSignerConfig returns the current remote signer config.

func Revoke

func Revoke(d *db.DB, caName, serial string, reason int) error

func RevokeByPrincipalUid

func RevokeByPrincipalUid(d *db.DB, principalUid string, reason int) (int, error)

RevokeByPrincipalUid revokes all valid certificates matching the given PrincipalUid. Uses SQL-based filter (fast path) when the principal_uid column is populated, falls back to DER scan for legacy data.

func RevokeBySubCA

func RevokeBySubCA(d *db.DB, caName string, reason int) (int, error)

RevokeBySubCA revokes all valid certificates under a given CA (sub-CA name). Returns the number of revoked certs.

func RevokeSubCA

func RevokeSubCA(database *db.DB, name string, reason int) error

RevokeSubCA revokes a sub-CA by name.

func RevokeWithCascade

func RevokeWithCascade(d *db.DB, caName, serial string, reason int) (int, error)

RevokeWithCascade revokes a certificate and cascade-revokes all agent certificates with the same PrincipalUid.

func SPKIHash

func SPKIHash(pub crypto.PublicKey) []byte

SPKIHash returns the SPKI DER SHA-256 digest of the public key (v1.7.1 keyHash computation method).

func SPKISHA256

func SPKISHA256(cert *x509.Certificate) string

SPKISHA256 returns base64url(SHA-256(SubjectPublicKeyInfo)) — the key_hash convention used for both X.509 PrincipalUid and AIC-JWT kid.

func SanitizeCAName

func SanitizeCAName(name string) string

func SeedCRLNumber

func SeedCRLNumber(n int64)

SeedCRLNumber sets the CRL number counter to at least n. Call this at startup with the last known CRL number (from DB or parsed CRL file) to prevent RFC 5280 monotonicity violation across restarts.

func SetRemoteSignerConfig

func SetRemoteSignerConfig(cfg *remotesigner.Config)

SetRemoteSignerConfig enables remote HSM signing for all LoadSigner calls.

func SignCACSR

func SignCACSR(cfg *OfflineSignConfig) ([]byte, *big.Int, error)

func SubmitCertificate

func SubmitCertificate(url, apiKey string, cert *x509.Certificate, chain []*x509.Certificate, optHTTPClient ...*http.Client) (sctVersion int, logID string, timestamp uint64, extensions string, sigDER []byte, err error)

func SubmitRequest

func SubmitRequest(database *db.DB, csrDER []byte, cn, sanList, profile, caName, requester string, requiredApprovals int) (int, error)

func TrustAnchorFederate

func TrustAnchorFederate(database *db.DB, remoteURL string) (int, error)

func ValidateAdminCert

func ValidateAdminCert(cert *x509.Certificate) error

ValidateAdminCert verifies that a client certificate has admin privileges. Admin certificates must:

  • Have DigitalSignature key usage
  • Be signed by a trusted CA in the provided pool
  • Have a valid scope matching the target sub-CA (if scope is set)
  • Not be revoked (CRL check)

Note: admin certs are now entity certs (IsCA=false), not CA certs.

func ValidateAdminCertFromPEM

func ValidateAdminCertFromPEM(certPEM []byte) (*x509.Certificate, error)

ValidateAdminCertFromPEM validates an admin certificate from PEM bytes.

func ValidateAdminCertFromPEMWithPool

func ValidateAdminCertFromPEMWithPool(certPEM []byte, pool *x509.CertPool, targetCA string) (*x509.Certificate, error)

ValidateAdminCertFromPEMWithPool validates an admin certificate from PEM bytes against an explicit trust pool. Unlike ValidateAdminCertFromPEMWithTarget (which skips chain verification because it has no trust anchors), this performs full chain verification against the pool — the only path that prevents a self-signed or expired attacker certificate from being accepted as an "admin certificate" (H9 fix). When targetCA is non-empty, the certificate's management scope must also cover targetCA.

func ValidateAdminCertFromPEMWithTarget

func ValidateAdminCertFromPEMWithTarget(certPEM []byte, targetCA string) (*x509.Certificate, error)

ValidateAdminCertFromPEMWithTarget validates an admin certificate from PEM bytes and, when targetCA is non-empty, checks that the certificate's management scope covers targetCA.

func ValidateAdminCertWithTarget

func ValidateAdminCertWithTarget(cert *x509.Certificate, chain *x509.CertPool, targetCA string) error

ValidateAdminCertWithTarget performs full admin cert validation:

  • chain: trust pool for chain verification (nil = skip chain check)
  • targetCA: name of the sub-CA to manage (empty = skip scope check)

func ValidateAndConsumeNonce

func ValidateAndConsumeNonce(token *RenewalTokenExt, store NonceStorer) error

ValidateAndConsumeNonce validates one-time nonce usage and consumes it. Combined check: length + not expired + not already used; atomically consumes on pass.

func ValidateOutboundHTTPURL

func ValidateOutboundHTTPURL(rawurl string) error

ValidateOutboundHTTPURL checks that url is an http(s) URL with a non-empty host whose literal or resolved addresses are not internal/private/loopback (SSRF defense). Used when registering outbound destinations such as webhook subscriptions so the server cannot be used as a pivot to reach internal hosts.

func ValidateParameterSubset

func ValidateParameterSubset(granted, declared Capability) error

ValidateParameterSubset checks whether the declared (AIC capability) parameters fall within the granted (PA grant authorization boundary) parameter range. Scheme mismatch or grant has no parameters → no parameter-level rules, pass. Out-of-bounds returns a non-nil error.

func ValidatePrincipalUidKeyHash

func ValidatePrincipalUidKeyHash(pu PrincipalUid) error

ValidatePrincipalUidKeyHash validates that keyHash length matches the declared hashAlgo (v1.7.1 §5, spec P1-A-12). SHA-2/SHA-3 + SM3 (built-in pure Go implementation in types, C1) are validated against the mapping table for output length; algorithms requiring external dependencies like BLAKE2/BLAKE3 return a clear "unsupported" error.

func VerifyColdBackup

func VerifyColdBackup(inPath, password string) (string, error)

VerifyColdBackup reads a cold backup envelope, verifies its MAC, decrypts the key, and checks that the decrypted key matches the enclosed certificate's public key. It returns a human-readable summary on success.

func VerifyDelegationAuthorization

func VerifyDelegationAuthorization(userCert *x509.Certificate, aic *AICConfig, agentSPKI []byte) error

VerifyDelegationAuthorization verifies the DelegationAuthorization carried in an AICConfig against the DA signer's certificate:

  1. Reconstructs DelegationAuthTBS with the exact same field order the client signer and gateway-lib VerifyDelegationAuth use.
  2. Cross-validates the user certificate's SPKI hash against PrincipalUid.KeyHash (prevents forged user certs).
  3. Verifies the signature with the user certificate's public key (ECDSA/RSA/RSA-PSS/Ed25519), keyed off the declared algorithm OID.

agentSPKI is the DER encoding of the agent public key being certified (x509.MarshalPKIXPublicKey(sc.SubjectPubKey)).

Version negotiation:

  • aic.Version == 0: try v2 when agentSPKI is non-empty, then v1.
  • aic.Version == 1: only v1 (legacy DER).
  • aic.Version == 2: only v2 (agentSPKI must be non-empty).

On success aic.Version is updated to the actual version verified (audit trail). Both versions failing returns an aggregated error (fail-closed). A nil userCert or nil AIC is treated as invalid (fail-closed).

func VerifySCT

func VerifySCT(cert *x509.Certificate, sctVersion int, logID string, timestamp uint64, extensions string, sigDER []byte, logPubKey crypto.PublicKey) error

VerifySCT performs full RFC 6962 §3.2 signature verification of a v1 SCT against the CT log's public key. It reconstructs the exact TLS bytes the log signed over (SignedCertificateTimestamp → SCTSignedData: version | sig_type | timestamp | entry_type | 3-byte-length cert DER | 2-byte-length extensions) and verifies the DigitallySigned signature with the supplied log key.

The previous implementation only validated the TLS framing (algo bytes + length prefix) and never touched the log signature — any garbage SCT passed. Pass a nil logPubKey to skip the cryptographic check (callers that do this MUST treat the result as unverified).

Types

type AIC

type AIC struct {
	Version                  int                     `asn1:"default:1"`
	AgentId                  string                  `asn1:"utf8"`
	PrincipalUid             PrincipalUid            `asn1:""`
	Capabilities             []Capability            `asn1:"sequence"`
	DelegationMode           DelegationMode          `asn1:"default:0"`
	AuthorizationConstraints []Capability            `asn1:"optional,omitempty,contextspecific,explicit,tag:0"`
	DelegationAuthorization  DelegationAuthorization `asn1:"optional,omitempty"`
	Extensions               []ExtField              `asn1:"optional,omitempty,contextspecific,explicit,tag:1"`
}

AIC is the X.509v3 extension ASN.1 structure (v1.7.1 spec §AIC). ASN.1 field order: version, agentId, principalUid, capabilities, delegationMode, authorizationConstraints [0], delegationAuthorization, extensions [1]. delegationAuthorization is required by spec; Go encoding/asn1 cannot marshal "required but empty" DA (empty signature OID causes error), so the tag retains optional,omitempty; required semantics are enforced in ParseAIC/BuildAIC.

func ParseAIC

func ParseAIC(cert *x509.Certificate) (*AIC, error)

ParseAIC parses the AIC extension from a certificate. If the certificate does not contain an AIC extension, returns nil (not an error). Per v1.7.1 spec, delegationAuthorization is required; missing returns an error.

func (*AIC) CheckPermission

func (a *AIC) CheckPermission(required string) (bool, error)

CheckPermission checks whether an agent certificate has a specified permission (matched by CapabilityId).

func (*AIC) Config

func (a *AIC) Config() *AICConfig

Config reconstructs an AICConfig from a parsed AIC so that an AIC-bearing certificate can be re-issued with the same authorization constraints (L14: cmdReSign previously dropped the AIC when re-signing).

func (*AIC) HasProtocol

func (a *AIC) HasProtocol(schemeId string) bool

HasProtocol checks whether an agent has a protocol capability with the specified SchemeId.

func (*AIC) IntersectPermissions

func (a *AIC) IntersectPermissions(userPerms []string) []string

IntersectPermissions returns the intersection of CapabilityId list with user permissions.

func (*AIC) Principal

func (a *AIC) Principal() string

Principal returns the human principal that the Agent represents.

type AICConfig

type AICConfig struct {
	AgentId                  string
	PrincipalUid             PrincipalUid
	DelegationMode           DelegationMode
	Capabilities             []Capability
	AuthorizationConstraints []Capability
	DelegationAuthorization  *DelegationAuthorization
	Version                  int
}

AICConfig is the configuration needed to issue the AIC extension. BuildAIC uses required fields to construct the ASN.1 extension. Version is the DA / AIC envelope version:

  • 0 (unspecified): BuildAIC applies the current default — version 2 with agent SPKI binding (agentKeyBinding must be populated before calling).
  • 1: legacy v1 (no agentKeyBinding).
  • 2: explicit v2.
  • Any other value: BuildAIC rejects.

type AggregatorConfig

type AggregatorConfig struct {
	Window       time.Duration // Time window (default 200ms)
	BatchMax     int           // Maximum batch size (default 1000)
	AutoSwitchAt int           // Queue depth threshold for auto-switching to aggregation mode (default 50)
	BufferSize   int           // Pending issuance queue capacity (default 10000)
}

AggregatorConfig is the aggregator configuration.

func DefaultAggregatorConfig

func DefaultAggregatorConfig() AggregatorConfig

DefaultAggregatorConfig returns the default configuration.

type AggregatorError

type AggregatorError struct {
	// contains filtered or unexported fields
}

func (*AggregatorError) Error

func (e *AggregatorError) Error() string

type AggregatorReq

type AggregatorReq struct {
	CN      string
	Profile string
	KeyType string
	CAName  string
	SAN     string
	Result  chan *AggregatorResult // Result channel
}

AggregatorReq is a single issuance request in the aggregator.

type AggregatorResult

type AggregatorResult struct {
	Serial string
	Cert   *x509.Certificate
	Err    error
}

AggregatorResult is the issuance result.

type AlgorithmIdentifier

type AlgorithmIdentifier struct {
	Algorithm  asn1.ObjectIdentifier
	Parameters asn1.RawValue `asn1:"optional"`
}

AlgorithmIdentifier is the ASN.1 algorithm identifier (I-D §6 AlgorithmIdentifier).

type ArchivePolicy

type ArchivePolicy struct {
	Enabled        bool     `json:"enabled"`
	RetentionDays  int      `json:"retention_days"` // archive certs after N days from expiry/revocation
	ExcludeCAs     []string `json:"exclude_cas,omitempty"`
	IncludeCA      string   `json:"include_ca,omitempty"`
	ArchiveExpired bool     `json:"archive_expired"`
	ArchiveRevoked bool     `json:"archive_revoked"`
}

type ArchiveResult

type ArchiveResult struct {
	Archived     int `json:"archived"`
	ExpiredCount int `json:"expired_count"`
	RevokedCount int `json:"revoked_count"`
}

func ArchiveCerts

func ArchiveCerts(database *db.DB, policy *ArchivePolicy) (*ArchiveResult, error)

type AutoRenewCallback

type AutoRenewCallback func(caName, serial string, validityDays int) (newSerial string, err error)

type AutoRenewPolicy

type AutoRenewPolicy struct {
	Enabled         bool          `json:"enabled"`
	Interval        time.Duration `json:"interval"`
	WindowDays      int           `json:"window_days"`
	DefaultValidity int           `json:"default_validity_days"`
	Profiles        []string      `json:"profiles,omitempty"`    // only auto-renew these profiles (empty = all)
	ExcludeCAs      []string      `json:"exclude_cas,omitempty"` // skip these CAs
	NotifyOnly      bool          `json:"notify_only"`           // dry-run: notify but don't renew
	MaxRenewals     int           `json:"max_renewals"`          // max per run (0 = unlimited)
}

type AutoRenewResult

type AutoRenewResult struct {
	Serial      string `json:"serial"`
	CAName      string `json:"ca_name"`
	CommonName  string `json:"common_name"`
	OldNotAfter string `json:"old_not_after"`
	NewSerial   string `json:"new_serial,omitempty"`
	Action      string `json:"action"` // renewed, skipped, error, notify
	Error       string `json:"error,omitempty"`
}

func AutoRenew

func AutoRenew(database *db.DB, policy *AutoRenewPolicy, renewFn AutoRenewCallback, notifyFn func(event, caName, serial, cn, msg string)) []AutoRenewResult

type CRLConfig

type CRLConfig struct {
	DB              *db.DB
	CACert          *x509.Certificate
	CAKey           crypto.Signer
	CAName          string
	ValidityDays    int
	ThisUpdate      time.Time
	LastThisUpdate  time.Time // previous thisUpdate to guarantee monotonicity against clock rollback
	LastCRLNumber   *big.Int  // cRLNumber of the base CRL (for Delta CRL's Base CRL Number extension)
	Partition       int       // 0-based partition index. -1 means all (generate full CRL)
	TotalPartitions int       // total partition count. 0 or 1 = no partitioning

	// RevokedEntriesSource overrides the revoked-entries read path. When nil
	// (CLI/init-full) the DB is used directly. When set (serve layer with the
	// memory engine enabled) the engine is authoritative; revoked cross-cert
	// entries are always read from the DB as the engine does not index them.
	RevokedEntriesSource RevokedEntriesSource

	// NumberStore persists the last CRL number per CA (H12 fix). When nil the
	// in-memory counter is used and monotonicity is only guaranteed within a
	// single process lifetime.
	NumberStore CRLNumberStore
}

type CRLNumberStore

type CRLNumberStore interface {
	GetLastCRLNumber(caName string) (int64, error)
	SetLastCRLNumber(caName string, number int64) error
}

CRLNumberStore persists the last used CRL number per CA so the counter can be re-seeded at startup, preserving RFC 5280 §5.2.4 monotonicity across restarts (H12 fix). When set on CRLConfig, generateCRL raises the in-memory counter to the persisted value before incrementing and records the new number after each successful generation.

type Capability

type Capability struct {
	SchemeId     string `json:"scheme_id,omitempty" asn1:"utf8"`
	CapabilityId string `json:"capability_id,omitempty" asn1:"utf8"`
	Parameters   []byte `json:"parameters,omitempty" asn1:"optional,omitempty,contextspecific,explicit,tag:0"`
}

Capability is the protocol-level capability container — Varwof only defines the container structure, not specific semantics. schemeId identifies the capability scheme (e.g. "varwof-gateway-v1"), capabilityId is the specific capability under that scheme, parameters are encoded as declared by schemeId.

Encoding specification (consistent with types Capability.FullID()): capabilityId is a pure action identifier (without scheme prefix), full permission identifier = schemeId + ":" + capabilityId, uniformly generated by FullID(); all matching/intersection/authorization decisions must be based on the full identifier.

func (Capability) FullID

func (c Capability) FullID() string

FullID returns the full capability identifier (schemeId:capacityId), semantically identical to types Capability.FullID(). When schemeId is empty, it degrades to capabilityId (compatible with generic capabilities without a scheme).

type CertAggregator

type CertAggregator struct {
	// contains filtered or unexported fields
}

CertAggregator is the certificate issuance aggregator. Issues instantly under low load, automatically switches to time-window batch issuance under high load.

func NewCertAggregator

func NewCertAggregator(cfg AggregatorConfig, signer CertSigner) *CertAggregator

NewCertAggregator creates an aggregator and starts the background processing goroutine.

func (*CertAggregator) Close

func (a *CertAggregator) Close()

Close closes the aggregator and waits for all pending requests to complete.

func (*CertAggregator) Request

func (a *CertAggregator) Request(req *AggregatorReq) *AggregatorResult

Request submits an issuance request. Under low load, issues instantly and returns directly; under high load, enqueues for batch processing.

type CertBufferItem

type CertBufferItem struct {
	Record *db.CertRecord
	Key    crypto.Signer
}

CertBufferItem is a single record pending persistence in the memory buffer.

type CertSigner

type CertSigner interface {
	SignBatch(items []*AggregatorReq, caName string) []*AggregatorResult
}

CertSigner is the signing interface called by the aggregator.

type CertSource

type CertSource interface {
	// FindCA returns every configured CA certificate (ca_meta) whose Subject
	// matches the given issuer, plus the trust anchors. Implementations may
	// return nil slices for either group.
	FindIssuerCandidates(issuerRaw string) ([]*x509.Certificate, error)
	// TrustAnchors returns the set of trusted root certificates.
	TrustAnchors() ([]*x509.Certificate, error)
}

CertSource supplies certificates for path building. Both *db.DB and a test stub implement it.

type ChainNode

type ChainNode struct {
	Subject     string `json:"subject"`
	Fingerprint string `json:"fingerprint"`
	Issuer      string `json:"issuer"`
	Trusted     bool   `json:"trusted"`
	Source      string `json:"source,omitempty"`
}

type ColdBackupEnvelope

type ColdBackupEnvelope struct {
	Version       int    `json:"version"`
	CAName        string `json:"ca_name"`
	CreatedAt     string `json:"created_at"`
	CertPEM       string `json:"cert_pem"`
	EncryptedKey  string `json:"encrypted_key"`            // base64(PBES2 DER)
	CertSHA256    string `json:"cert_sha256"`              // hex
	KeyFP         string `json:"key_fingerprint"`          // hex SHA-256 of public key
	MAC           string `json:"mac"`                      // base64(HMAC-SHA256 over canonical fields)
	MACSalt       string `json:"mac_salt,omitempty"`       // base64(16B salt), M11+
	MACIterations int    `json:"mac_iterations,omitempty"` // PBKDF2 iterations, M11+
}

ColdBackupEnvelope is the on-disk format for an offline Root CA cold backup. The private key is encrypted with PBES2 (AES-256-CBC, see pbes2.go) and the whole envelope carries an HMAC so tampering or a wrong password is detected before any secret key material is processed.

type CreateConfig

type CreateConfig struct {
	DB                *db.DB
	Name              string
	SubjectName       string // optional, overrides Name as CN
	Profile           Profile
	Parent            *x509.Certificate
	ParentKey         crypto.Signer
	KeyType           string
	ReuseKey          crypto.Signer
	Validity          time.Duration
	CRLBaseURL        string
	PermittedDomains  []string
	ExcludedDomains   []string
	PermittedEmails   []string
	ExcludedEmails    []string
	PermittedURIs     []string
	ExcludedURIs      []string
	PermittedIPRanges []string
	ExcludedIPRanges  []string
	DefaultCountry    string
	DefaultOrg        string
	MaxPathLen        int // override profile default (0 = use profile default)
	OCSPURL           string
	IssuerURL         string
}

type CreateResult

type CreateResult struct {
	Cert      *x509.Certificate
	CertDER   []byte
	Signer    crypto.Signer
	SerialHex string
}

func CreateCA

func CreateCA(cfg *CreateConfig) (*CreateResult, error)

type DBSource

type DBSource struct {
	DB *db.DB
}

DBSource adapts *db.DB to CertSource.

func (*DBSource) FindIssuerCandidates

func (s *DBSource) FindIssuerCandidates(issuerRaw string) ([]*x509.Certificate, error)

func (*DBSource) TrustAnchors

func (s *DBSource) TrustAnchors() ([]*x509.Certificate, error)

type DelegationAuthorization

type DelegationAuthorization struct {
	Reason             Reason              `asn1:""`
	RequestedLifetime  int                 `asn1:"default:0"`
	Timestamp          time.Time           `asn1:"generalized"`
	Nonce              []byte              `asn1:"octet"` // 32-byte random number, anti-replay (REQUIRED)
	SignatureAlgorithm AlgorithmIdentifier `asn1:""`
	SignatureValue     []byte              `asn1:"octet"`
}

DelegationAuthorization is the cryptographic evidence of user authorization (v1.7.1 spec §DelegationAuthorization). Field order (consistent with types): reason → requestedLifetime → timestamp → nonce → signatureAlgorithm → signatureValue. reason is required (authorization must have a reason). User certificate is located via PrincipalUid.KeyHash (SPKI SHA-256), no longer storing UserCertHash separately.

type DelegationMode

type DelegationMode int

DelegationMode defines the Agent's delegation mode.

const (
	DelegationAuthorized     DelegationMode = 0 // Authorized delegation (Agent acts on behalf of user)
	DelegationRepresentative DelegationMode = 1 // Full representation (Agent fully represents user)
)

type DelegationModeEnum

type DelegationModeEnum int

DelegationModeEnum defines the delegation mode enumeration (draft-02 §8 DelegationModeEnum).

const (
	DelegationModeAuthorizedOnly        DelegationModeEnum = 0
	DelegationModeRepresentativeAllowed DelegationModeEnum = 1
)

type DelegationPolicy

type DelegationPolicy struct {
	Version         int `json:"version,omitempty" asn1:"default:1"`
	MaxAgents       int `json:"max_agents,omitempty" asn1:"default:1"`
	AllowedMode     int `json:"allowed_mode,omitempty" asn1:"enum,default:0"`
	MaxSessionHours int `json:"max_session_hours,omitempty" asn1:"optional,omitempty,explicit,tag:0"`
}

DelegationPolicy controls how a principal delegates permissions to an Agent (v1.7.1 spec §DelegationPolicy).

type DeltaCRLConfig

type DeltaCRLConfig struct {
	// Since is the base CRL thisUpdate; only certificates revoked at or after
	// this time are included in the delta.
	Since time.Time
	// BaseCRLNumber is the cRLNumber of the base CRL the delta applies to
	// (encoded in the Base CRL Number extension, 2.5.29.31).
	BaseCRLNumber *big.Int
}

DeltaCRLConfig carries Delta CRL parameters.

type ExtField

type ExtField struct {
	ExtnID    asn1.ObjectIdentifier `asn1:"objectidentifier"`
	Critical  bool                  `asn1:"default:false"`
	ExtnValue []byte                `asn1:"octet"`
}

ExtField is a single extension field in the AIC internal extension slot. Vendors can use their own IANA PEN OIDs, users can use the Varwof-reserved .1.1.5.x space.

type IdentitySource

type IdentitySource interface {
	Lookup(ctx context.Context, source, username string) (*UserIdentity, error)
}

IdentitySource resolves a person's identity attributes from a directory service (LDAP/AD via bridge-ldap, or OAuth/OIDC via bridge-oauth) for automated certificate issuance.

Lookup resolves the identity for a username. The caller supplies the source_tag to route to a specific bridge backend; empty tries all backends in bridge configuration order.

func NewIdentitySource

func NewIdentitySource(cfg *IdentitySourceConfig) (IdentitySource, error)

NewIdentitySource builds an IdentitySource from config. It returns an error for an unknown type or an empty bridge URL.

type IdentitySourceConfig

type IdentitySourceConfig struct {
	// Type is the identity source kind: "ldap" (bridge-ldap) or "oauth" (bridge-oauth). Default "ldap".
	Type IdentitySourceType `json:"type,omitempty"`
	// SourceURL is the base URL of the identity bridge service, e.g. "http://127.0.0.1:8082".
	SourceURL string `json:"source_url,omitempty"`
	// Token is the bridge management API bearer token. Empty = no auth.
	Token string `json:"token,omitempty"`
	// Source is the default source_tag used when a request does not specify one.
	Source string `json:"source,omitempty"`
	// Username is the automation account username (oauth type only, resource-owner grant).
	Username string `json:"username,omitempty"`
	// Password is the automation account password (oauth type only, resource-owner grant).
	Password string `json:"password,omitempty"`
	// TimeoutSec bounds each upstream bridge request. Default 10.
	TimeoutSec int `json:"timeout_sec,omitempty"`
	// OUFromGroups maps identity-source groups to certificate OU values
	// (RBAC roles). Matching is exact OR case-insensitive suffix containment
	// of the OU-portion of an LDAP group DN. When no rule matches, DefaultOU
	// (or the source dept) is used.
	OUFromGroups map[string]string `json:"ou_from_groups,omitempty"`
	// DefaultOU is the OU written when no OUFromGroups rule matches and the
	// source carries no dept. Empty = omit OU.
	DefaultOU string `json:"default_ou,omitempty"`
	// DisabledOK allows issuing identity-user certs for disabled accounts.
	// Default false: disabled accounts are rejected (fail-closed).
	DisabledOK bool `json:"disabled_ok,omitempty"`
}

IdentitySourceConfig configures the remote identity source used for the identity-user profile. See docs/api.md for the full field reference.

func (*IdentitySourceConfig) CertificateOUS

func (c *IdentitySourceConfig) CertificateOUS(id *UserIdentity) []string

CertificateOUS from identity attributes applies the OUFromGroups mapping, falling back to DefaultOU or the source dept. Returns the OU list to embed in the certificate subject.

type IdentitySourceType

type IdentitySourceType string

IdentitySourceType enumerates supported identity-source kinds.

const (
	// IdentitySourceLDAP resolves persons via bridge-ldap's POST /api/v1/lookup.
	IdentitySourceLDAP IdentitySourceType = "ldap"
	// IdentitySourceOAuth resolves persons via bridge-oauth's password grant +
	// userinfo flow (service account). The configured username/password belong
	// to an automation account authorized in the IdP.
	IdentitySourceOAuth IdentitySourceType = "oauth"
)

type ImportTrustResult

type ImportTrustResult struct {
	Imported int
	Skipped  int
	Total    int
}

func ImportTrustBundle

func ImportTrustBundle(database *db.DB, pemData []byte, source string) (*ImportTrustResult, error)

type IssueResult

type IssueResult struct {
	Cert   *x509.Certificate
	Record *db.CertRecord
	Key    crypto.Signer
}

IssueResult is the result of a single certificate issuance.

func IssueAndBuffer

func IssueAndBuffer(signerCfg *SignConfig, buffer *MemoryBuffer) (*IssueResult, error)

IssueAndBuffer issues a certificate and adds it to the memory buffer.

type JWK

type JWK = aicjwt.JWK

JWK is the RFC 7517 JSON Web Key (kty/crv/x/y/n/e + kid/use/x5c/x5t).

func CertToJWK

func CertToJWK(cert *x509.Certificate) (JWK, error)

CertToJWK converts an X.509 certificate into a JWK bound to that certificate (kid = SPKI hash, x5c = the certificate, x5t = thumbprint).

type JWKS

type JWKS = aicjwt.JWKS

JWKS is a JWK Key Set (RFC 7517 §5).

func BuildJWKS

func BuildJWKS(certs []*x509.Certificate) (JWKS, error)

BuildJWKS builds a JWKS from certificates, deduplicated by kid.

type JWTSignOptions

type JWTSignOptions struct {
	// Audience is the aud claim (REQUIRED by draft). Empty → defaults to
	// "varwof-core" (the core issuer's own audience).
	Audience []string
	// Issuer overrides the iss claim. Empty → "varwof-core".
	Issuer string
	// TokenType overrides the default typ (aic+jwt). Kept for extensibility.
	TokenType string
	// Scope is an optional OAuth scope string mirroring the AIC capabilities
	// (informational; capabilities remain the authority).
	Scope string
	// StatusListURI, when set, adds a status claim (RFC 9457 status list
	// reference) pointing to the core revocation status list endpoint.
	StatusListURI string
	// StatusListIndex is the index within the status list.
	StatusListIndex int
	// DA is an optional inner DA JWS (typ aic+da+jwt, signed by the principal
	// key). Authorized mode omits it (lifetime-bounded lightweight profile);
	// representative mode REQUIRES it. When set, it is embedded in the outer
	// "da" claim.
	DA string
}

JWTSignOptions controls AIC-JWT issuance on top of SignConfig.

type Job

type Job struct {
	ID        string          `json:"id"`
	Status    JobStatus       `json:"status"`
	Progress  int             `json:"progress"` // completed count
	Total     int             `json:"total"`    // total count
	CreatedAt time.Time       `json:"created_at"`
	Error     string          `json:"error,omitempty"`
	Results   []JobResultItem `json:"results,omitempty"`
	// contains filtered or unexported fields
}

Job stores the complete state of an async job.

type JobProcessor

type JobProcessor interface {
	Process(items []JobRequestItem) []JobResultItem
}

JobProcessor is the actual issuance interface.

type JobQueue

type JobQueue struct {
	// contains filtered or unexported fields
}

JobQueue is an async job queue.

func NewJobQueue

func NewJobQueue(workers int, ttl time.Duration, processor JobProcessor) *JobQueue

NewJobQueue creates an async job queue and starts background workers.

func (*JobQueue) Close

func (q *JobQueue) Close()

Close closes the queue.

func (*JobQueue) GetJob

func (q *JobQueue) GetJob(id string) *Job

GetJob queries job status.

func (*JobQueue) Submit

func (q *JobQueue) Submit(items []JobRequestItem) string

Submit submits an async issuance job and returns the job ID.

type JobRequest

type JobRequest struct {
	Items []JobRequestItem `json:"requests"`
}

JobRequest represents a single async issuance job.

type JobRequestItem

type JobRequestItem struct {
	CN      string `json:"cn"`
	SAN     string `json:"san,omitempty"`
	Profile string `json:"profile,omitempty"`
	KeyType string `json:"key_type,omitempty"`
	CA      string `json:"ca,omitempty"`
}

JobRequestItem represents a single certificate issuance request.

type JobResultItem

type JobResultItem struct {
	Serial string `json:"serial,omitempty"`
	CN     string `json:"cn"`
	Status string `json:"status"`
	Error  string `json:"error,omitempty"`
}

JobResultItem represents a single certificate issuance result.

type JobStatus

type JobStatus string

JobStatus represents the status of an async job.

const (
	JobPending    JobStatus = "pending"
	JobProcessing JobStatus = "processing"
	JobDone       JobStatus = "done"
	JobFailed     JobStatus = "failed"
)

type LDAPConfig

type LDAPConfig struct {
	URL          string `json:"url,omitempty"`
	BindDN       string `json:"bind_dn,omitempty"`
	BindPassword string `json:"bind_password,omitempty"`
	BaseDN       string `json:"base_dn,omitempty"`
	Filter       string `json:"filter,omitempty"`
	UIDAttr      string `json:"uid_attr,omitempty"`
	MapCN        string `json:"map_cn,omitempty"`
	MapOrg       string `json:"map_o,omitempty"`
	MapOU        string `json:"map_ou,omitempty"`
	MapL         string `json:"map_l,omitempty"`
	MapST        string `json:"map_st,omitempty"`
	MapC         string `json:"map_c,omitempty"`
	MapEmail     string `json:"map_email,omitempty"`
}

type LDAPLookup

type LDAPLookup interface {
	Lookup(cfg *LDAPConfig, username string) (*pkix.Name, error)
	CheckMembership(cfg *LDAPConfig, username, groupDN string) (bool, error)
}

LDAPLookup is the interface for directory service lookups. The default implementation uses go-ldap directly; a satellite (ldap-bridge) can provide a remote implementation over HTTP.

func NewDefaultLDAPLookup

func NewDefaultLDAPLookup() LDAPLookup

type MemoryBuffer

type MemoryBuffer struct {
	// contains filtered or unexported fields
}

MemoryBuffer is a high-speed memory buffer for certificate issuance. It accumulates certificate records pending persistence and triggers batch writes to the external DB periodically or on count threshold.

func NewMemoryBuffer

func NewMemoryBuffer(extDB *db.DB, cfg PersistConfig) (*MemoryBuffer, error)

NewMemoryBuffer creates a memory buffer and starts the background persistence goroutine.

func (*MemoryBuffer) Add

func (b *MemoryBuffer) Add(item *CertBufferItem) error

Add adds a certificate record to the buffer. In realtime mode, writes directly to the external DB; in batch/async mode, writes to the in-memory queue.

func (*MemoryBuffer) Close

func (b *MemoryBuffer) Close() error

Close closes the buffer, flushes all pending data, and releases resources.

func (*MemoryBuffer) Flush

func (b *MemoryBuffer) Flush() error

Flush batch writes all pending persistence records from the buffer to the external DB.

func (*MemoryBuffer) Size

func (b *MemoryBuffer) Size() int

Size returns the number of records currently pending persistence.

type NameConstraints

type NameConstraints struct {
	PermittedDomains  []string
	ExcludedDomains   []string
	PermittedEmails   []string
	ExcludedEmails    []string
	PermittedURIs     []string
	ExcludedURIs      []string
	PermittedIPRanges []string
	ExcludedIPRanges  []string
}

type NonceStorer

type NonceStorer interface {
	StoreNonce(nonce []byte) error
	ConsumeNonce(nonce []byte) error
}

NonceStorer abstracts nonce persistence for one-time-use enforcement. Implemented by db.DB via StoreNonce/ConsumeNonce.

type OfflineSignConfig

type OfflineSignConfig struct {
	CACert   *x509.Certificate
	CAKey    crypto.Signer
	CSR      *x509.CertificateRequest
	Validity time.Duration
	Hash     string // sha256, sha384, sha512
	PathLen  int    // 0 = sub-ca, 1+ = intermediate, -1 = no constraint
}

type PathVerificationResult

type PathVerificationResult struct {
	// Valid is true when every x509-level check (signature, validity, KU,
	// basic constraints) passed.
	Valid bool `json:"valid"`
	// Chain is the ordered path leaf → root (length ≥ 1).
	Chain []*x509.Certificate `json:"-"`
	// RootIsTrusted is true when the path terminates in a trust anchor.
	RootIsTrusted bool `json:"root_is_trusted"`
	// Policy is the policy-processing outcome (nil when Valid is false or no
	// policy processing was requested).
	Policy *PolicyDecision `json:"policy,omitempty"`
	// RejectReason describes why the path was rejected (empty when Valid).
	RejectReason string `json:"reject_reason,omitempty"`
}

PathVerificationResult is the full output of VerifyPath.

func VerifyPath

func VerifyPath(chain []*x509.Certificate, src CertSource, opts VerifyPathOptions) (*PathVerificationResult, error)

VerifyPath verifies a pre-built path (or builds one when src is non-nil):

  1. signature and validity on every hop (x509-level)
  2. trust anchor termination
  3. optional RFC 5280 §6.1 policy processing

When src is nil and chain is provided, only signature/validity + policy checks are run (no trust-anchor check beyond the caller's responsibility).

type PersistConfig

type PersistConfig struct {
	Mode          PersistMode
	BatchSize     int           // Batch trigger count (default 100)
	BatchInterval time.Duration // Timer trigger interval (default 5 seconds)
	QueueSize     int           // Queue capacity limit (default 10000)
}

PersistConfig is the runtime persistence configuration.

func DefaultPersistConfig

func DefaultPersistConfig() PersistConfig

DefaultPersistConfig returns the default persistence configuration (realtime mode, compatible with existing behavior).

type PersistMode

type PersistMode int

PersistMode defines the persistence mode after certificate issuance.

const (
	PersistRealtime PersistMode = iota // Realtime persistence: write to external DB immediately after each issuance
	PersistBatch                       // Batch persistence: accumulate N records then batch write
	PersistAsync                       // Async persistence: write only to memory buffer, background async persistence
)

func ParsePersistConfig

func ParsePersistConfig(mode string) PersistMode

ParsePersistConfig parses the persistence mode from a configuration string.

type Policy

type Policy struct {
	Rules []PolicyRule `json:"rules"`
}

func LoadPolicy

func LoadPolicy(path string) (*Policy, error)

type PolicyDecision

type PolicyDecision struct {
	// AcceptedUserPolicies is the non-empty intersection of the final policy
	// set and the requester's user-initial-policy-set. When the requester
	// accepted anyPolicy (empty user set), AcceptedUserPolicies lists the
	// valid policies actually matched.
	AcceptedUserPolicies []string
	// MappedPolicies lists the effective policy mappings applied along the
	// path (issuerDomainPolicy → subjectDomainPolicy at each mapping CA).
	MappedPolicies []PolicyMappingApplied
	// InhibitAnyPolicyHit is true when anyPolicy was suppressed by an
	// inhibitAnyPolicy constraint along the path.
	InhibitAnyPolicyHit bool
	// ExplicitPolicyRequired is true when a requireExplicitPolicy constraint
	// was in force at the leaf.
	ExplicitPolicyRequired bool
	// ValidPolicyTree describes, per depth, the valid policies at that
	// certificate. Depth 0 is the leaf.
	ValidPolicyTree []ValidPolicyNode
}

PolicyDecision is the outcome of RFC 5280 §6.1 policy processing over a verified chain.

func EvaluatePolicy

func EvaluatePolicy(chain []*x509.Certificate, opts VerifyPathOptions) (*PolicyDecision, error)

EvaluatePolicy performs RFC 5280 §6.1 policy processing over an already structurally-valid path (chain[0] = leaf, ascending). It returns the policy decision, or an error when the path is malformed.

The walk implements, for each certificate depth i (leaf = 0):

  • decrement anyPolicy/explicitPolicy/inhibitAnyPolicy counters
  • apply policy mappings (2.5.29.33) of intermediate CAs
  • intersect/accumulate the valid policy set
  • honor requireExplicitPolicy, inhibitPolicyMapping, inhibitAnyPolicy

The final valid policy set is intersected with the user-initial-policy-set (empty = accept any). A non-empty intersection means the policy is accepted.

type PolicyMapping

type PolicyMapping struct {
	IssuerDomainPolicy  string // Issuer domain policy OID (e.g. "2.5.29.32.0")
	SubjectDomainPolicy string // Subject domain policy OID
}

PolicyMapping represents a single mapping in the RFC 5280 §4.2.1.5 Policy Mappings extension: issuerDomainPolicy maps to subjectDomainPolicy. Allowed only in CA certificates.

func ParsePolicyMapping

func ParsePolicyMapping(s string) (PolicyMapping, error)

ParsePolicyMapping parses an "issuerDomainPolicy:subjectDomainPolicy" string into a PolicyMapping (for reuse by API/CLI layers).

func ParsePolicyMappings

func ParsePolicyMappings(strs []string) ([]PolicyMapping, error)

ParsePolicyMappings batch-parses a list of strings ("issuer:subject" format) into []PolicyMapping; any invalid item causes the entire operation to fail.

type PolicyMappingApplied

type PolicyMappingApplied struct {
	Depth               int    `json:"depth"`
	IssuerDomainPolicy  string `json:"issuer_domain_policy"`
	SubjectDomainPolicy string `json:"subject_domain_policy"`
}

type PolicyRule

type PolicyRule struct {
	Comment         string   `json:"comment,omitempty"`
	AllowedCNs      []string `json:"allowed_cns,omitempty"`
	DeniedCNs       []string `json:"denied_cns,omitempty"`
	AllowedSANs     []string `json:"allowed_sans,omitempty"`
	DeniedSANs      []string `json:"denied_sans,omitempty"`
	AllowedProfiles []string `json:"allowed_profiles,omitempty"`
	AllowedKeyTypes []string `json:"allowed_key_types,omitempty"`
	AllowedCAs      []string `json:"allowed_cas,omitempty"`
	MaxValidityDays int      `json:"max_validity_days,omitempty"`
}

type PrincipalAuthorization

type PrincipalAuthorization struct {
	Version                  int              `asn1:"default:1"`
	Grants                   []Capability     `asn1:"optional,omitempty"`
	AuthorizationConstraints []Capability     `asn1:"optional,omitempty,contextspecific,explicit,tag:0"`
	DelegationPolicy         DelegationPolicy `asn1:"optional,explicit,tag:1"`
	Extensions               []ExtField       `asn1:"optional,omitempty,contextspecific,explicit,tag:2"`
}

PrincipalAuthorization corresponds to the subject authorization statement in X.509v3 extensions (OID 1.3.6.1.4.1.66257.1.2). ASN.1 (v1.7.1): version, grants, authorizationConstraints [0], delegationPolicy [1], extensions [2].

func ParsePrincipalAuthorizationExtension

func ParsePrincipalAuthorizationExtension(exts []pkix.Extension) (*PrincipalAuthorization, error)

ParsePrincipalAuthorizationExtension parses the PrincipalAuthorization extension from the certificate extension list.

func (*PrincipalAuthorization) AllowsRepresentative

func (pa *PrincipalAuthorization) AllowsRepresentative() bool

AllowsRepresentative checks whether the PrincipalAuthorization allows representative delegation.

func (*PrincipalAuthorization) GrantIds

func (pa *PrincipalAuthorization) GrantIds() []string

GrantIds returns the full permission identifiers (scheme:capabilityId) for all Grants. Aligned with types Capability.FullID() spec: matching/authorization decisions use the full identifier.

func (*PrincipalAuthorization) PermIds

func (pa *PrincipalAuthorization) PermIds() []string

PermIds is an alias for GrantIds, maintaining backward compatibility (legacy API callers).

type PrincipalAuthorizationConfig

type PrincipalAuthorizationConfig struct {
	Grants []Capability `json:"grants,omitempty"`
	// AuthorizationConstraints is the principal's operational boundary — the
	// time window / network / parameter limits that every delegation from this
	// principal inherits.  It is set when the *user* certificate is issued and
	// is the source of truth the DA and AIC copy from; an agent can only
	// narrow it, never widen it.
	AuthorizationConstraints []Capability      `json:"authorization_constraints,omitempty"`
	DelegationPolicy         *DelegationPolicy `json:"delegation_policy,omitempty"`
}

PrincipalAuthorizationConfig is the configuration needed to issue a PrincipalAuthorization extension.

type PrincipalUid

type PrincipalUid struct {
	Version    int                 `asn1:"default:1"`
	Realm      string              `asn1:"utf8"`
	Identifier string              `asn1:"utf8"`
	KeyHash    []byte              `asn1:"octet"`
	HashAlgo   AlgorithmIdentifier `asn1:"optional,omitempty,explicit,tag:0"`
}

PrincipalUid is the structured ASN.1 type for principal identity (spec §3.2). ASN.1 SEQUENCE: { version INTEGER { v1(1) }, realm UTF8String, identifier UTF8String,

keyHash OCTET STRING (SIZE(1..64)), hashAlgo [0] EXPLICIT AlgorithmIdentifier OPTIONAL }

keyHash = hashAlgo(SPKI); hashAlgo defaults to SHA-256 when omitted. Communication format: {realm}:{identifier}:{keyFingerprint} (keyFingerprint is base64url encoded)

func MakePrincipalUidFromCert

func MakePrincipalUidFromCert(realm, identifier string, cert *x509.Certificate) PrincipalUid

MakePrincipalUidFromCert constructs a PrincipalUid from a certificate (KeyHash = SPKI SHA-256, used for CA issuance).

func ParsePrincipalUid

func ParsePrincipalUid(s string) (PrincipalUid, error)

ParsePrincipalUid parses a communication-format PrincipalUid string.

func (PrincipalUid) HashAlgoOID

func (pu PrincipalUid) HashAlgoOID() asn1.ObjectIdentifier

HashAlgoOID returns the actually effective hash algorithm OID (empty → default SHA-256).

func (PrincipalUid) String

func (pu PrincipalUid) String() string

String returns the communication format {realm}:{identifier}:{keyFingerprint} (spec §3.2).

type Profile

type Profile string
const (
	ProfileRootCA      Profile = "root-ca"
	ProfileSubCA       Profile = "sub-ca"
	ProfilePolicyCA    Profile = "policy-ca"
	ProfileTLSServer   Profile = "tls-server"
	ProfileTLSClient   Profile = "tls-client"
	ProfileOCSPSigner  Profile = "ocsp-signer"
	ProfileTimestamp   Profile = "timestamp"
	ProfileCodeSigning Profile = "codesigning"
	ProfileEmail       Profile = "email"
	ProfileDocument    Profile = "document"
	ProfileAgentProxy  Profile = "agent-proxy"
	ProfileCMP         Profile = "cmp"

	// ProfileIdentityUser is a person's base identity certificate issued
	// automatically from a directory/identity source (bridge-ldap lookup or
	// bridge-oauth userinfo). CN/OU/email are filled from the identity source;
	// used by the serve layer with IdentitySourceConfig.
	ProfileIdentityUser Profile = "identity-user"

	// VPN certificate templates: used for mTLS-based VPN channels such as WireGuard/OpenVPN.
	// vpn-client is ClientAuth only (client certificate); vpn-server includes ServerAuth+ClientAuth
	// (server certificate, also allows client-side verification for mutual authentication).
	ProfileVPNClient Profile = "vpn-client"
	ProfileVPNServer Profile = "vpn-server"

	// Management certificate templates (m-*): built-in PKI management role certificate presets.
	// Automatically sets OU + ClientAuth EKU + DigitalSignature KU;
	// at issuance, only profile and CN need to be specified, no need to manually pass OU.
	ProfileMSuperAdmin Profile = "m-superadmin"
	ProfileMAdmin      Profile = "m-admin"
	ProfileMOperator   Profile = "m-operator"
	ProfileMRevoker    Profile = "m-revoker"
	ProfileMAuditor    Profile = "m-auditor"
	ProfileMReadonly   Profile = "m-readonly"
	ProfileMConsole    Profile = "m-console"
	ProfileMAutoRenew  Profile = "m-auto-renew"
	ProfileMReporter   Profile = "m-reporter"
)

type Reason

type Reason struct {
	ReasonCode  string `asn1:"utf8"`
	Description string `asn1:"utf8"`
}

Reason is the reason description for delegation authorization (v1.7.1 spec §Reason), for audit/display only, not participating in permission decisions. Both fields are required: reasonCode 1..64, description 1..512.

type RenewalToken

type RenewalToken struct {
	Version        int          `asn1:"default:1"`
	PrincipalUid   PrincipalUid `asn1:"optional,contextspecific,tag:0"`
	OldCertSerial  []byte       `asn1:"octet"`
	NewKeyHash     []byte       `asn1:"octet"`
	Timestamp      time.Time    `asn1:"generalized"`
	Nonce          []byte       `asn1:"octet"` // SIZE(16)
	ValidityPeriod int          `asn1:"default:300"`
}

RenewalToken is the ASN.1 structure for automatic renewal tokens (I-D §6). 7 fields: version, principalUid, oldCertSerial, newKeyHash, timestamp, nonce, validityPeriod.

type RenewalTokenExt

type RenewalTokenExt struct {
	Version        int          `asn1:"default:1"`
	PrincipalUid   PrincipalUid `asn1:"optional,contextspecific,tag:0"`
	OldCertSerial  []byte       `asn1:"octet"`
	NewKeyHash     []byte       `asn1:"octet"`
	Timestamp      time.Time    `asn1:"generalized"`
	Nonce          []byte       `asn1:"octet"` // SIZE(16)
	ValidityPeriod int          `asn1:"default:300"`
}

RenewalTokenExt is the ASN.1 serialization structure for RenewalToken (used for DER encoding). Identical to RenewalToken (no CertificateIssuerName or other contextual fields).

func ParseRenewalToken

func ParseRenewalToken(cert *pkix.Extension) (*RenewalTokenExt, error)

ParseRenewalToken parses a RenewalToken from a certificate extension.

func (*RenewalTokenExt) IsExpired

func (r *RenewalTokenExt) IsExpired() bool

IsExpired checks whether the RenewalToken has expired (5-minute validity period).

func (*RenewalTokenExt) ValidateConstraints

func (r *RenewalTokenExt) ValidateConstraints() error

ValidateConstraints validates RenewalToken constraints (spec §6).

func (*RenewalTokenExt) VerifyNonce

func (r *RenewalTokenExt) VerifyNonce() bool

VerifyNonce verifies that the nonce length is 16 bytes (spec §6: SIZE(16)).

type RevokedEntriesSinceSource

type RevokedEntriesSinceSource interface {
	GetRevokedCertEntriesSince(caName string, since time.Time) ([]*db.RevokedCertEntry, error)
}

RevokedEntriesSinceSource is implemented by sources that support Delta CRL queries (revoked at/after a base thisUpdate). Both *db.DB and *engine.Engine satisfy it.

type RevokedEntriesSource

type RevokedEntriesSource interface {
	GetRevokedCertEntries(caName string) ([]*db.RevokedCertEntry, error)
}

RevokedEntriesSource supplies the revoked-certificate entries a CRL is built from. Both *db.DB and *engine.Engine satisfy it; when the resident memory engine is enabled CRL generation should prefer it (memory-authoritative reads, zero SQL, immediate visibility of revocations), falling back to the DB when the engine is not configured.

type RotatingSigner

type RotatingSigner struct {
	// contains filtered or unexported fields
}

RotatingSigner provides atomic hot-swap of the active CA signing key with a legacy key retained during the transition window.

func NewRotatingSigner

func NewRotatingSigner(cert *x509.Certificate, key crypto.Signer) *RotatingSigner

NewRotatingSigner wraps a signer key as the initial active key.

func (*RotatingSigner) Active

func (rs *RotatingSigner) Active() *SignerKey

Active returns the current active cert+key, or nil.

func (*RotatingSigner) Cert

func (rs *RotatingSigner) Cert() *x509.Certificate

Cert returns the active certificate. Safe to call on nil receiver (nil).

func (*RotatingSigner) ClearLegacy

func (rs *RotatingSigner) ClearLegacy()

ClearLegacy drops the retained legacy key, ending the transition window.

func (*RotatingSigner) Key

func (rs *RotatingSigner) Key() crypto.Signer

Key returns the active signing key.

func (*RotatingSigner) Legacy

func (rs *RotatingSigner) Legacy() *SignerKey

Legacy returns the retained legacy cert+key during the transition window, or nil.

func (*RotatingSigner) NeedsRotation

func (rs *RotatingSigner) NeedsRotation(window time.Duration) bool

NeedsRotation reports whether the active CA certificate expires within the window (or is already expired).

func (*RotatingSigner) NotAfter

func (rs *RotatingSigner) NotAfter() time.Time

NotAfter returns the active certificate's NotAfter (zero time if none).

func (*RotatingSigner) Public

func (rs *RotatingSigner) Public() crypto.PublicKey

Public implements crypto.Signer.

func (*RotatingSigner) Rotate

func (rs *RotatingSigner) Rotate(cert *x509.Certificate, key crypto.Signer)

Rotate is an alias for Store with clearer semantics: the previous active key becomes legacy, the new one becomes active.

func (*RotatingSigner) Sign

func (rs *RotatingSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error)

Sign implements crypto.Signer, delegating to the active key.

func (*RotatingSigner) Store

func (rs *RotatingSigner) Store(cert *x509.Certificate, key crypto.Signer)

Store atomically installs a new active cert+key, promoting the previous active key to legacy (dual-signing transition window).

type SignCSRCallback

type SignCSRCallback func(csrDER []byte, cn, profile, caName string) (serial string, certDER []byte, err error)

type SignConfig

type SignConfig struct {
	DB                *db.DB
	SkipDB            bool // true = issue only, no DB write (high-throughput batch mode, caller responsible for persistence)
	CAKey             crypto.Signer
	CACert            *x509.Certificate
	CAName            string
	SubjectPubKey     any
	Profile           Profile
	CommonName        string
	Subject           *pkix.Name
	SANs              []string
	CAScope           []string
	KeyType           string
	Hash              string
	CRLBaseURL        string
	OCSPURL           string
	IssuerURL         string
	Validity          time.Duration
	DefaultCountry    string
	DefaultOrg        string
	IssuerAltNames    []string
	SubjectInfoAccess []string
	PolicyOIDs        []string
	// PolicyMappings is the RFC 5280 §4.2.1.5 Policy Mappings extension,
	// each item maps issuerDomainPolicy to subjectDomainPolicy, allowed only in CA certificates
	// (enterprise bridge / cross-domain trust scenarios).
	PolicyMappings []PolicyMapping
	// RequireExplicitPolicy is the RFC 5280 §4.2.1.11 Policy Constraints
	// requireExplicitPolicy (skipCerts count). nil means this field is not generated.
	RequireExplicitPolicy *int
	// InhibitPolicyMapping is the RFC 5280 §4.2.1.11 Policy Constraints
	// inhibitPolicyMapping (skipCerts count). nil means this field is not generated.
	InhibitPolicyMapping *int
	// InhibitAnyPolicy is the RFC 5280 §4.2.1.14 Inhibit anyPolicy extension's skipCerts
	// count (0 means anyPolicy is prohibited on this certificate chain). nil means this extension is not generated.
	InhibitAnyPolicy  *int
	DedupCN           bool
	MaxPathLen        int
	ExtraEKUOIDs      []string
	MustStaple        bool
	CRLPartitions     int
	NotBefore         *time.Time
	NotAfter          *time.Time
	PermittedDomains  []string
	ExcludedDomains   []string
	PermittedEmails   []string
	ExcludedEmails    []string
	PermittedURIs     []string
	ExcludedURIs      []string
	PermittedIPRanges []string
	ExcludedIPRanges  []string
	Policy            *Policy
	PolicyFile        string
	// RequirePolicy (M4 fix): when true and no issuance policy is loaded, Sign()
	// rejects issuance instead of warn-and-continue. The serve layer derives it
	// from config enforce_policy. Library consumers may set it explicitly.
	RequirePolicy bool
	// RequireTLSServerSAN (F11 fix): when true, a TLS-server certificate with
	// no subjectAltName (RFC 5280 §4.1.2.6 / RFC 6125) is rejected at issuance
	// instead of warn-and-continue. Mirrors RequirePolicy so strict deployments
	// can opt in without breaking existing CN-only issuance.
	RequireTLSServerSAN bool
	AIC                 *AICConfig // Agent Identity Certificate configuration
	// SPIFFEID, when set, is placed in the certificate SAN URIs (RFC 7555
	// spiffe:// URI) as the workload's SPIFFE identity. It is kept distinct from
	// AIC.AgentId so the AIC extension can carry the plain agent name (matching
	// the DA-authorized identity) while the SPIFFE ID lives only in the SAN —
	// this is what the validating side (gateway) actually enforces on.
	SPIFFEID               string
	PrincipalAuthorization *PrincipalAuthorizationConfig // user authorization
	Scope                  string                        // admin scope: which CAs this admin can manage (comma-separated)
	// MaxAgentProxyValidity is the hard upper bound for agent-proxy (authorized mode)
	// certificate validity. 0 uses DefaultAgentProxyMaxValidity (1h); spec P1-B-09/25 and
	// P2-A-04's "authorized certificate validity ≤24h" semantics — default 1h, can be relaxed
	// up to ≤24h via configuration.
	MaxAgentProxyValidity time.Duration

	// ValidateCapabilities is an optional AIC capability registration validation hook.
	// Injected by the upper layer (serve layer): passes the full identifiers of capabilities
	// declared in the AIC ("scheme:capability_id" list) to the register capability registry
	// for validation; returns an error if issuance should fail. nil means skip this validation
	// (capability registration validation not enabled, backward compatible).
	ValidateCapabilities func(caps []string) error

	// UserCert is the DA signer (user) certificate whose SPKI hash must match
	// AIC.PrincipalUid.KeyHash. When set (and AIC is non-nil), Sign() verifies
	// the DelegationAuthorization signature at the library layer (M5 fix) — so
	// library consumers cannot mint AIC certificates carrying unverified DA
	// evidence.
	UserCert *x509.Certificate

	// DelegationAlreadyVerified asserts that the caller has already verified
	// the DelegationAuthorization signature by other means. It is required when
	// AIC carries a DelegationAuthorization but UserCert is nil: without either,
	// Sign() refuses rather than minting a certificate whose delegation was
	// never verified. Set it only where verification genuinely happened
	// elsewhere (the serve /api/aic/issue path verifies the client's signed
	// message itself) or where the DA carries no principal signature to verify
	// (OAuth re-issuance, lab fixtures).
	DelegationAlreadyVerified bool

	// ConsumeDANonce is an optional anti-replay hook called once with the DA nonce
	// after DA signature verification passes. Injected by the serve layer (engine or
	// DB store); library consumers that need replay protection may supply their own.
	// nil skips nonce consumption (no persistence → replayable).
	ConsumeDANonce func(nonce []byte) error
}

func (*SignConfig) MaxAgentProxyValidityLimit

func (sc *SignConfig) MaxAgentProxyValidityLimit() time.Duration

MaxAgentProxyValidityLimit returns the effective agent-proxy validity upper bound. M2 fix: any positive configured value is capped at the spec ceiling (24h); 0/negative falls back to the 1h default.

type SignJWTResult

type SignJWTResult struct {
	Token     string // JWS compact serialization
	Header    aicjwt.Header
	Claims    aicjwt.OuterClaims
	Principal PrincipalUid
	AgentID   string
	CA        *x509.Certificate
	Alg       string
}

SignJWTResult is the output of SignJWT.

func SignJWT

func SignJWT(sc *SignConfig, opts JWTSignOptions) (*SignJWTResult, error)

SignJWT issues an AIC-JWT. It performs all AIC issuance validations shared with Sign(): required AIC/PA/DA checks, capability registration, PA−cap coverage, and — like Sign — the DA nonce consumption hook. It does NOT touch the database or build an X.509 certificate.

type SignResult

type SignResult struct {
	Cert       *x509.Certificate
	CertDER    []byte
	SerialHex  string
	PrivateKey crypto.Signer
	// Record is the DB record for this certificate. In SkipDB mode, the database is not
	// written; the caller is responsible for persistence (e.g. via RecordBuffer batch
	// persistence); also populated in synchronous write mode.
	Record *db.CertRecord
}

func CrossSign

func CrossSign(d *db.DB, issuerCert *x509.Certificate, issuerKey crypto.Signer, issuerName string, targetCAMeta *db.CAMeta, validity time.Duration, nc *NameConstraints) (*SignResult, error)

func Sign

func Sign(sc *SignConfig) (*SignResult, error)

type SignerKey

type SignerKey struct {
	Cert *x509.Certificate
	Key  crypto.Signer
}

SignerKey is a CA certificate + its private key.

type StaticSource

type StaticSource struct {
	CAs   []*x509.Certificate
	Roots []*x509.Certificate
}

StaticSource is an in-memory CertSource (test-friendly).

func (*StaticSource) FindIssuerCandidates

func (s *StaticSource) FindIssuerCandidates(issuerRaw string) ([]*x509.Certificate, error)

func (*StaticSource) TrustAnchors

func (s *StaticSource) TrustAnchors() ([]*x509.Certificate, error)

type SubCAConfig

type SubCAConfig struct {
	Name             string        // Unique name for the sub-CA
	ParentCA         string        // Parent CA name (issuing CA)
	KeyType          string        // Key type (ecdsa-p256, ecdsa-p384, rsa-2048, etc.)
	Validity         time.Duration // Certificate validity period
	MaxPathLen       int           // Path length constraint (0 for end-entity only)
	KeyUsage         []string      // Key usage extensions
	Protocol         string        // Protocol identifier (scep, cmp, acme, est)
	PermittedDomains []string      // Name constraints: permitted domains
	ExcludedDomains  []string      // Name constraints: excluded domains
	CRLBaseURL       string        // Base URL for the CRL Distribution Point extension
}

SubCAConfig holds configuration for creating a new sub-CA.

type SubCAMeta

type SubCAMeta struct {
	ID           int64
	Name         string
	ParentCA     string
	Cert         *x509.Certificate
	CertDER      []byte
	KeyEncrypted []byte
	Subject      string
	NotBefore    time.Time
	NotAfter     time.Time
	KeyAlgorithm string
	Fingerprint  string
	Status       string
	Protocol     string
	KeyUsage     string
	MaxPathLen   int
	CreatedAt    time.Time
	RevokedAt    *time.Time
	RevokeReason *int
}

SubCAMeta holds metadata for an existing sub-CA.

func GetSubCA

func GetSubCA(database *db.DB, name string) (*SubCAMeta, error)

GetSubCA retrieves a sub-CA by name.

func ListSubCAs

func ListSubCAs(database *db.DB, protocol string) ([]*SubCAMeta, error)

ListSubCAs returns all sub-CAs, optionally filtered by protocol.

func VerifySubCA

func VerifySubCA(database *db.DB, name string) (*SubCAMeta, error)

VerifySubCA checks if a sub-CA is valid (exists, active, and not expired).

type SubCAResult

type SubCAResult struct {
	Name        string
	Cert        *x509.Certificate
	CertDER     []byte
	CertPEM     []byte
	Key         crypto.Signer
	KeyPEM      []byte
	SerialHex   string
	Fingerprint string
}

SubCAResult holds the result of sub-CA creation.

func IssueSubCA

func IssueSubCA(database *db.DB, cfg *SubCAConfig, parentCert *x509.Certificate, parentKey crypto.Signer) (*SubCAResult, error)

IssueSubCA creates a new sub-CA certificate signed by the parent CA.

type TrustBridgeConfig

type TrustBridgeConfig struct {
	Bridges []TrustBridgePolicy `json:"bridges,omitempty"`
}

type TrustBridgePolicy

type TrustBridgePolicy struct {
	Enabled         bool     `json:"enabled"`
	IssuerCA        string   `json:"issuer_ca"`
	SubjectCA       string   `json:"subject_ca"`
	Validity        int      `json:"validity_days"`
	NameConstraints []string `json:"name_constraints,omitempty"`
}

type UserIdentity

type UserIdentity struct {
	Username string   `json:"username"`
	FullName string   `json:"full_name,omitempty"`
	Email    string   `json:"email,omitempty"`
	Dept     string   `json:"dept,omitempty"`
	Groups   []string `json:"groups,omitempty"`
	Source   string   `json:"source,omitempty"`
	Disabled bool     `json:"disabled"`
}

UserIdentity is the unified person identity model resolved from an identity source (bridge-ldap lookup or bridge-oauth userinfo). Phase 2 of identity-source unification: core maps these attributes into a base identity certificate ("identity-user" profile) without manual subject entry.

type ValidPolicyNode

type ValidPolicyNode struct {
	Depth    int      `json:"depth"`
	Policies []string `json:"policies"`
}

type VerifyPathOptions

type VerifyPathOptions struct {
	// UserInitialPolicySet is the set of acceptable certificate policies.
	// An empty set means "any policy is acceptable" (RFC 5280 §6.1.1 empty
	// user-initial-policy-set).
	UserInitialPolicySet []string
	// PolicyOID strings are parsed once; pass the ASN.1 OIDs to avoid
	// re-parsing.
	UserInitialPolicyOIDs []asn1ObjectIdentifier
	// VerifyPolicy enables RFC 5280 §6.1 policy processing. When false, only
	// structural validation (signatures, validity, trust) is performed.
	VerifyPolicy bool
	// CurrentTime overrides the "now" used for validity checks (default: now).
	CurrentTime time.Time
	// MaxDepth bounds chain length to protect against certificate loops.
	// Default 16 (RFC 5280 recommends ≤ 8 for normal PKIs, but bridge CA
	// chains can be longer).
	MaxDepth int
}

VerifyPathOptions controls path verification.

type VerifyResult

type VerifyResult struct {
	Valid           bool        `json:"valid"`
	Chain           []ChainNode `json:"chain,omitempty"`
	Verified        bool        `json:"verified"`
	RootTrustSource string      `json:"root_trust_source,omitempty"`
}

func VerifyCertificate

func VerifyCertificate(cert *x509.Certificate, roots *x509.CertPool, sources map[string]string) (*VerifyResult, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL