# Changelog
## Unreleased
The public API is source-compatible: everything re-exported from `lib.rs`, the public modules with
their pub fields and methods, and the CLI arguments are unchanged. The only API change is two new
functions, listed under "Added".
### Fixed
- **Nested bundles are all signed, innermost first.** Previously only the direct children of
`Frameworks/` and `PlugIns/` were signed. Bundles in `Watch/`, `AppClips/` and `Extensions/`
(ExtensionKit), and anything nested deeper (PlugIns of a Watch app, frameworks of an extension),
kept their original signature, so iOS rejected the install. Now every `.app`, `.appex` and
`.framework` with an executable is signed wherever it is. Each bundle seals the ones inside it, and
each gets `entitlements_by_bundle_id` and `embedded_mobileprovisions_by_bundle_id` for its own
bundle id.
- Directories with an `Info.plist` but no `CFBundleExecutable` (`.bundle` resource bundles, codeless
frameworks) directly under `Frameworks/` or `PlugIns/` made signing fail with
`MissingInfoString`. They are now sealed as plain files. So is any `.app`, `.appex` or
`.framework` whose Info.plist can't be read or parsed, or that has no `CFBundleIdentifier`: a
stray broken plist inside a resource bundle no longer aborts the whole signing.
- Files in subdirectories of `Frameworks/` and `PlugIns/` that are not bundles (for instance
`Frameworks/assets/data.bin`) were left out of `CodeResources`, which iOS reports as a modified
resource. They are sealed now.
- Root files whose name merely starts with `SC_Info` (for instance `SC_Info.plist`) were left
unsealed. Only the FairPlay `SC_Info/` directory is removed and skipped now.
- Stale files left in a bundle's own `_CodeSignature/` were sealed into its `CodeResources`. That
directory is now skipped, as Apple's codesign does.
- An untrusted IPA could make the signer read or write outside the bundle in two ways:
- a `CFBundleExecutable` such as `../x` or an absolute path;
- a symbolic link at `Info.plist`, `embedded.mobileprovision`, `_CodeSignature` or the executable.
Both are now refused with `InvalidBundlePath`; a symlinked `Info.plist` is refused before it is
read. For the root bundle this happens before anything is written. A nested bundle is checked
only when its turn comes, so by then its parents may already be partly rewritten. That leaves a
half-signed copy, but nothing outside the bundle is touched.
- Bundles nested more than 16 deep are refused with `InvalidBundlePath`. Real apps nest about four
deep, and a crafted IPA could otherwise recurse until the stack overflows.
- Mach-O parsing and signature placement:
- `section_64` flags were read from `reserved1` (offset 68, not 64). Zerofill sections were
therefore not recognised, and other sections could be mistaken for zerofill, which skewed the
header-padding limit for a new `LC_CODE_SIGNATURE`.
- When the first section started right at the end of the load commands (no header padding), the
limit fell back to the whole file. A new load command could then overwrite code that happened
to start with 16 zero bytes. That case is now `NeedsCodeSignatureAllocation`.
- An `LC_CODE_SIGNATURE` with `datasize == 0` and a stale `dataoff` sized the reservation from
that `dataoff` instead of where the signature goes (after `__LINKEDIT`). Signing then failed with
"encoded signature exceeded reserved LC_CODE_SIGNATURE size", or reserved far too much.
- A `__LINKEDIT` overlapping the load commands could panic (out-of-bounds write) or truncate the
binary. A `__LINKEDIT` that was not the last segment silently lost the segments after it. Both
are errors now.
- Fat binaries with very large alignments panicked or allocated gigabytes, and a fat header with
no architecture produced an 8-byte file. Both are errors now (alignments above 2^15, lipo's
limit, are refused).
- Overflowing `__TEXT`/segment bounds no longer panic in debug builds, and a fat architecture
count that overflows on 32-bit hosts is an error.
- An existing signature whose `dataoff` lies past the end of the file is refused, instead of
zero-filling the binary up to it (up to 4 GiB).
- A binary with no `LC_CODE_SIGNATURE` and trailing bytes after `__LINKEDIT` now plans its signature
from where it lands, `align16(__LINKEDIT end)`, instead of from the file length. It no longer
reserves code slots for bytes that get truncated. When `__LINKEDIT` ends the file, as it does
with every linker output, the result is byte-identical to before.
- Matching Apple's codesign, each checked against Apple's Security sources and real `CodeResources`
files:
- Files under a bundle's own `Base.lproj/` are no longer marked optional, because the
weight-1010 `^Base\.lproj/` rule wins over `^.*\.lproj/`. A parent still marks a nested
bundle's `Base.lproj/` files optional, since the rules apply to paths relative to the sealing
bundle. This changes `CodeResources`, and so the root CodeDirectory, of any app with
`Base.lproj`.
- CodeDirectories without a CMS blob (`cms_signer: None`) now set `CS_ADHOC`, like codesign and
rcodesign. The CLI always signs with CMS, so its output is unaffected.
- `execSegLimit` is `__TEXT`'s filesize, not its end. The two are equal whenever `__TEXT` starts
at offset 0, which is always the case in practice.
- Negative entitlement integers are encoded as minimal two's-complement DER integers, instead of
failing with `UnsupportedEntitlement`.
- `RustCryptoCmsSigner::from_der` now checks that the private key belongs to the signer
certificate, instead of producing a signature that only fails on the device. It compares the
RSA modulus and exponent, not the encoding of the certificate's key.
- The CLI prints errors with their message (`apple-codesign-quick: I/O error on …`) instead of their
`Debug` form. The exit code is still 1.
### Added
- `macho::read_macho_entitlements(path) -> Result