Cross-platform code signing functionality

8 breaking releases

Uses new Rust 2021

0.9.0 Nov 8, 2022
0.8.0 Jun 5, 2022
0.7.0 Jun 5, 2022
0.6.0 Mar 6, 2022
0.2.0 May 6, 2021

#505 in Cryptography

Download history 98/week @ 2022-08-13 71/week @ 2022-08-20 96/week @ 2022-08-27 56/week @ 2022-09-03 53/week @ 2022-09-10 43/week @ 2022-09-17 76/week @ 2022-09-24 60/week @ 2022-10-01 53/week @ 2022-10-08 47/week @ 2022-10-15 80/week @ 2022-10-22 64/week @ 2022-10-29 194/week @ 2022-11-05 87/week @ 2022-11-12 69/week @ 2022-11-19 57/week @ 2022-11-26

436 downloads per month
Used in 2 crates

MPL-2.0 license



tugger-code-signing is a library crate implementing functionality related to code signing.

tugger-code-signing is part of the Tugger application distribution tool but exists as its own crate to facilitate code reuse for other tools wishing to perform similar functionality. Tugger is part of the PyOxidizer project and this crate is developed in that repository.

While this crate is developed as part of a larger project, modifications to support its use outside of its primary use case are very much welcome!


Cross-platform interface for code signing.

This crate implements functionality for performing code signing in a platform-agnostic manner. It attempts to abstract over platform differences so users don't care what platform they are running on or what type of entity they are signing. It achieves varying levels of success, depending on limitations of underlying crates.

General Workflow

[SigningCertificate] represents a code signing certificate (logically a private key + a public X.509 certificate). Instances are constructed with a reference to a code signing certificate in one of the enumerated supported locations.

[SigningCertificate] are converted into [Signer], which is a slightly broader scoped entity. [Signer] holds additional attributes beyond the [SigningCertificate], such as a list of issuing [CapturedX509Certificate] constituting the certificate chain and a Time-Stamp Protocol server URL to use.

[SignableCandidate] represents the different potential data types that can be signed. e.g. a filesystem path or slice of data.

[Signer] exposes a concrete test for whether a [SignableCandidate] is signable, via [Signer::resolve_signability]. The test relies on heuristics in the supported signing backends (e.g. [apple_codesign] and [tugger_windows_codesign]) as well as [Signer] specific state to determine if an entity is signable. False positives and negatives are possible: please report bugs! If an entity is signable, it will be converted to a [Signable] instance.

To sign a [Signable], you need to obtain a [SignableSigner]. You can do this by calling [Signer::resolve_signer]. This function performs signability checks internally and returns None if unsignable entities are seen. So most consumers with an intent to sign should call this without calling [Signer::resolve_signability] to avoid a potentially expensive double signability test.

[SignableSigner] are associated with a [Signer] and [Signable] and are used for signing just a single entity. [SignableSigner] instances are guaranteed to be capable of signing a [Signable]. However, signing operations support specifying writing the signed results to multiple types of destinations, as specified via [SigningDestination], and not every destination is supported by every input or signing backend. So before attempting signing, it is a good idea to call [SignableSigner.destination_compatibility] and verify that writing to a specific [SigningDestination] is supported!

[SignableSigner] instances can further be customized to influence signing settings. See its documentation for available settings. For power users, callback functions can be registered on [Signer] instances to allow customization of the low-level signing primitives used for signing individual [Signable]. See [Signer::apple_settings_callback] and [Signer::windows_settings_callback].

Finally, a signing operation can be performed via [SignableSigner::sign]. This hides away all the complexity of mapping different signable entities to different signing backends and gives you a relatively clean interface to attempt code signing. If signing was successful, you'll get a [SignedOutput] describing where the signed content lives.


~1M SLoC