Help us shape the future of Go! Take the 2026 Go Developer Survey

applesign

package
v3.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 21 Imported by: 0

README

Portable Apple signatures

This is original Go format-writing code. No third-party signing implementation was copied or translated, and no module dependency was added. It reuses the project's universal-binary writer, safe file copier, shared PFX/CMS/RFC3161 primitives, and already-present property-list codec.

Supported inputs are little-endian 64-bit AMD64/ARM64 Mach-O executables, dylibs and loadable bundles targeting macOS 10.12 or later; universal binaries containing those architectures; canonical Contents application bundles; flat and versioned frameworks; and UDIF disk images with payloads below 4 GiB. Nested applications, XPC services, bundles, frameworks and Mach-O helpers are signed inside out. Scripts in code directories, encrypted executables, alternate bundle layouts, insufficient load-command padding and malformed metadata fail explicitly.

SignMachO, SignBundle, and SignDMG require a fresh output path. They preserve the input and remove partial output on failure. Callers own final transactional publication. Bundle copies preserve relative symlinks and file modes, reject escaping symlinks, and limit entry count and resource metadata. The code never exports keychain identities or logs private keys or passwords.

SigningOptions accepts a shared codesignature.Identity. It produces detached SHA-256/RSA CMS signatures with Apple's two hash-agility attributes and a signing time. Developer ID designated requirements bind the bundle identifier, Apple generic trust anchor, Developer ID issuer/leaf certificate extensions, and team OU. Non-Apple test identities bind the identifier and leaf certificate hash. The latter operand requires SHA-1 by Apple's requirement format; executable pages, resource seals and CMS signatures use SHA-256. A nil CMS callback selects ad hoc signing. XML and modern DER entitlements are both sealed.

Timestamping uses an explicitly configured server. The shared verifier accepts its documented SHA-2 timestamp profiles and verifies nonce, imprint, signer, certificate usage and trust before embedding a token. Apple's legacy SHA-1 timestamp profile is not silently accepted. A generated test certificate passing codesign --verify proves signature format/integrity, not Developer ID trust, Gatekeeper acceptance or notarization. Those distribution gates need a usable Developer ID identity and a compatible secure timestamp. Keychain-only identities and unsupported profiles retain the native-tool route in command integration.

Inspect reads code-directory identities for notarization ticket lookup. It is not a certificate-trust or signed-content verifier. CopyBundle and BundleExecutable provide the safe staging and main-executable lookup used by ticket attachment. Ticket retrieval and validation belong to a separate package.

Independent validation and fixtures

Tests use only standard-library test imports and project packages. On macOS they also invoke Apple's codesign --verify --strict --all-architectures, including deep bundle verification and tampering rejection. Native macOS 27.0.1 verification passed ad hoc and generated-RSA-certificate signatures for both architectures, universal files, nested apps/helpers, frameworks, and DMGs. Native hdiutil verify also accepted the signed image without payload changes.

A later control signed a Go application with native codesign, a real Developer ID identity, hardened runtime and a secure timestamp. The Go Notary client submitted it from Linux, received Apple's acceptance, and attached its ticket with the Go stapler. Native signature, ticket and Gatekeeper checks passed; see the live Notary acceptance. This establishes the submission and stapling path for a native-signed Developer ID application. It does not establish Apple's acceptance of Developer ID signatures generated by this package; no private key was exported.

The retained Mach-O fixtures were compiled from probe.c.txt (main) and the equally small original function int wails_probe(void) { return 42; } (framework) using Apple's clang for the named architecture. The DMG was created with Apple's hdiutil create -srcfolder over one original text file. developerid.req is the native csreq compilation of the requirement asserted in the corresponding test. The DER entitlement golden was extracted from native codesign output for our test property list. These are original test inputs and independently generated format fixtures, not third-party implementation code. The mixed-type entitlement fixture tests serialization only; the runtime fixture uses a valid JIT entitlement.

Primary format references:

Apple treats these structures as implementation details rather than a supported cross-platform API. Native verification remains an acceptance gate.

Small benchmark

Retained BenchmarkSignMachO signs a 4,272-byte AMD64 fixture to a fresh file, then removes that output. On Linux/AMD Ryzen 9 3950X, Go 1.26.10, GOMAXPROCS=4, three 200 ms runs measured 86.826, 89.774 and 121.759 microseconds/op (median 89.774), about 40.6 KB allocated and 34 allocations/op. This includes filesystem work and is not a whole-application throughput claim. A one-second CPU sample spent 78% in system calls and under 1% in the existing SHA-NI SHA-256 assembly. That profile provides no justification for custom assembly. Raw benchmark and profile results are retained with the external native acceptance artifacts.

Documentation

Overview

Package applesign writes Apple code signatures without platform executables. Signature formats are implementation details; interoperability is tested with Apple's codesign verifier, independently of this package's parser.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BundleExecutable

func BundleExecutable(root string) (string, error)

BundleExecutable locates a canonical app's main executable without following symlinked metadata or code directories. The returned path is absolute.

func CopyBundle

func CopyBundle(ctx context.Context, input, output string) (err error)

CopyBundle creates a fresh copy for signing or stapling, preserving regular file modes and relative symlinks while rejecting links outside the input. It removes partial output on failure and never overwrites an existing path.

func SignBundle

func SignBundle(ctx context.Context, input, output string, o Options) (err error)

SignBundle copies and signs a canonical macOS Contents-layout app bundle. Nested application bundles and Mach-O helpers are signed from the inside out. Output must not exist; callers can transactionally publish the completed copy.

Types

type CodeIdentity

type CodeIdentity struct {
	CPU        uint32
	Identifier string
	HashType   uint8
	CDHash     [20]byte
}

func Inspect

func Inspect(path string) ([]CodeIdentity, error)

Inspect returns code-directory identities for ticket lookup. It validates structural bounds, not certificate trust or the integrity of signed content.

func SignDMG

func SignDMG(ctx context.Context, input, output string, o Options) (id CodeIdentity, err error)

SignDMG embeds a signature between a UDIF image's payload and trailing header. The payload is unchanged. The header is sealed in special slot 6 with its signature size zeroed, matching Apple's independently generated disk images.

func SignMachO

func SignMachO(ctx context.Context, input, output string, options Options) (identities []CodeIdentity, err error)

SignMachO writes a new signed file. Input remains untouched, output must not exist, and partial output is removed on failure. Both native Wails desktop architectures are supported, independently signed in a universal executable.

type Options

type Options struct {
	Identifier      string
	TeamID          string
	Entitlements    []byte
	HardenedRuntime bool
	// SignCMS returns a detached CMS signature over the complete CodeDirectory.
	// A nil callback selects an ad-hoc signature. Key loading, certificate policy
	// and timestamp validation belong to the shared signing-identity layer.
	SignCMS func(context.Context, []byte) ([]byte, error)
	// contains filtered or unexported fields
}

func SigningOptions

func SigningOptions(identity codesignature.Identity, timestamp codesignature.Options) (Options, error)

SigningOptions constructs certificate-backed signing options, sharing the authenticated PFX/CMS/timestamp implementation with Windows signing. Set the returned Identifier for files or DMGs; bundle identifiers come from Info.plist. Timestamping uses only the explicitly configured server and trust roots. Notarized Developer ID distribution requires a compatible secure timestamp; callers must enforce that policy before submitting an artifact.

Jump to

Keyboard shortcuts

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