cacpiv

package
v0.17.3 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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.

View Source
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:

  1. 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.

  2. 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.

  3. 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

func ExtractEDIPIFromString(s string) string

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

func PrincipalValue(p *mtlsx.Principal, mapping string) (string, error)

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 New

func New(opts ...Option) *Adapter

New constructs an Adapter applying opts.

func (*Adapter) Apply

func (a *Adapter) Apply(p *mtlsx.Principal) error

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

func (a *Adapter) Verify(leaf *x509.Certificate, mapping string) (string, string, error)

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

func WithRejectUnknownClassification(v bool) Option

WithRejectUnknownClassification rejects certificates that cannot be classified into any of the known PE/NPE/CardAuth categories.

func WithRequirePerson

func WithRequirePerson(v bool) Option

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.

Jump to

Keyboard shortcuts

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