Pure Rust Implementation of Apple Code Signing
April 14, 2021 at 01:45 PM | categories: PyOxidizer, Apple, RustA few weeks ago I (foolishly?) set out to implement Apple code signing
(what Apple's codesign
tool does) in pure Rust.
I wanted to quickly announce on this blog the existence of the project and
the news that as of a few minutes ago, the tugger-apple-codesign
crate
implementing the code signing functionality is now
published on crates.io!
So, you can now sign Apple binaries and bundles on non-Apple hardware by doing something like this:
$ cargo install tugger-apple-codesign
$ rcodesign sign /path/to/input /path/to/output
Current features include:
- Robust support for parsing embedded signatures and most related data
structures.
rcodesign extract
can be used to extract various signature data in raw or human readable form. - Parse and verify RFC 5652 Cryptographic Message Syntax (CMS) signature data.
- Sign binaries. If a code signing certificate key pair is provided, a CMS signature will be created. This includes support for Time-Stamp Protocol (TSP) / RFC 3161 tokens. If no key pair is provided, you get an ad-hoc signature.
- Signing bundles. Nested bundles and binaries will automatically be signed.
Non-code resources will be digested and a
CodeResources
XML file will be produced.
The most notable missing features are:
- No support for obtaining signing keys from keychains. If you want to sign with a cryptographic key pair, you'll need to point the tool at a PEM encoded key pair and CA chain.
- No support for parsing the Code Signing Requirements language. We can parse the
binary encoding produced by
csreq -b
and convert it back to this DSL. But we don't parse the human friendly language. - No support for notarization.
All of these could likely be implemented. However, I am not actively working on any of these features. If you would like to contribute support, make noise in the GitHub issue tracker.
The Rust API, CLI, and documentation are still a bit rough around the edges. I
haven't performed thorough QA on aspects of the functionality. However, the
tool is able to produce signed binaries that Apple's canonical codesign
tool
says are well-formed. So I'm reasonably confident some of the functionality
works as intended. If you find bugs or missing features, please
report them on GitHub. Or even
better: submit pull requests!
As part of this project, I also created and published the cryptographic-message-syntax crate, which is a pure Rust partial implementation of RFC 5652, which defines the cryptographic message signing mechanism. This RFC is a bit dated and seems to have been superseded by RPKI. So you may want to look elsewhere before inventing new signing mechanisms that use this format.
Finally, it appears the Windows code signing mechanism (Authenticode) also uses RFC 5652 (or a variant thereof) for cryptographic signatures. So by implementing Apple code signatures, I believe I've done most of the legwork to implement Windows/PE signing! I'll probably implement Windows signing in a new crate whenever I hook up automatic code signing to PyOxidizer, which was the impetus for this work (I want to make it possible to build distributable Apple programs without Apple hardware, using as many open source Rust components as possible).