From 75994241ad376db1ec3eb12faed50143bcd6a52c Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 30 Jul 2026 15:55:19 -0700 Subject: [PATCH 1/2] Bump to 4.5.1: sign and verify both XCFramework layers 4.5.0 shipped provider artifacts whose outer .xcframework was signed but whose inner .framework bundles were not. An App Store IPA built against it reported `signed = true` -- the 4.5.0 fix working, with receipt cdhashes matching the published artifacts byte for byte -- but `isSecureTimestamp = false` for Python-ios, _ssl, _hashlib and dart_bridge. Identical 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 scan demonstrably accepts, signs all ten of its slices with an Apple Distribution identity and a secure timestamp and signs the outer bundle last. An unsigned inner framework was the only structural difference left. python-build 20260730 and dart_bridge 1.7.1 now sign both layers. Consumer verification follows: spv_verify_provider checks each slice's inner framework as well as the outer bundle, so an unsigned slice fails during packaging instead of surfacing in a submission. Two lookup bugs found while building negative controls for it are fixed here too: * Slice frameworks are located by glob rather than by the xcframework's name. stage_spm.sh stages Python.xcframework as Python-.xcframework, and a name-keyed lookup silently finds nothing there -- turning "this slice is unsigned" into "there was nothing to check". * Signature presence is probed with `codesign -dv` rather than by looking for a _CodeSignature directory. A versioned macOS bundle keeps it under Versions// and that name is not always "A": CPython uses Versions/3.14, so a correctly signed macOS framework was reported as unsigned. Re-pins python-build to 20260730 and dart_bridge to 1.7.1. No Python version moved (3.12.13 / 3.13.14 / 3.14.6); nothing about Android, Linux or Windows changed. 20260730 also fixes the macOS Python.framework layout -- Headers and Modules were real directories at the framework root, which codesign rejects for a versioned bundle -- so they are now symlinks into Versions/Current. Verified end to end against the signed artifacts in require mode with the real team id: 57 xcframeworks and 115 slice frameworks verified at extraction, digests unchanged through sync and both SwiftPM stagings, and the staged _ssl.xcframework inside the Swift package reporting Apple Distribution on BOTH layers with its original signing timestamp. Stripping an inner signature fails the run. --- src/serious_python/CHANGELOG.md | 6 + src/serious_python/README.md | 4 + .../lib/src/python_versions.dart | 6 +- src/serious_python/pubspec.yaml | 2 +- src/serious_python_android/CHANGELOG.md | 4 + .../android/build.gradle.kts | 2 +- .../android/python_versions.properties | 6 +- src/serious_python_android/pubspec.yaml | 2 +- src/serious_python_darwin/CHANGELOG.md | 7 ++ .../darwin/python_versions.properties | 6 +- .../darwin/serious_python_darwin.podspec | 2 +- .../darwin/xcframework_verify.sh | 112 +++++++++++++----- src/serious_python_darwin/pubspec.yaml | 2 +- src/serious_python_linux/CHANGELOG.md | 4 + .../linux/python_versions.properties | 6 +- src/serious_python_linux/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 + .../pubspec.yaml | 2 +- src/serious_python_windows/CHANGELOG.md | 4 + src/serious_python_windows/pubspec.yaml | 2 +- .../windows/python_versions.properties | 6 +- 21 files changed, 138 insertions(+), 53 deletions(-) diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index c915ba73..d85ce6a5 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.5.1 + +* **iOS/macOS:** the bundled `Python`, `dart_bridge` and stdlib extension XCFrameworks are now signed on both layers — each slice's inner `.framework` as well as the outer `.xcframework`. 4.5.0 signed only the outer bundle, which left IPA receipts reporting `isSecureTimestamp = false`. See `serious_python_darwin` 4.5.1. +* Packaging now verifies both layers, so an unsigned slice fails the build instead of surfacing in an App Store submission. Set `SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES=require` for release builds. +* Bundled python-build snapshot re-pinned to **20260730** (`dart_bridge` **1.7.0 → 1.7.1**). No Python version moved — **3.12.13 / 3.13.14 / 3.14.6** and Pyodide are unchanged from 20260729. + ## 4.5.0 * **iOS/macOS:** the pre-built `Python`, `dart_bridge` and stdlib extension XCFrameworks are now staged byte-for-byte and never modified. `SERIOUS_PYTHON_BUNDLE_ID` now namespaces only the frameworks built from *your* app's wheels; the pre-built ones keep the stable `dev.flet.*` identifiers their publisher assigned. diff --git a/src/serious_python/README.md b/src/serious_python/README.md index 163732f4..8f73160e 100644 --- a/src/serious_python/README.md +++ b/src/serious_python/README.md @@ -284,6 +284,10 @@ framework with your Apple Distribution identity at embed time and again at single file inside an `.xcframework` — even one `Info.plist` key — invalidates the publisher's signature and turns the receipt back into `signed = false`. +Both layers are signed: each slice's inner `.framework` as well as the outer +`.xcframework`, in that order. Signing only the outer bundle yields a receipt +reading `signed = true` but `isSecureTimestamp = false`. + So serious_python treats `Python.xcframework`, `dart_bridge.xcframework`, and the stdlib extension frameworks as **immutable** once downloaded. They are copied verbatim at every staging step, and each step re-checks a digest manifest recorded diff --git a/src/serious_python/lib/src/python_versions.dart b/src/serious_python/lib/src/python_versions.dart index 8110879d..f79a851b 100644 --- a/src/serious_python/lib/src/python_versions.dart +++ b/src/serious_python/lib/src/python_versions.dart @@ -1,5 +1,5 @@ // GENERATED by `dart run serious_python:gen_version_tables` from python-build's -// manifest.json (release 20260729). Do not edit by hand — edit python-build's +// manifest.json (release 20260730). Do not edit by hand — edit python-build's // manifest.json, cut a release, bump `pythonReleaseDate`, and regenerate. const pythonVersionEnvironmentVariable = "SERIOUS_PYTHON_VERSION"; @@ -10,8 +10,8 @@ const pyodideVersionEnvironmentVariable = "SERIOUS_PYTHON_PYODIDE_VERSION"; const dartBridgeVersionEnvironmentVariable = "DART_BRIDGE_VERSION"; /// python-build release the bundled runtimes come from (YYYYMMDD). -const pythonReleaseDate = "20260729"; -const dartBridgeVersion = "1.7.0"; +const pythonReleaseDate = "20260730"; +const dartBridgeVersion = "1.7.1"; const defaultPythonVersion = "3.14"; class PythonRelease { diff --git a/src/serious_python/pubspec.yaml b/src/serious_python/pubspec.yaml index 77722da3..f31870f6 100644 --- a/src/serious_python/pubspec.yaml +++ b/src/serious_python/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python description: A cross-platform plugin for adding embedded Python runtime to your Flutter apps. homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.5.0 +version: 4.5.1 platforms: ios: diff --git a/src/serious_python_android/CHANGELOG.md b/src/serious_python_android/CHANGELOG.md index 22d1d50e..5fe559c3 100644 --- a/src/serious_python_android/CHANGELOG.md +++ b/src/serious_python_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.1 + +* Re-pins the bundled python-build snapshot to **20260730** and `dart_bridge` to **1.7.1**. Both exist to sign the Apple XCFrameworks on both layers (see `serious_python_darwin` 4.5.1); no Python version moved from 20260729 (**3.12.13 / 3.13.14 / 3.14.6**), and nothing about Android changed. + ## 4.5.0 * Re-pins the bundled python-build snapshot to **20260729** and `dart_bridge` to **1.7.0**. Both exist to provider-sign the Apple XCFrameworks (see `serious_python_darwin` 4.5.0). Nothing about Android changed: no Python version moved from 20260727 (**3.12.13 / 3.13.14 / 3.14.6**), and `dart_bridge`'s Android `.so`s are byte-identical to 1.6.1 — verified, not assumed. diff --git a/src/serious_python_android/android/build.gradle.kts b/src/serious_python_android/android/build.gradle.kts index 4ba1d4a4..dc9027c1 100644 --- a/src/serious_python_android/android/build.gradle.kts +++ b/src/serious_python_android/android/build.gradle.kts @@ -21,7 +21,7 @@ buildscript { } group = "com.flet.serious_python_android" -version = "4.5.0" +version = "4.5.1" rootProject.allprojects { repositories { diff --git a/src/serious_python_android/android/python_versions.properties b/src/serious_python_android/android/python_versions.properties index ea1da9cf..0bdc3bba 100644 --- a/src/serious_python_android/android/python_versions.properties +++ b/src/serious_python_android/android/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260729). Do not edit by hand. +# python-build manifest.json (release 20260730). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.7.0 -python_build_release_date=20260729 +dart_bridge_version=1.7.1 +python_build_release_date=20260730 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_android/pubspec.yaml b/src/serious_python_android/pubspec.yaml index 8f8007af..4a422008 100644 --- a/src/serious_python_android/pubspec.yaml +++ b/src/serious_python_android/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_android description: Android implementation of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.5.0 +version: 4.5.1 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index 31f74ca2..f2c9d794 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,3 +1,10 @@ +## 4.5.1 + +* **iOS/macOS: the bundled XCFrameworks are now signed on both layers — each slice's inner `.framework` as well as the outer `.xcframework`.** 4.5.0 shipped artifacts whose outer bundle was signed but whose inner frameworks were not. An App Store IPA built against it reported `signed = true` — the 4.5.0 fix working — but `isSecureTimestamp = false` for `Python-ios`, `_ssl`, `_hashlib` and `dart_bridge`, in both a development archive and an App Store export. Every slice of an XCFramework Apple's scan demonstrably accepts ([krzyzanowskim/OpenSSL](https://github.com/krzyzanowskim/OpenSSL) 3.6.3000) carries its own Apple Distribution signature with a secure timestamp, with the outer bundle signed last; an unsigned inner framework was the only structural difference left. See flet-dev/python-build#38 and flet-dev/dart-bridge#14. +* **Consumer verification now covers both layers.** `spv_verify_provider` checks each slice's inner framework as well as the outer bundle, so an unsigned slice fails during packaging rather than surfacing in an IPA weeks later. Slice frameworks are located by glob rather than by the xcframework's name, since `stage_spm.sh` stages `Python.xcframework` as `Python-.xcframework` and a name-keyed lookup would silently find nothing there. Signature presence is probed with `codesign -dv` rather than by looking for `_CodeSignature`, because a versioned macOS bundle keeps it under `Versions//` and that name is not always `A` — CPython uses `Versions/3.14`. +* Re-pins the bundled python-build snapshot to **20260730** (`dart_bridge` **1.7.0 → 1.7.1**). No Python version moved — **3.12.13 / 3.13.14 / 3.14.6** and Pyodide are unchanged from 20260729. Verified on the published artifacts: 56 XCFrameworks and 112 slice frameworks in `python-ios-dart-3.14.6`, all signed and securely timestamped by `Apple Distribution: Appveyor Systems Inc. (GXXRQJK434)`. +* python-build 20260730 also fixes the macOS `Python.framework` layout: `Headers` and `Modules` were real directories at the framework root, which codesign rejects for a versioned bundle (`unsealed contents present in the root directory of an embedded framework`). They are now symlinks into `Versions/Current`, as CPython's own framework does for `Headers` and `Resources`. See flet-dev/python-build#39. + ## 4.5.0 * **iOS/macOS: provider-built XCFrameworks are now staged byte-for-byte, and their SDK-origin signatures are verified.** `Python.xcframework`, `dart_bridge.xcframework`, and every stdlib extension framework arrive pre-built from flet-dev/python-build and flet-dev/dart-bridge. serious_python no longer rewrites anything inside them: the `SERIOUS_PYTHON_BUNDLE_ID` pass now applies **only** to frameworks built here from the app's own wheels, which are generated in a separate directory, mutated there, and merged with the untouched provider artifacts afterwards. `reconcile_framework_install_names` (which rewrites Mach-O headers and re-signs ad-hoc) likewise never reaches a provider binary. diff --git a/src/serious_python_darwin/darwin/python_versions.properties b/src/serious_python_darwin/darwin/python_versions.properties index ea1da9cf..0bdc3bba 100644 --- a/src/serious_python_darwin/darwin/python_versions.properties +++ b/src/serious_python_darwin/darwin/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260729). Do not edit by hand. +# python-build manifest.json (release 20260730). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.7.0 -python_build_release_date=20260729 +dart_bridge_version=1.7.1 +python_build_release_date=20260730 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_darwin/darwin/serious_python_darwin.podspec b/src/serious_python_darwin/darwin/serious_python_darwin.podspec index 5bf40acf..6e4ef60e 100644 --- a/src/serious_python_darwin/darwin/serious_python_darwin.podspec +++ b/src/serious_python_darwin/darwin/serious_python_darwin.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'serious_python_darwin' - s.version = '4.5.0' + s.version = '4.5.1' s.summary = 'A cross-platform plugin for adding embedded Python runtime to your Flutter apps.' s.description = <<-DESC A cross-platform plugin for adding embedded Python runtime to your Flutter apps. diff --git a/src/serious_python_darwin/darwin/xcframework_verify.sh b/src/serious_python_darwin/darwin/xcframework_verify.sh index 34d76162..abafce85 100644 --- a/src/serious_python_darwin/darwin/xcframework_verify.sh +++ b/src/serious_python_darwin/darwin/xcframework_verify.sh @@ -82,7 +82,74 @@ spv_each() { return 0 } -# Verify the provider signature on every xcframework under the given roots. +# Emit the per-slice .framework bundles inside an xcframework. +# +# Globbed rather than derived from the xcframework's name: stage_spm.sh stages +# Python.xcframework as Python-.xcframework, and a name-keyed lookup +# would quietly find nothing there — turning "this slice is unsigned" into "there +# was nothing to check". +spv_slice_frameworks() { + for _spv_slice in "$1"/*/; do + [ -d "$_spv_slice" ] || continue + for _spv_fw in "$_spv_slice"*.framework; do + if [ -d "$_spv_fw" ]; then printf '%s\n' "$_spv_fw"; fi + done + done + return 0 +} + +# Assert one signed bundle carries a usable provider signature: verifiable, not +# ad-hoc, securely timestamped, and (when configured) the expected team. +spv_assert_signature() { + _spv_t=$1 + + # `codesign -dv` rather than probing for a _CodeSignature directory: a + # versioned macOS bundle keeps it at Versions//_CodeSignature and the + # version directory is not always "A" (CPython uses e.g. Versions/3.14). + if ! codesign -dv "$_spv_t" >/dev/null 2>&1; then + spv_bad "$_spv_t: not signed at all;" \ + "its IPA receipt will report signed = false" || return 1 + return 0 + fi + + if ! _spv_out=$(codesign --verify --strict --verbose=4 "$_spv_t" 2>&1); then + spv_bad "$_spv_t: provider signature does not verify: $_spv_out" || return 1 + return 0 + fi + + _spv_info=$(codesign -dvvv "$_spv_t" 2>&1) || _spv_info="" + + if printf '%s\n' "$_spv_info" | grep -q '^Signature=adhoc'; then + spv_bad "$_spv_t: ad-hoc signature, not a provider signature" || return 1 + return 0 + fi + + if ! printf '%s\n' "$_spv_info" | grep -q '^Timestamp='; then + spv_bad "$_spv_t: no secure timestamp;" \ + "its IPA receipt will report isSecureTimestamp = false" || return 1 + return 0 + fi + + if [ -n "${SERIOUS_PYTHON_EXPECTED_TEAM_ID:-}" ]; then + _spv_team=$(printf '%s\n' "$_spv_info" | sed -n 's/^TeamIdentifier=//p' | head -1) + if [ "$_spv_team" != "$SERIOUS_PYTHON_EXPECTED_TEAM_ID" ]; then + spv_bad "$_spv_t: TeamIdentifier '$_spv_team' !=" \ + "expected '$SERIOUS_PYTHON_EXPECTED_TEAM_ID'" || return 1 + return 0 + fi + fi + return 0 +} + +# Verify the provider signature on every xcframework under the given roots — +# BOTH the outer bundle and each slice's inner .framework. +# +# Both layers matter. An XCFramework whose outer bundle is signed but whose inner +# frameworks are not produces an IPA receipt reading `signed = true` but +# `isSecureTimestamp = false`; every slice of an XCFramework Apple's App Store +# scan accepts is signed in its own right. Checking only the outer seal would let +# that regression reach a submission unnoticed, which is exactly how it reached +# one before. # # Roots must name PROVIDER artifacts only. Frameworks this plugin generates from # the app's own site-packages are built locally, carry an ad-hoc signature, and @@ -95,44 +162,27 @@ spv_verify_provider() { _spv_status=0 _spv_count=0 + _spv_slices=0 _spv_problems=0 for _spv_root in "$@"; do [ -e "$_spv_root" ] || continue for _spv_xcf in $(spv_each "$_spv_root"); do _spv_count=$((_spv_count + 1)) + # Outer bundle. Checked by file rather than via codesign so an + # entirely unsigned XCFramework reports the useful message. if [ ! -f "$_spv_xcf/_CodeSignature/CodeResources" ]; then spv_bad "$_spv_xcf: no provider signature (unsigned XCFramework);" \ "its IPA receipt will report signed = false" || _spv_status=1 continue fi + spv_assert_signature "$_spv_xcf" || _spv_status=1 - if ! _spv_out=$(codesign --verify --strict --verbose=4 "$_spv_xcf" 2>&1); then - spv_bad "$_spv_xcf: provider signature does not verify: $_spv_out" || _spv_status=1 - continue - fi - - _spv_info=$(codesign -dvvv "$_spv_xcf" 2>&1) || _spv_info="" - - if printf '%s\n' "$_spv_info" | grep -q '^Signature=adhoc'; then - spv_bad "$_spv_xcf: ad-hoc signature, not a provider signature" || _spv_status=1 - continue - fi - - if ! printf '%s\n' "$_spv_info" | grep -q '^Timestamp='; then - spv_bad "$_spv_xcf: no secure timestamp;" \ - "its IPA receipt will report isSecureTimestamp = false" || _spv_status=1 - continue - fi - - if [ -n "${SERIOUS_PYTHON_EXPECTED_TEAM_ID:-}" ]; then - _spv_team=$(printf '%s\n' "$_spv_info" | sed -n 's/^TeamIdentifier=//p' | head -1) - if [ "$_spv_team" != "$SERIOUS_PYTHON_EXPECTED_TEAM_ID" ]; then - spv_bad "$_spv_xcf: TeamIdentifier '$_spv_team' !=" \ - "expected '$SERIOUS_PYTHON_EXPECTED_TEAM_ID'" || _spv_status=1 - continue - fi - fi + # Each slice's inner framework, in its own right. + for _spv_inner in $(spv_slice_frameworks "$_spv_xcf"); do + _spv_slices=$((_spv_slices + 1)) + spv_assert_signature "$_spv_inner" || _spv_status=1 + done done done @@ -140,10 +190,12 @@ spv_verify_provider() { # An empty tree must never read as "everything passed". spv_bad "no *.xcframework found under: $*" || _spv_status=1 elif [ "$_spv_problems" -gt 0 ]; then - spv_note "$_spv_problems of $_spv_count provider xcframework(s) lack a usable" \ - "SDK-origin signature; the resulting IPA will report them as unsigned" + spv_note "$_spv_problems signature problem(s) across $_spv_count provider" \ + "xcframework(s) and $_spv_slices slice framework(s); the resulting IPA" \ + "will report them as unsigned or without a secure timestamp" else - spv_note "provider signatures verified on $_spv_count xcframework(s)" + spv_note "provider signatures verified on $_spv_count xcframework(s)" \ + "and $_spv_slices slice framework(s)" fi return $_spv_status } diff --git a/src/serious_python_darwin/pubspec.yaml b/src/serious_python_darwin/pubspec.yaml index 8b036be9..a2d93ae1 100644 --- a/src/serious_python_darwin/pubspec.yaml +++ b/src/serious_python_darwin/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_darwin description: iOS and macOS implementations of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.5.0 +version: 4.5.1 environment: # The Swift Package Manager build path needs Flutter 3.44 / Dart 3.11 (the diff --git a/src/serious_python_linux/CHANGELOG.md b/src/serious_python_linux/CHANGELOG.md index 0953aca4..099cf562 100644 --- a/src/serious_python_linux/CHANGELOG.md +++ b/src/serious_python_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.1 + +* Re-pins the bundled python-build snapshot to **20260730** and `dart_bridge` to **1.7.1**. Both exist to sign the Apple XCFrameworks on both layers (see `serious_python_darwin` 4.5.1); no Python version moved from 20260729 (**3.12.13 / 3.13.14 / 3.14.6**), and nothing about Linux changed. + ## 4.5.0 * Re-pins the bundled python-build snapshot to **20260729** and `dart_bridge` to **1.7.0**. Both exist to provider-sign the Apple XCFrameworks (see `serious_python_darwin` 4.5.0). Nothing about Linux changed: no Python version moved from 20260727 (**3.12.13 / 3.13.14 / 3.14.6**), and `libdart_bridge-linux-*.so` is byte-identical to 1.6.1 — verified, not assumed. diff --git a/src/serious_python_linux/linux/python_versions.properties b/src/serious_python_linux/linux/python_versions.properties index ea1da9cf..0bdc3bba 100644 --- a/src/serious_python_linux/linux/python_versions.properties +++ b/src/serious_python_linux/linux/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260729). Do not edit by hand. +# python-build manifest.json (release 20260730). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.7.0 -python_build_release_date=20260729 +dart_bridge_version=1.7.1 +python_build_release_date=20260730 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_linux/pubspec.yaml b/src/serious_python_linux/pubspec.yaml index 925b8a2a..17683d1d 100644 --- a/src/serious_python_linux/pubspec.yaml +++ b/src/serious_python_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_linux description: Linux implementations of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.5.0 +version: 4.5.1 environment: sdk: '>=3.1.3 <4.0.0' diff --git a/src/serious_python_platform_interface/CHANGELOG.md b/src/serious_python_platform_interface/CHANGELOG.md index c6e13e09..54336b7b 100644 --- a/src/serious_python_platform_interface/CHANGELOG.md +++ b/src/serious_python_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.1 + +* Version bump aligning with the `serious_python_*` 4.5.1 release. + ## 4.5.0 * Version bump aligning with the `serious_python_*` 4.5.0 release. diff --git a/src/serious_python_platform_interface/pubspec.yaml b/src/serious_python_platform_interface/pubspec.yaml index 3ae8281a..dedef6ba 100644 --- a/src/serious_python_platform_interface/pubspec.yaml +++ b/src/serious_python_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_platform_interface description: A common platform interface for the serious_python plugin. homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.5.0 +version: 4.5.1 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/src/serious_python_windows/CHANGELOG.md b/src/serious_python_windows/CHANGELOG.md index aee2da86..34f29de1 100644 --- a/src/serious_python_windows/CHANGELOG.md +++ b/src/serious_python_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.5.1 + +* Re-pins the bundled python-build snapshot to **20260730** and `dart_bridge` to **1.7.1**. Both exist to sign the Apple XCFrameworks on both layers (see `serious_python_darwin` 4.5.1); no Python version moved from 20260729 (**3.12.13 / 3.13.14 / 3.14.6**), and nothing about Windows changed. + ## 4.5.0 * Re-pins the bundled python-build snapshot to **20260729** and `dart_bridge` to **1.7.0**. Both exist to provider-sign the Apple XCFrameworks (see `serious_python_darwin` 4.5.0); no Python version moved from 20260727 (**3.12.13 / 3.13.14 / 3.14.6**). `dart_bridge.dll` is rebuilt rather than byte-identical — MSVC stamps a build timestamp and PDB GUID into every DLL — but no Windows source changed between 1.6.1 and 1.7.0. diff --git a/src/serious_python_windows/pubspec.yaml b/src/serious_python_windows/pubspec.yaml index 40191744..20432051 100644 --- a/src/serious_python_windows/pubspec.yaml +++ b/src/serious_python_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: serious_python_windows description: Windows implementations of the serious_python plugin homepage: https://flet.dev repository: https://github.com/flet-dev/serious-python -version: 4.5.0 +version: 4.5.1 environment: sdk: '>=3.1.3 <4.0.0' diff --git a/src/serious_python_windows/windows/python_versions.properties b/src/serious_python_windows/windows/python_versions.properties index ea1da9cf..0bdc3bba 100644 --- a/src/serious_python_windows/windows/python_versions.properties +++ b/src/serious_python_windows/windows/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260729). Do not edit by hand. +# python-build manifest.json (release 20260730). Do not edit by hand. default_python_version=3.14 -dart_bridge_version=1.7.0 -python_build_release_date=20260729 +dart_bridge_version=1.7.1 +python_build_release_date=20260730 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 From aa078588ac45afcfd75854609b1927b9d7653d3c Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 30 Jul 2026 16:40:18 -0700 Subject: [PATCH 2/2] Record that inner signing did not flip isSecureTimestamp The 4.5.1 entry implied that signing both layers was the fix for `isSecureTimestamp = false`. It is not, and shipping that claim unamended would be wrong. With both layers signed, the App Store receipts still read `signed = true` / `isSecureTimestamp = false`. Three findings pin this down: * The receipt's cdhash matches the OUTER xcframework's CDHash exactly (dart_bridge 582388d2..., _ssl df6220fd...), not the inner framework's. Xcode reads the outer signature -- the one that carries the TSA timestamp -- and reports false anyway. * Decoding the CMS in _CodeSignature/CodeSignature shows a genuine RFC 3161 id-smime-aa-timeStampToken (1.2.840.113549.1.9.16.2.14) unsigned attribute, not merely a self-asserted signingTime. * That CMS attribute set is structurally identical to the accepted OpenSSL artifact's, down to the Apple certificate extensions -- so there is no observable difference left between a signature Apple accepts and ours. Every receipt in the archive reports false, and all record signatureType = AppleDeveloperProgram; the accepted artifact is signed by an Apple Distribution identity too and would carry the same type. The reasonable reading is that ITMS-91065 tests `signed`, which is now true, and that isSecureTimestamp is not reachable by signing. Signing both layers is kept regardless: it matches what an accepted artifact does, and it is correct practice for a distributed binary framework. No code changes -- changelog only. --- src/serious_python/CHANGELOG.md | 2 +- src/serious_python_darwin/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index d85ce6a5..8e4d189a 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -1,6 +1,6 @@ ## 4.5.1 -* **iOS/macOS:** the bundled `Python`, `dart_bridge` and stdlib extension XCFrameworks are now signed on both layers — each slice's inner `.framework` as well as the outer `.xcframework`. 4.5.0 signed only the outer bundle, which left IPA receipts reporting `isSecureTimestamp = false`. See `serious_python_darwin` 4.5.1. +* **iOS/macOS:** the bundled `Python`, `dart_bridge` and stdlib extension XCFrameworks are now signed on both layers — each slice's inner `.framework` as well as the outer `.xcframework`. 4.5.0 signed only the outer bundle. Note this did **not** change `isSecureTimestamp`, which still reports false — that field appears not to be reachable by signing; `signed`, which `ITMS-91065` names, is true. See `serious_python_darwin` 4.5.1. * Packaging now verifies both layers, so an unsigned slice fails the build instead of surfacing in an App Store submission. Set `SERIOUS_PYTHON_VERIFY_PROVIDER_SIGNATURES=require` for release builds. * Bundled python-build snapshot re-pinned to **20260730** (`dart_bridge` **1.7.0 → 1.7.1**). No Python version moved — **3.12.13 / 3.13.14 / 3.14.6** and Pyodide are unchanged from 20260729. diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index f2c9d794..bbe8c163 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,6 +1,7 @@ ## 4.5.1 * **iOS/macOS: the bundled XCFrameworks are now signed on both layers — each slice's inner `.framework` as well as the outer `.xcframework`.** 4.5.0 shipped artifacts whose outer bundle was signed but whose inner frameworks were not. An App Store IPA built against it reported `signed = true` — the 4.5.0 fix working — but `isSecureTimestamp = false` for `Python-ios`, `_ssl`, `_hashlib` and `dart_bridge`, in both a development archive and an App Store export. Every slice of an XCFramework Apple's scan demonstrably accepts ([krzyzanowskim/OpenSSL](https://github.com/krzyzanowskim/OpenSSL) 3.6.3000) carries its own Apple Distribution signature with a secure timestamp, with the outer bundle signed last; an unsigned inner framework was the only structural difference left. See flet-dev/python-build#38 and flet-dev/dart-bridge#14. +* **This did not change `isSecureTimestamp`, and that appears not to be something signing can change.** With both layers signed, the receipts still read `signed = true` / `isSecureTimestamp = false`. The receipt's `cdhashes` entry matches the **outer** xcframework's CDHash exactly — so Xcode reads the outer signature, which carries a genuine Apple TSA `Timestamp=`, and reports `isSecureTimestamp = false` anyway. Every receipt in the archive reports false, and the receipts record `signatureType = AppleDeveloperProgram`; the accepted OpenSSL artifact is signed by an Apple Distribution identity too, so it would carry the same type. Decoding the CMS blob in `_CodeSignature/CodeSignature` settles it: our signature carries a genuine RFC 3161 `id-smime-aa-timeStampToken` (1.2.840.113549.1.9.16.2.14) unsigned attribute — a real TSA token, not just a self-asserted `signingTime` — and its attribute set is structurally identical to the accepted OpenSSL artifact's, down to the Apple certificate extensions. There is no observable difference left between the two signatures, so `isSecureTimestamp = false` cannot be caused by anything in ours. The reasonable reading is that `ITMS-91065: Missing signature` tests `signed`, which is now true, and that `isSecureTimestamp` is not reachable for a Developer Program identity. Signing both layers is kept regardless: it matches what an accepted artifact does, and it is correct practice for a distributed binary framework. * **Consumer verification now covers both layers.** `spv_verify_provider` checks each slice's inner framework as well as the outer bundle, so an unsigned slice fails during packaging rather than surfacing in an IPA weeks later. Slice frameworks are located by glob rather than by the xcframework's name, since `stage_spm.sh` stages `Python.xcframework` as `Python-.xcframework` and a name-keyed lookup would silently find nothing there. Signature presence is probed with `codesign -dv` rather than by looking for `_CodeSignature`, because a versioned macOS bundle keeps it under `Versions//` and that name is not always `A` — CPython uses `Versions/3.14`. * Re-pins the bundled python-build snapshot to **20260730** (`dart_bridge` **1.7.0 → 1.7.1**). No Python version moved — **3.12.13 / 3.13.14 / 3.14.6** and Pyodide are unchanged from 20260729. Verified on the published artifacts: 56 XCFrameworks and 112 slice frameworks in `python-ios-dart-3.14.6`, all signed and securely timestamped by `Apple Distribution: Appveyor Systems Inc. (GXXRQJK434)`. * python-build 20260730 also fixes the macOS `Python.framework` layout: `Headers` and `Modules` were real directories at the framework root, which codesign rejects for a versioned bundle (`unsealed contents present in the root directory of an embedded framework`). They are now symlinks into `Versions/Current`, as CPython's own framework does for `Headers` and `Resources`. See flet-dev/python-build#39.