Skip to content

Add CodePushCore Swift package for JWT signature verification - #94

Open
ofalvai wants to merge 1 commit into
masterfrom
codepushcore-jwt-swift
Open

ofalvai wants to merge 1 commit into
masterfrom
codepushcore-jwt-swift

Conversation

@ofalvai

@ofalvai ofalvai commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Part of the work enabling Swift Package Manager distribution (community RFC)

The iOS SDK verifies code-signed updates with the JWT (~> 3.0.0-beta.12) and Base64 (~> 1.1) pods. These two pods block SPM support (JWT is only packaged as a Pod, no Package.swift), and a security library pinned to 3.0.0-beta.12 from 2019 is a scary situation (the library is abandoned anyway, last release is from 2021).

Base64 was only there because JWT.podspec depends on it, no CodePush source used it.

The stack

  1. Add CodePushCore Swift package for JWT signature verification #94 (this PR): add a Swift verifier in a separate package. Nothing calls it yet.
  2. Replace JWT/Base64 pods with CodePushCore Swift verifier #95: use it in the code-signing path, and remove the two pods.
  3. Remove vendored JWT/Base64 pod sources #96: delete the vendored JWT/Base64 sources that nothing compiles.
  4. Add a Swift Package Manager manifest for React Native SPM apps #97: add a Package.swift for React Native SPM apps.

What this PR adds

ios/CodePushCore/ is a Swift package with the standard SPM layout. It imports only Foundation and Security, so it's unit testable directly, there is no dependency on React Native.

Most of our JWT pod usage can be replaced with calls into the Security framework's SecKeyCreateWithData() and SecKeyVerifySignature() (check code comments for more details). We need to add a little "hand-rolled" code to parse the public key and the JWT token format, but these are ~100 lines total. Needless to say, I ran this PR through many rounds of LLM-assisted security reviews.

Decisions

Why not a third-party JWT library

A candidate had to be on CocoaPods trunk and support SPM. State in October 2026:

Library On trunk Verdict
JOSESwift yes Maintained (3.0.0, 2024-08). The only real option, see below.
Kitura Swift-JWT yes Last release 2022, Kitura is dead.
SwiftyRSA yes Last trunk release 2021. Its key import is its own ASN.1 parser with no OID check.
ASN1Decoder yes General DER decoder. We would still write the SPKI walk.
SwiftASN1 pod yes Unofficial repackage of apple/swift-asn1, several versions behind. Not on a crypto path.
vapor/jwt-kit no A pod cannot depend on an SPM package.
apple/swift-crypto no Same, and RSA is only in the unstable _CryptoExtras.

JOSESwift does not remove the part that matters. Its verifier takes a SecKey, so we would still call SecKeyCreateWithData ourselves. It would replace about 80 lines (split, base64url decode, pin alg, call verify) with a dependency plus an adapter. The crypto call is the same in both cases: one SecKeyVerifySignature call into Apple's corecrypto.

For comparison, Android uses com.nimbusds:nimbus-jose-jwt. JVM has a good library with no modularity problem. CocoaPods has no equivalent.

What input does an attacker control?
  • The token: yes. It comes from the downloaded bundle. Foundation does the parsing (components(separatedBy:), Data(base64Encoded:), JSONSerialization). Our own logic on this path is about 20 lines: the RS256 pin, and the order (signature before payload). The tests cover the two attacks that matter, alg: none and HS256 with the public key as the secret.
  • The key: no. It comes only from CodePushPublicKey in Info.plist. CodePushConfig.m has no setter for it on purpose. A malformed key fails closed. An attacker who can edit Info.plist already controls the app.
Is SecKeyCreateWithData safe to rely on down to iOS 15.5?
  • Apple DTS documents the SPKI unwrapping in Importing Cryptographic Keys (2021, the iOS 14 era).
  • In apple-oss-distributions/Security, SecRSAPublicKeyInit is the same from Security-59306 (iOS 13) to Security-61901 (iOS 27). It hands the bytes to corecrypto's ccrsa_import_pub.
  • The tests and a probe pass on the iOS 17.0 and iOS 27.0 simulators and on macOS 27.
Why ios/CodePushCore/ and not Swift files in ios/CodePush/

SwiftPM rejects a target that mixes Swift and C-family sources. #97 builds ios/CodePush/ as an Objective-C target, so Swift must live in its own target. The podspec globs both directories, so CocoaPods consumers get one pod as before. Future Swift that needs React or Objective-C types needs a new Swift-only target that depends on ReactHeaders.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings October 27, 2026 20:52
@ofalvai
ofalvai added this pull request to stack #98 October 27, 2026 20:52
@ofalvai ofalvai changed the title codepushcore jwt swift Add CodePushCore Swift package for JWT signature verification Oct 27, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The verifier’s security-critical key import and rejection assumptions require validation on supported Apple runtimes, but this review environment could not run the new XCTest suite.

No open issues 🎉

What changed in this PR

Adds a standalone CodePushCore Swift package for verifying CodePush release JWT signatures with Apple’s Security framework, independently of React Native.

Changes:

  • Adds RS256 signature verification, RSA public-key parsing, and descriptive verification errors.
  • Adds tests for valid signatures, supported key formats, tampered tokens, and invalid inputs.
  • Integrates core tests into the iOS unit-test command and ignores Swift Package Manager artifacts.
File Description
package.json Runs existing iOS tests and the new core test suite.
ios/​CodePushCore/​Tests/​CodePushCoreTests/​JWTVerifierTests.swift Tests signature verification and rejection of invalid tokens and keys.
ios/​CodePushCore/​Sources/​CodePushCore/​SignatureVerification/​RSAPublicKey.swift Imports PEM or bare-base64 RSA public keys.
ios/​CodePushCore/​Sources/​CodePushCore/​SignatureVerification/​JWTVerifier.swift Verifies RS256 signatures and returns decoded payloads through an Objective-C-compatible API.
ios/​CodePushCore/​Sources/​CodePushCore/​SignatureVerification/​JWTVerificationError.swift Defines verification errors and their descriptions.
ios/​CodePushCore/​Package.swift Declares the standalone library and test target.
.gitignore Excludes Swift Package Manager build and workspace artifacts.

Review effort: Balanced

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.

3 participants