Skip to content

Feature/codesigning - #1

Merged
setoelkahfi merged 4 commits into
developmentfrom
feature/codesigning
Aug 6, 2026
Merged

Feature/codesigning#1
setoelkahfi merged 4 commits into
developmentfrom
feature/codesigning

Conversation

@setoelkahfi

Copy link
Copy Markdown
Contributor

No description provided.

setoelkahfi and others added 4 commits August 6, 2026 07:11
First slice of the fastlane match replacement, chosen because it is the
part nothing else can substitute for: generating a key pair, getting Apple
to certify it, and keeping both halves.

Apple never gives you a private key. You generate one locally, send only
the public half inside a CSR, and Apple returns a certificate that vouches
for it. So a certificate whose key is lost is dead permanently, and an
expired certificate cannot be renewed at all: there is no such endpoint,
only creating a new one, which means a new key and a new CSR. That is why
the portal's Download button cannot rescue anybody, and why this command
writes the key to disk before it calls Apple. Failing after the request
would leave a certificate whose key we never persisted, which is
unrecoverable; failing before it leaves an unused key, which costs
nothing.

The CSR must be RSA 2048. Apple rejects ECDSA, which is what most modern
tooling emits by default. rcgen cannot generate RSA keys, so the key comes
from the rsa crate as PKCS#8 PEM and rcgen re-parses it purely to produce
the self-signature. The test asserts the result through openssl rather
than through our own types, since checking our output with our own parser
would prove nothing. It reads stdout and stderr together: macOS ships
LibreSSL, which prints the verify banner to stderr while OpenSSL proper
puts it on stdout, and reading one stream makes the test depend on which
implementation the machine has.

Key material is Zeroizing, Debug is hand-written to redact it, and the
private key file is opened 0600 at creation rather than chmod'ed after, so
it is never briefly world-readable. certificateContent stays out of the
list output: it is large, and printing it invites pasting certificate
bodies somewhere they should not go.

Revoking asks for the id twice. It is irreversible and breaks every
provisioning profile embedding that certificate for the whole team at
once, including builds already running in CI, so it should be hard to do
by accident and impossible to do by autocomplete.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGeEFM4hBGSZYXjFWguaWa
Two runners on purpose. The CSR tests verify our output through openssl
rather than through our own parser, and the hosts ship different ones:
ubuntu has OpenSSL 3, macOS has LibreSSL, and they disagree about which
stream the verify banner goes to. That difference already cost a debugging
round locally, so a single-host matrix would let the same class of bug
through unnoticed. macOS is also where this crate's signing work has to
run eventually, since keychain access exists nowhere else. fail-fast is
off because when a test fails on exactly one host, that asymmetry is the
most useful part of the report.

The rustdoc job pays for itself immediately: it found a link to a
`KeyPair` type that does not exist, in the paragraph explaining that a
lost private key is unrecoverable. The intra-doc links carry real weight
here, since the certificate module's warnings point at the csr module that
explains them, and a broken one drops that thread silently. Also fixes a
pre-existing unclosed `<key-id>` HTML tag in the CLI's help text, which
rustdoc reads as a tag rather than a placeholder.

openssl version is printed before the tests because it decides how the CSR
verification behaves and changes under us whenever GitHub bumps runner
images.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGeEFM4hBGSZYXjFWguaWa
@setoelkahfi setoelkahfi self-assigned this Aug 6, 2026
@setoelkahfi
setoelkahfi merged commit 1da55d5 into development Aug 6, 2026
2 checks passed
@setoelkahfi
setoelkahfi deleted the feature/codesigning branch August 6, 2026 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants