Sign inner .framework bundles too; dart_bridge 1.7.1 - #38
Merged
Conversation
20260729 signed each outer .xcframework but left the .framework bundles inside every slice unsigned. An App Store IPA built against it reported `signed = true` but `isSecureTimestamp = false` for Python-ios, _ssl, _hashlib and dart_bridge, even though the outer seals carry genuine Apple TSA timestamps -- the receipt cdhashes match the published artifacts byte for byte, so Xcode was reading our signatures. The same result appeared in a development archive and an App Store export, so the export method was not the variable. The control that settles it: krzyzanowskim/OpenSSL 3.6.3000, an XCFramework Apple's App Store scan demonstrably accepts, signs all ten of its slices with an Apple Distribution identity and a secure timestamp, then signs the outer bundle last. An unsigned inner framework was the only structural difference left. xcf_sign_one now signs each slice's .framework first and the outer bundle last. The order is not optional: the outer seal hashes the bundle contents, so signing an inner framework afterwards would invalidate it. No -i for the inner bundles -- a .framework has a real CFBundleIdentifier in its own Info.plist. Verification was refactored into xcf_assert_signature (verifiable, not ad-hoc, secure timestamp, expected authority and team) and now covers every slice framework as well as the outer bundle, so an unsigned inner framework fails the release rather than shipping. No script outside the shared helper changes: sign_darwin_archives.sh already signs the finished archives through xcf_sign_one, and inner _CodeSignature directories survive both `xcodebuild -create-xcframework` and the tar round trip. This does add roughly two extra codesign invocations per xcframework -- about 500 TSA round trips across the three Python versions -- so expect sign-darwin-artifacts to take noticeably longer than on 20260729.
1.7.1 is the first dart_bridge release whose dart_bridge.framework slices are signed, not just the outer xcframework -- the counterpart of the change on this branch for the Python XCFrameworks (flet-dev/dart-bridge#14). Both halves have to move together. An IPA that picks up a 1.7.0 dart_bridge alongside inner-signed Python XCFrameworks would still carry one receipt reporting isSecureTimestamp = false, which leaves the ITMS-91065 question unanswered rather than answered. Version-keyed consumer caches hold the outer-only-signed 1.7.0 zip, so this must be a version bump rather than a re-release. Compiled binaries are unchanged from 1.7.0 on every platform.
xcf_slice_frameworks located each slice's bundle as <slice>/<xcframework basename>.framework. Everything this repo publishes has matching names, but the assumption fails silently rather than loudly when it does not hold: serious_python stages Python.xcframework as Python-<platform>.xcframework, and an unsigned inner framework in a renamed copy would be reported as "no slice frameworks found" instead of as unsigned. Globbing <slice>/*.framework removes the assumption. xcf_signing_identifier now walks the same enumerator instead of repeating the name-keyed lookup. Verification only -- signed output is unchanged. Keeps this helper byte-identical to dart-bridge's copy (flet-dev/dart-bridge#15).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #37. Pairs with dart-bridge#14 — both need to land, then a new dart_bridge release and a new date-keyed release here, before
serious_pythonre-pins.The finding
20260729 signed each outer
.xcframeworkbut left the.frameworkbundles inside every slice unsigned. An App Store IPA built against it:signedflipped as intended — that was #37 working. The receipt cdhashes match the published artifacts byte for byte (_ssl→ddb6c974196b0e16fef3c5e8defb4805b5d344ca), and the receipt's embedded leaf certificate is ourApple Distribution: Appveyor Systems Inc. (GXXRQJK434), so Xcode was reading our signatures. ButisSecureTimestampstayed false despite the outer seals carrying genuine Apple TSA timestamps.Checked in both a development archive and an App Store export — identical, so the export method wasn't the variable. (Also learned the
Signatures/folder only travels in App Store–bound IPAs; development and ad-hoc exports omit it, and it lives in the.xcarchivein all cases.)The control
krzyzanowskim/OpenSSL 3.6.3000 — accepted by Apple's scan per partout-io/openssl-apple#72. All ten slices are signed, outer signed last:
Ours:
codesign -dvvvon any inner framework reported not signed at all. That was the only structural difference left.The change
xcf_sign_onesigns each slice's.frameworkfirst, outer bundle last. The order is not optional — the outer seal hashes the bundle contents, so signing an inner framework afterwards would invalidate it, which is the exact failure mode this repo's signing work exists to prevent. No-ion the inner bundles: a.frameworkcarries a realCFBundleIdentifierin its ownInfo.plist.Verification refactored into
xcf_assert_signature(verifiable, not ad-hoc, secure timestamp, expected authority, expected team) and applied to every slice framework as well as the outer bundle. An unsigned inner framework now fails the release.Still no
--deep— Apple documents it as inappropriate for a distributable signature; signing each slice explicitly gives the same coverage correctly.No script outside the shared helper changes.
sign_darwin_archives.shalready signs the finished archives throughxcf_sign_one, and inner_CodeSignaturedirectories survive bothxcodebuild -create-xcframeworkand the tar round trip (verified).dart_bridge 1.7.1
manifest.jsonmoves to 1.7.1, the first dart_bridge release whose framework slices are signed rather than only the outer xcframework.Both halves have to move together: an IPA that picks up a 1.7.0 dart_bridge alongside inner-signed Python XCFrameworks would still carry one receipt reporting
isSecureTimestamp = false, which leaves the question unanswered rather than answered. Version bump rather than a re-release, because consumer caches are version-keyed and hold the outer-only-signed 1.7.0 zip.Merge order: dart-bridge#14 → release dart_bridge v1.7.1 → merge this → cut the new date-keyed release here. Merging this first would pin a manifest at a release that does not exist yet.
Expect a slower signing job
This adds roughly two extra
codesigninvocations per xcframework — about 500 TSA round trips across the three Python versions, up from ~170.sign-darwin-artifactswill take noticeably longer than on 20260729. If it becomes a problem, the fan-out is a one-line change to pipeline per archive.Honest caveat
This makes our artifacts structurally identical to one Apple accepts, which is the strongest evidence available. It is not proof that
isSecureTimestampwill flip — the field's semantics are undocumented, and the audited archive contained no signed third-party control to compare against. The definitive test is a TestFlight upload once this, dart-bridge 1.7.1, and theserious_pythonre-pin have all landed.