Documentation
¶
Overview ¶
Package cacpiv extends mtlsx.Principal with DOD CAC and federal PIV identity extraction per FIPS 201-3 and the X.509 Certificate Policy for the U.S. Federal PKI Common Policy Framework.
Index ¶
- Variables
- func ExtractEDIPI(c *x509.Certificate) string
- func ExtractEDIPIFromString(s string) string
- func PolicyOIDByName(name string) (asn1.ObjectIdentifier, bool)
- func PolicyOIDName(oid asn1.ObjectIdentifier) (string, bool)
- func PrincipalValue(p *mtlsx.Principal, mapping string) (string, error)
- type Adapter
- type Classification
- type Option
- type PolicyOID
Constants ¶
This section is empty.
Variables ¶
var ( // ErrRequirePerson is returned when the route demanded a PE // (Person Entity) but the certificate is a CardAuth, NPE, or // unknown classification. ErrRequirePerson = errors.New("cacpiv: route requires PE certificate") // ErrRequiredPolicyOID is returned when none of the required // PolicyOIDs are present on the certificate. ErrRequiredPolicyOID = errors.New("cacpiv: required certificate policy OID absent") // ErrUnknownClassification is returned when WithRejectUnknownClassification // is set and the certificate cannot be classified into any known PE/NPE // or CardAuth category. ErrUnknownClassification = errors.New("cacpiv: certificate classification unknown") // ErrMappingEmpty is returned by PrincipalValue (and therefore // Adapter.Verify) when the field selected by mtls.principal_mapping is // empty on the verified certificate. A configured mapping is a security // control -- binding identity to a specific certificate field -- so a // certificate that does not carry that field must fail closed rather // than authenticate with no identity bound to it. ErrMappingEmpty = errors.New("cacpiv: principal_mapping field empty on certificate") // ErrUnknownMapping is returned when principal_mapping names a value // PrincipalValue does not recognize. Config validation should already // reject this before generation; this is a defense-in-depth guard. ErrUnknownMapping = errors.New("cacpiv: unknown principal_mapping") // ErrNilCertificate is returned by Adapter.Verify when called with a // nil leaf certificate. ErrNilCertificate = errors.New("cacpiv: nil certificate") )
Errors returned by Adapter.Apply.
var PrincipalMappings = []string{"subject_cn", "upn", "san_email", "san_dns_first", "edipi"}
PrincipalMappings lists the values cmd/apic's config validation accepts for mtls.principal_mapping (empty string is also accepted and defaults to "subject_cn"). Exported so cmd/apic can validate against the single source of truth instead of duplicating the literal list.
Functions ¶
func ExtractEDIPI ¶
func ExtractEDIPI(c *x509.Certificate) string
ExtractEDIPI returns the EDIPI (DOD ID Number) found in the certificate.
A-S8 hardening (2026-05-28): the prior implementation ran a loose `\b\d{10}\b` regex over CommonName / UPN / email local-part, which could false-positive on any fortuitous 10-digit substring (e.g. a phone number inside an OU). Extraction now anchors to the documented EDIPI position in each source:
Subject CommonName: per DOD CAC convention "LAST.FIRST.M.EDIPI", EDIPI is the FINAL dot-separated component. Only that component is checked, and it must be EXACTLY ten ASCII digits.
SAN UPN otherName (typically EDIPI@mil): EDIPI is the entire local-part to the left of "@". Only that local-part is checked, same strict ten-digit rule. Uses mtlsx.ExtractUPN to read the OID-1.3.6.1.4.1.311.20.2.3 otherName, not raw DNS/email SANs.
SAN rfc822Name (email): EDIPI is the local-part to the left of "@". Same strict rule.
Returns "" when no source yields a strict ten-digit EDIPI match.
func ExtractEDIPIFromString ¶
ExtractEDIPIFromString is kept for tests and external callers that have already isolated a candidate string (e.g. via custom OID extraction). It applies the same strict ten-digit rule rather than the prior regex-anywhere scan.
A bare ten-digit string returns that string. Strings containing any other character (including leading/trailing whitespace) return "". Callers that want to feed a CommonName or full email should use ExtractEDIPI directly so the per-source anchoring runs first.
func PolicyOIDByName ¶
func PolicyOIDByName(name string) (asn1.ObjectIdentifier, bool)
PolicyOIDByName looks up an OID by its IANA / DOD label.
func PolicyOIDName ¶
func PolicyOIDName(oid asn1.ObjectIdentifier) (string, bool)
PolicyOIDName looks up the label for a known OID. Returns "" if unknown.
func PrincipalValue ¶ added in v0.17.0
PrincipalValue selects one identity field from p per mapping. Recognized values (case-insensitive, whitespace-trimmed): "" and "subject_cn" (default) select p.CommonName; "upn" selects p.UPN; "san_email" selects the first p.SANEmails entry; "san_dns_first" selects the first p.SANDNS entry; "edipi" selects p.EDIPI (populated by Adapter.Apply). Returns ErrMappingEmpty when the selected field is empty and ErrUnknownMapping when mapping does not match any recognized value -- config validation (cmd/apic) rejects unknown mapping values before generation, so reaching the default case here indicates a config/runtime mismatch and this is defense-in-depth, not the primary guard.
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter post-processes mtlsx.Principal with CAC/PIV identity fields. Construct via New(opts...) and pass Adapter.Apply as mtlsx.VerifierConfig.PrincipalAdapter.
func (*Adapter) Apply ¶
Apply populates p with CAC/PIV fields and enforces the configured policy. Returns nil and leaves p untouched when p.Raw is nil (anonymous flow).
func (*Adapter) Verify ¶ added in v0.17.0
Verify implements securex.CertPolicyVerifier (structural typing -- this package never imports securex, so there is no method-signature copy to keep in sync beyond the two return values matching). It builds a mtlsx.Principal from leaf, applies the adapter's CAC/PIV policy (require_person / required_policy_oids / reject_unknown_classification), and resolves mapping (mtls.principal_mapping) to an identity string via PrincipalValue. Returns an error -- and therefore fails the mTLS request closed -- when the adapter rejects the certificate OR the selected mapping field is empty on it (ErrMappingEmpty): a configured mapping is a security control, so a certificate lacking that field must not silently authenticate with no bound identity.
type Classification ¶
type Classification int
Classification labels for CAC/PIV certificates by certificate policy OID.
const ( ClassUnknown Classification = iota ClassPersonPIVAuth // federal PIV authentication cert ClassPersonCAC // DOD CAC (mediumHardware family) ClassCardAuthentication // PIV card-auth cert (uses PIN, no signing) ClassNonPersonEntity // DOD NPE / device cert )
func Classify ¶
func Classify(c *x509.Certificate) Classification
Classify inspects the certificate's policy OIDs and returns the most specific classification. The order of checks is deliberate: PIV Auth before CAC, CAC before NPE, NPE before CardAuth — because some DOD certs carry multiple policies and we want the strictest match first.
func (Classification) IsPerson ¶
func (c Classification) IsPerson() bool
IsPerson reports whether c represents a human cardholder (PE). PE endpoints (typical workforce / mission users) should refuse Card Authentication and NPE classifications.
func (Classification) String ¶
func (c Classification) String() string
String renders the canonical kebab-case label used by Principal.Classification.
type Option ¶
type Option func(*Adapter)
Option mutates an Adapter at construction.
func WithRejectUnknownClassification ¶
WithRejectUnknownClassification rejects certificates that cannot be classified into any of the known PE/NPE/CardAuth categories.
func WithRequirePerson ¶
WithRequirePerson rejects any certificate not classified as PE (PIV Auth or CAC). Use for human-only endpoints.
func WithRequiredPolicyOIDs ¶
func WithRequiredPolicyOIDs(oids []asn1.ObjectIdentifier) Option
WithRequiredPolicyOIDs requires at least one of the listed OIDs in the certificate's PolicyIdentifiers. Empty slice disables the check.
type PolicyOID ¶
type PolicyOID struct {
Name string
OID asn1.ObjectIdentifier
}
PolicyOID captures one named certificate policy used by federal/DOD PKIs.
func PolicyOIDs ¶
func PolicyOIDs() []PolicyOID
PolicyOIDs returns a copy of the catalog for callers that need to walk it.