Known Issues and Limitations¶
Apple code signing is complex. While this project strives to provide all the features and compatibility that Apple’s official tooling provides, we won’t always get it right. This document captures some of the areas where we know we fall short.
Bundle Handling in General¶
Bundle signing is complex for a few reasons:
The types and layouts of bundles are highly varied. Application bundles. Frameworks. Kernel extensions. macOS flavored vs iOS flavored bundles. The list goes on.
Bundles can be nested.
Signatures in nested bundles often need to propagate to their parent bundle.
Bundles encapsulate other signable entities, notably Mach-O binaries.
All this complexity means bundle signing is susceptible to a lot of subtle bugs and variation from how Apple’s tooling does it.
If you find bugs in bundle signing or have suggestions for improving its ergonomics, please file a GitHub issue!
Cannot Sign File Contents of DMGs¶
We support signing DMGs. But we can’t recursively inspect the files within DMGs and sign those. e.g. if a DMG contains a Mach-O binary, we can’t sign that Mach-O by unpacking it from the DMG and writing a new DMG.
The reason we can’t do this is because DMGs contain a nested filesystem (likely HFS+) and we don’t (yet) have a cross-platform mechanism for reading and writing HFS+ filesystems.
On macOS, we could call out to
hdiutil to mount a DMG to see its
contents and again to create a new DMG. However, this isn’t implemented
because we don’t perceive there to be value in it: if you have access to
macOS you should probably just use Apple’s official signing tooling!
There are open source libraries for reading and writing HFS+ filesystems. We could potentially integrate those to support reading and writing the contents of DMGs. We could also potentially leverage a pure Rust HFS+ implementation (this is a preferred solution).
DMG also supports multiple embedded filesystem types and it is possible we could leverage one that isn’t HFS+ (or APFS) and produce working DMGs. This is an area we haven’t yet explored.
If you want to distribute DMGs signed with this tool that themselves have
signed files, you’ll need to sign the files inside the DMG before the DMG
is created. Then you’ll need to create the DMG (using
whatever tool you have access to) then feed that DMG into this tool for
https://github.com/indygreg/PyOxidizer/issues/540 is our tracking issue for DMG writing support. If you have ideas, please comment there!
Cannot Recursively Sign Flat Packages (
Flat Packages (
.pkg installers) are a complex file format.
We have support for signing
.pkg installers by reading the files
within a flat package. And we are capable of recursively extracting
and signing the
.pkg installers that themselves are often embedded
What we don’t yet have support for is mutating the file content within
flat packages /
.pkg installers. This means we can’t recursively sign
.pkg installers or bundles or Mach-O binaries within.
The main blocker to implementing
.pkg writing is support for
reading and writing Apple’s Bill of Materials file format. These are
Bom files within flat packages. The author of this project
has an unpublished Rust crate to read and write bom files but he
encountered issues getting it to write files that validate with Apple’s
So if you want to sign
.pkg files that themselves containable signable
entities, you need to sign files going into the
.pkg before creating
.pkg. Then you need to create the
.pkg and invoke this tool to
.pkg. For installers that contained nested
this process will be quite tedious. Invoking
productbuild will likely be much simpler.
https://github.com/indygreg/PyOxidizer/issues/541 is our tracking issue for flat packages writing support.
Extra Signing or Time-Stamp Token Operations¶
Signatures often need to encapsulate the size of the resulting signature. This creates a chicken-and-egg problem because how can we know the size of the resulting signature before we actually produce it!
In some cases, this tool will create a fake signature and obtain an actual time-stamp token from a server in order to resolve the size of the data so we can better estimate the size of the real signature.
We are not sure if Apple’s tooling does this. But ours does and the extra operations can be annoying because they may require extra unlocks of signing keys or communications with a time-stamp token server.
We can likely eliminate the extra use of the signing key for generating these stand-in signatures and we can probably only make 1 request to the time-stamp token server to obtain the size of its signatures. But we haven’t implemented this throughout the code base yet.
Long Tail of Random Discrepancies from Apple’s Tooling¶
Apple’s code signature format is really, really complex. There are tons of data structures and fields with complex values.
There is likely a long tail of minor differences in implementation that result in variations between the behavior of our implementation and Apple’s.
In general, we consider differences in behavior in our implementation to
be bugs worth filing. So please use
rcodesign diff-signatures to report
Known areas where discrepancies are likely include:
The code requirements expression embedded into Mach-O binaries. We attempt to derive one based on the signing key. The expression may not be exactly what Apple’s tools derive automatically. We consider this a bug.
Executable segment flags and code signing flags. The exact logic for determining what flags to set when is complex. In general, we consider differences in behavior here to be bugs.
Size of embedded signatures. You often need to estimate the size of the produced embedded signature before signing because the signature encapsulates its own size. Our estimation method varies from Apple’s and can result in signatures with more or less padded null bytes. This difference should be mostly harmless. Improvements to make our signatures use less wasteful extra padding are appreciated.