cms

package
v0.7.3 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package cms signs and verifies attached and detached CMS signatures used by Apple device management, and decrypts CMS envelopes.

Design

Detached signatures authenticate Mdm-Signature request bodies; attached signatures carry configuration-profile content. Verification requires one signer and supports explicit trust roots, an injected clock and configured signing-time tolerance. The tolerant path still validates digest, attributes, signature and chain.

A valid signature proves key possession, not authorization for an enrollment. HTTP certificate extraction and service pinning apply that separate policy. Callers select trust roots and whether profile parsing requires a signature.

DecryptEnvelope handles BER/DER EnvelopedData with a matching RSA recipient. The caller authenticates the enclosing MDM response and retains the original reply key for delayed FileVault results. Certificate expiry does not prevent decrypting an older envelope; decryption alone does not prove its sender.

References

Index

Examples

Constants

View Source
const HeaderName = "Mdm-Signature"

HeaderName is the HTTP header Apple devices use.

Variables

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

View Source
var (
	ErrRecipient = errors.New("cms: invalid recipient certificate or key")
	ErrDecrypt   = errors.New("cms: envelope decryption failed")
)

Envelope errors do not include ciphertext, key material or plaintext.

Functions

func DecodeHeader

func DecodeHeader(header string) ([]byte, error)

DecodeHeader parses an Mdm-Signature header value.

func DecryptEnvelope added in v0.7.3

func DecryptEnvelope(data []byte, cert *x509.Certificate, key crypto.Decrypter) ([]byte, error)

DecryptEnvelope decrypts CMS EnvelopedData (BER or DER), including FileVault's EncryptedNewRecoveryKey. cert must be the ReplyEncryptionCertificate used for the command; key may be an external RSA crypto.Decrypter. Retain both until delayed command responses have been handled. No certificate expiry check is made: expiration does not prevent decrypting a previously encrypted response. Supported algorithms are those of the existing smallstep/pkcs7 decoder. Decryption provides no sender authentication; authenticate the MDM response.

Example
package main

import (
	"crypto"
	"crypto/x509"
	"errors"

	"github.com/deploymenttheory/go-apple-dm/devicemanagement/mdmprotocol/cms"
	"github.com/deploymenttheory/go-apple-dm/devicemanagement/schema/commands"
)

var errMissingRotationResult = errors.New("missing encrypted recovery key")

// readRotatedKey is called after authenticating/decoding the MDM response.
// Load the original reply certificate/key by CommandUUID, even if the active
// reply key has since rotated. Store the recovered bytes in your secret store.
func readRotatedKey(response *commands.RotateFileVaultKeyResponse, cert *x509.Certificate, key crypto.Decrypter) ([]byte, error) {
	if response == nil || response.RotateResult == nil || len(response.RotateResult.EncryptedNewRecoveryKey) == 0 {
		return nil, errMissingRotationResult
	}
	return cms.DecryptEnvelope(response.RotateResult.EncryptedNewRecoveryKey, cert, key)
}

func main() {
	// For the outgoing RotateFileVaultKey command, set
	// ReplyEncryptionCertificate to cert.Raw. Persist that certificate and its
	// private-key reference with the command before enqueueing it. A later
	// response is handled by readRotatedKey, not by selecting today's active key.
	_ = readRotatedKey
}

func EncodeHeader

func EncodeHeader(der []byte) string

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

func IsSigned(data []byte) bool

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

func Sign(content []byte, cert *x509.Certificate, key crypto.Signer) ([]byte, error)

Sign produces a detached, DER-encoded CMS SignedData over content with SHA-256, signed by key and carrying cert.

func SignAttached

func SignAttached(content []byte, cert *x509.Certificate, key crypto.Signer) ([]byte, error)

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.

Jump to

Keyboard shortcuts

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