Documentation
¶
Overview ¶
Package cms signs and verifies the CMS (PKCS #7) signatures Apple MDM uses: the detached signature a device sends in the Mdm-Signature header when the MDM payload sets SignMessage, and the attached signature a server puts on configuration profiles.
Why ¶
The Mdm-Signature header is how a check-in or connect request proves it came from the enrolled device when TLS client certificates are not available to the application, and a signed enrollment profile is how a device knows the profile was not altered in transit. Phase 2 of the plan of record needs both. Verification wraps github.com/smallstep/pkcs7 and adds what that library lacks: a trust store, an injectable clock, and a signing-time tolerance (decision record 0006), because a device whose clock lags can sign with a certificate whose NotBefore is a few seconds in the future, which the library rejects unconditionally.
The package knows nothing about enrollments or HTTP. httpapi turns a verified signer certificate into the request identity, and profile decides when a profile must be signed. Header encoding and decoding live here so both sides of the protocol agree on the format.
References ¶
- Decision record 0006: docs/research/decisions/0006-mdm-signature-verification.md
- Decision record 0009: docs/research/decisions/0009-enrollment-profiles.md
- Plan of record: docs/research/implementation_plan.md (phase 2)
- Threat model: docs/security/threat-model.md (/checkin and /connect rows)
- Apple: https://developer.apple.com/documentation/devicemanagement/check-in
- Apple: https://developer.apple.com/documentation/devicemanagement/managing-certificates-for-device-management-services-and-devices
- Schema: third_party/device-management/mdm/profiles/com.apple.mdm.yaml (SignMessage)
- RFC 5652 (Cryptographic Message Syntax): https://www.rfc-editor.org/rfc/rfc5652
Index ¶
- Constants
- Variables
- func DecodeHeader(header string) ([]byte, error)
- func EncodeHeader(der []byte) string
- func Fingerprint(cert *x509.Certificate) string
- func IsSigned(data []byte) bool
- func Sign(content []byte, cert *x509.Certificate, key crypto.Signer) ([]byte, error)
- func SignAttached(content []byte, cert *x509.Certificate, key crypto.Signer) ([]byte, error)
- func Verify(der, content []byte, o VerifyOptions) (*x509.Certificate, error)
- func VerifyAttached(der []byte, o VerifyOptions) ([]byte, *x509.Certificate, error)
- func VerifyAttachedWith(der []byte, o VerifyAttachedOptions) ([]byte, *x509.Certificate, error)
- func VerifyHeader(header string, body []byte, o VerifyOptions) (*x509.Certificate, error)
- type VerifyAttachedOptions
- type VerifyOptions
Constants ¶
const HeaderName = "Mdm-Signature"
HeaderName is the HTTP header Apple devices use.
Variables ¶
var ( ErrHeader = errors.New("cms: malformed Mdm-Signature header") ErrParse = errors.New("cms: malformed CMS structure") ErrNoSigner = errors.New("cms: no signer") ErrMultipleSigners = errors.New("cms: more than one signer") ErrSignature = errors.New("cms: signature verification failed") ErrSigningTime = errors.New("cms: signing time outside certificate validity") ErrChain = errors.New("cms: certificate chain verification failed") ErrAlgorithm = errors.New("cms: unsupported algorithm") ErrSign = errors.New("cms: signing failed") )
Errors returned by this package.
Functions ¶
func DecodeHeader ¶
DecodeHeader parses an Mdm-Signature header value.
func EncodeHeader ¶
EncodeHeader renders a DER signature as the Mdm-Signature header value.
func Fingerprint ¶
func Fingerprint(cert *x509.Certificate) string
Fingerprint is the lower-case hex SHA-256 of the certificate's DER, the value stored for identity pinning.
func IsSigned ¶
IsSigned reports whether data looks like a DER CMS structure rather than a plain plist, so callers can accept both signed and unsigned profiles.
func Sign ¶
Sign produces a detached, DER-encoded CMS SignedData over content with SHA-256, signed by key and carrying cert.
func SignAttached ¶
SignAttached produces a CMS SignedData with the content embedded, which is what signed configuration profiles are (a .mobileconfig whose bytes are the DER structure).
func Verify ¶
func Verify(der, content []byte, o VerifyOptions) (*x509.Certificate, error)
Verify checks a detached signature over content and returns the signer certificate.
func VerifyAttached ¶
func VerifyAttached(der []byte, o VerifyOptions) ([]byte, *x509.Certificate, error)
VerifyAttached checks an attached signature and returns the embedded content and the signer certificate. The same trust and skew options as Verify apply.
func VerifyAttachedWith ¶
func VerifyAttachedWith(der []byte, o VerifyAttachedOptions) ([]byte, *x509.Certificate, error)
VerifyAttachedWith verifies an attached SignedData the way Apple device identities sign MachineInfo: exactly one signer whose certificate is in the bundle; when authenticated attributes are present the signature covers their DER SET and the messageDigest attribute must equal the digest of the content while contentType must be id-data, otherwise the signature covers the content; digest and signature algorithms are taken from the SignerInfo. It returns the embedded content and the signer.
func VerifyHeader ¶
func VerifyHeader(header string, body []byte, o VerifyOptions) (*x509.Certificate, error)
VerifyHeader verifies an Mdm-Signature header against the request body.
Types ¶
type VerifyAttachedOptions ¶
type VerifyAttachedOptions struct {
VerifyOptions
// IgnoreValidity builds the certificate path by name and signature
// alone, without applying validity windows. Apple device identities
// chain through the Apple iPhone Device CA, which expired in 2014 and
// still issues current leaves, so stock chain verification cannot
// accept them. SHA-1 signatures are tolerated on this path because the
// chain uses them.
IgnoreValidity bool
// Anchors are trust anchors matched by identity: the path is accepted
// when it reaches a certificate issued (by name and signature) by one
// of them, or one of them itself. They are the trust store for
// IgnoreValidity, since a CertPool cannot be walked; when
// IgnoreValidity is false they are added to Roots. Nil Anchors and nil
// Roots skip chain verification, as in Verify.
Anchors []*x509.Certificate
}
VerifyAttachedOptions control VerifyAttachedWith.
type VerifyOptions ¶
type VerifyOptions struct {
// Roots, when set, requires the signer certificate to chain to one of
// them (intermediates from the CMS structure are used). Nil skips chain
// verification and only checks the signature itself.
Roots *x509.CertPool
// Now supplies the verification time; defaults to time.Now.
Now func() time.Time
// ClockSkew tolerates a signing time this far outside the signer
// certificate's validity. Zero means no tolerance.
ClockSkew time.Duration
}
VerifyOptions control Verify.