Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/serious_python/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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. 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.

## 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.
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python/lib/src/python_versions.dart
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_android/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ buildscript {
}

group = "com.flet.serious_python_android"
version = "4.5.0"
version = "4.5.1"

rootProject.allprojects {
repositories {
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_android/android/python_versions.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 8 additions & 0 deletions src/serious_python_darwin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 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-<platform>.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/<name>/` 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.
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_darwin/darwin/python_versions.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
112 changes: 82 additions & 30 deletions src/serious_python_darwin/darwin/xcframework_verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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-<platform>.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/<name>/_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
Expand All @@ -95,55 +162,40 @@ 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

if [ "$_spv_count" -eq 0 ]; then
# 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
}
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_darwin/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_linux/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_linux/linux/python_versions.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_linux/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_platform_interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions src/serious_python_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/serious_python_windows/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
6 changes: 3 additions & 3 deletions src/serious_python_windows/windows/python_versions.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading