Skip to content

Trust: read the whole keychain (#9); working native opt-out; SetFallbackRoots - #14

Draft
schmonz wants to merge 13 commits into
mainfrom
fix/keychain-trust
Draft

schmonz wants to merge 13 commits into
mainfrom
fix/keychain-trust

Conversation

@schmonz

@schmonz schmonz commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

See docs plan 2026-09-13, Phase 2.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE

Amitai Schleier and others added 13 commits September 13, 2026 20:22
…not anchored via the bundle

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…d policy-aware enumeration (#9)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…t cover SSL (#9)

Go 1.17's sslTrustSettingsResult consulted admin trust settings only when the
user had none, so user settings for another policy, a hostname, or an explicit
Unspecified hid an admin Deny (fail-open). Evaluate user, then admin, falling
through when a domain gives no definitive result for SSL, as Apple does. The
Go 1.17 array evaluation is kept byte-for-byte in its own helper. List every
deviation from Go 1.17 in 0008. userdeny: guard the policy OID lookup, stop at
the first definitive result, skip unreadable results, and check Never Trust
only on self-signed certificates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…no longer panics

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…kroots=0 does too

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…e adapted Go 1.24 loop

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…ifiers and the user domain

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
The legacy SecTrust tests skipped whenever the path under test itself
errored on the fixture, so a broken legacy path read as SKIP under a PASSing
parent. They now skip only when an independent probe -- the non-forced
systemVerify, which is the modern API on CI's macOS -- says the host doesn't
trust the fixture; otherwise any error fails.

unit-trust.sh fails on CI if any KeychainUnion test or subtest skips, and
raises the top-level PASS floor to the 15 tests CI runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
…usted

Issue #9's core input shape had no portable test: one root DER enumerated
as TrustRoot and as Deny, in either order, with a CA bundle also carrying it.
The new table test turns the verify-time veto off and asserts the root is in
the distrust set and anchors nothing. Anchoring inside the enumeration loop
fails its TrustRoot-then-Deny case while every other test still passes.

The 0009 description now summarizes all the portable tests, and the
unit-trust floor rises to the 16 top-level tests CI runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
0010: keychainUnionDistrusted is filled lazily by loadSystemRoots, not by a
darwin init, and the veto applies whatever VerifyOptions.KeyUsages asks for.
0013: an empty union sends Verify with nil Roots to Apple's verifier, while
SystemCertPool() returns the error. 0008: kSecTrustSettingsApplication is
ignored as in Go 1.17 (no behavior change), noted in the description and in
an added, marked comment line beside Go 1.17's list of ignored keys; and the
USER-domain no-prompt claim is limited to the contexts tested.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
… step

CI's macOS can't exercise the 10.9 SecTrustCreateWithCertificates fix, so
unit-trust.sh asserts in the patched source that the SSL policy is passed
directly, not in a CFArray. smoke-trust.sh gains semi-manual steps for the
user -> admin fall-through: an admin SSL Never Trust must still reject when
the user's setting covers only S/MIME. The BadSSL root is supplied as its own
CA bundle (SSL_CERT_FILE) with a VERIFIED control first, since an untrusted
root is rejected anyway and REJECTED alone would prove nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
CLAUDE.md limits the no-prompt claim to the contexts tested. The release
notes cover trust settings made in Keychain Access generally, overriding the
administrator's only for the uses they cover. verify_tls.go no longer claims
the Let's Encrypt test endpoints chain to exactly one root: valid-isrgrootx1
chains via a cross-signed ISRG Root YR. capture/main.go is gofmt'd.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RR2zBLHQohzHofoxJ12UvE
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.

1 participant