79% vs 51%: independent neobanks lag on app security. Read the report

Security

Security

How FairPlay Protects iOS App Binaries, and an Account-Level Weakness

How Apple FairPlay encrypts iOS app binaries, what is documented, and why one reusable account credential unwraps every app it owns.

TL;DR

  • FairPlay encrypts the range of an iOS Mach-O binary declared by its LC_ENCRYPTION_INFO or LC_ENCRYPTION_INFO_64 load command. The kernel decrypts the range in 4 KB chunks as pages are faulted in, and the decrypted pages are never written back to the file. That is why the old way to analyze an App Store app was to dump its memory while it ran.
  • Each IPA also carries an SC_Info license. The SINF record wraps a 16-byte per-app work key, and the SUPF record supplies a 16-byte wrapped value and a 16-byte IV for every 4 KB page. The page key is derived from the work key, the page record and the page's file offset.
  • The public EthanArbuckle/fairplay-decrypt tool shows that the hierarchy collapses to one reusable 16-byte credential. The credential is extracted once from a jailbroken device; after that, decryption is offline, deterministic and needs no device.
  • The weakness is not a broken cipher. It is a bearer-key design: the credential unwraps each app's distinct work key, and the per-page keys are derived from that work key rather than being independent secrets. One credential therefore unwraps the work key of every app the account or enrollment owns.
  • A second project, DrWhax/unfairplay, recovers an equivalent model from an enrolled Mac and decrypts multiple IPAs on macOS or Linux. Its README states the model is tied to the enrollment, not to one app.
  • We found no CVE assigned. A keyword search of the NVD returned no fairplay record on 2026-11-12, and the CISA KEV catalog version 2026.11.10 has no FairPlay entry. This is a DRM circumvention technique that needs a jailbroken device or an enrolled Mac, not a remote exploit.
  • Treat the credential or model as an account secret. If you acquire IPAs for testing, use a dedicated account, keep the credential encrypted and access-logged, and delete it when the engagement ends. If you build an app, do not rely on FairPlay to keep code or long-lived secrets confidential.

Why does an iOS binary need decrypting at all?

Automated iOS testing breaks at the first step: getting the binary. Static analysis, dependency detection, string extraction and vulnerability scanning all need the Mach-O executable in the clear. The IPA that the App Store delivers is encrypted, and the decrypted pages exist only while the app runs.

The traditional workaround is a jailbroken device. The analyst launches the app, waits for the kernel to decrypt its pages, and dumps the process memory. OWASP's MASTG page on obtaining and extracting apps describes that memory-dump approach and names frida-ios-dump. It needs a physical device for every analysis, depends on jailbreak support for the iOS version, and is difficult to make repeatable or auditable.

The offline path changes the economics. Once a reusable credential exists, decryption becomes a function of the IPA and the credential. The device is needed to obtain the credential, not to decrypt each app. That is the operational shift this article explains, and the reason the account-level scope matters.

What does Apple document about FairPlay?

Apple documents code signing, not the confidentiality of app code. Its Platform Security guide says that on iOS, iPadOS, tvOS, visionOS and watchOS, "these operating systems require that all executable code be signed using an Apple-issued certificate" (Apple, App code signing process, published 2024-12-19, guide updated August 2026). Code signing proves who built the code and that it was not altered. It does not hide the code, and re-signing an app does not remove FairPlay encryption.

The code-signing page and the runtime-process page we checked do not describe FairPlay or the encryption of app binaries. We did not find an Apple page that documents the scheme. The kernel side is public in the XNU source, but the key derivation, the SINF and SUPF formats, and the account credential are reverse-engineered community knowledge. Each claim below names its source.

What are the three layers of FairPlay protection?

FairPlay is best understood as three layers with different jobs. Code signing protects integrity. The Mach-O encryption protects the bytes at rest. The SC_Info license and the device's FairPlay services decide who may decrypt which app.

flowchart TD A["IPA from the App Store"] --> B["Layer 1: code signature"] A --> C["Layer 2: encrypted Mach-O range (cryptid = 1)"] A --> D["Layer 3: SC_Info license (SINF, SUPF)"] D --> E["Device: fairplayd and FairPlayIOKit derive the work key and page keys"] C --> F["Kernel pager decrypts 4 KB chunks on page-in"] E --> F F --> G["Plaintext exists only in process memory"]

Layer 1: code signing

Code signing is the integrity layer. It binds the binary to an Apple-issued certificate and a team identifier, and the kernel refuses to execute code that fails validation. It says nothing about whether a reader can recover the instructions. An attacker who copies an IPA still has the encrypted bytes; an attacker who removes the signature still has the FairPlay encryption.

Layer 2: encrypted pages in the Mach-O

The Mach-O header can carry LC_ENCRYPTION_INFO or LC_ENCRYPTION_INFO_64. The command stores cryptoff (the file offset of the encrypted range), cryptsize (its size) and cryptid (which encryption system applies); the structure is in Apple's Mach-O loader header. A cryptid of 1 means the range is encrypted; 0 means the command is empty and the range is not encrypted. SensePost's 2011 decrypting iPhone apps write-up shows the same fields and the memory-dump workflow they enabled on early iOS.

Apple publishes the kernel side. In mach_loader.c, parse_machfile() handles both encryption commands in one case and calls set_code_unprotect(). If that returns LOAD_DECRYPTFAIL, the kernel records EXEC_EXIT_REASON_FAIRPLAY_DECRYPT ("failed to load due to missing FP keys") and kills a non-spawn exec with SIGKILL. In vm_apple_protect.c, the decryption runs in an external memory manager that "handles memory from the encrypted sections of some executables protected by the DSMOS kernel extension". On page-in it decrypts 4 KB chunks through a page_decrypt callback, and the source says "the decrypted pages will never be dirtied", so the originals stay clean and the plaintext lives only in memory.

That last property is the reason memory dumping worked for years. It is also the property the offline decryptor reproduces: it computes the same page keys and writes the plaintext into a copy of the file.

Layer 3: the license and key material

An App Store IPA carries an SC_Info/ directory. Noham's analysis of a cracked iOS app describes it as where "Apple's FairPlay DRM stores iTunes Store licensing metadata", with Manifest.plist, .sinf, .supf, .supp and .supx files, and notes that "Not much is documented publicly about the internal FairPlay file formats". The roles below therefore come from the public tooling and third-party analysis, not from an Apple specification.

The SINF is an MPEG-4-style atom tree. The fields that matter here are:

  • user and key: two 4-byte selectors that identify the account and license record.
  • iviv: a 16-byte value used with the app name to derive an IV.
  • priv: a 440-byte or 448-byte record that wraps the app's 16-byte work key.
  • name and sign: the app name and a signature.

The SUPF is the page table. The fairplay-decrypt README says it supports version-3 and version-5 SUPF, 448-byte and 440-byte SINF priv records, and encrypted arm64 Mach-O slices. In the code, a version-3 SUPF has a 32-byte per-architecture header and a version-5 SUPF has a 12-byte header; each page then gets a 32-byte record made of a 16-byte wrapped value followed by a 16-byte IV (core.py).

The license is the bridge between the account and the app. The SINF binds the app to an account and a license selector; the SUPF describes how to derive the page keys once the app's work key is known. The next section shows how the public tool turns those two files into plaintext.

How does the offline decryptor recover an app key?

The tool has two stages. The first extracts a reusable credential from a jailbroken device. The second uses that credential, the SINF and the SUPF to decrypt the IPA on an ordinary host. This section is our reading of the public code; Apple publishes none of it.

Stage 1: extract one 16-byte credential from a device

The credential-retrieval component supports iOS 10 and later and needs a paired, jailbroken device with root SSH access and at least one installed app from the account (credential-retrieval README). The retriever:

  1. Attaches to fairplayd with task_for_pid() and installs hardware breakpoints on IOConnectCallStructMethod (observer.c). It preserves other debuggers' watchpoints and works around a debug-register reload issue in XNU 8792 noted in the source.
  2. Runs a trigger that maps an encrypted executable and calls mremap_encrypted(), asking the kernel to set up FairPlay decryption for that executable. The trigger's own comment says it requests "FairPlay's first-use path without running or copying decrypted code" (trigger.c). This is why the extraction does not need to execute the target app.
  3. Reads the device's IC-Info.sidb, decrypts it with a key derived from the device UDID, and selects the SINF's user and key record (credential.c).
  4. Combines the 16-byte value observed from the FairPlay call with a context derived from the UDID, the account and the key selector, plus a 4 KB word table the tool ships, and writes the result as credential.bin.

The device is needed for this derivation. The important part is what happens next: the 16-byte output is a standalone value. Decryption never calls the device, fairplayd or FairPlayIOKit again.

The retrieval code carries version-specific knowledge. It chooses FairPlay selector 9 or 21 depending on the iOS version, and it has comments tied to iOS 16.3.1 for the modern UDID format. The README states iOS 10 and later, but this is a moving target: a change in fairplayd can break the retriever without changing the decryption format.

Stage 2: derive the work key and every page key offline

The offline decryptor is pure Python and runs on macOS and Linux (README). It opens the IPA as a ZIP, pairs each .supf or .v5.supf with its binary and SINF, and skips binaries that have no encrypted arm64 slice. For each encrypted slice it does three things.

First, it unwraps the 16-byte work key from the SINF priv record:

iv = MD5(name || iviv)
work = AES-CBC-decrypt(credential XOR bytes([0x30] * 16), iv, priv)[24:40]

The code checks that the decrypted record begins with itun, contains key at offset 20 and iviv at offset 44, and then takes the 16-byte key at offset 24. If the credential is wrong, the marker check fails and the tool stops; there is no attempt to brute-force the key (core.py).

Second, it derives a page key for every 4 KB page. The SUPF record gives a 16-byte wrapped value and a 16-byte IV. The code builds three 16-byte blocks from the wrapped value and the page's absolute file offset, encrypts each with AES-128-ECB under the work key, and runs the three 16-byte results through a custom mixing function built from 16-bit additions, multiplications and XORs. The output is the page key. The page offset is part of the input, so a page record moved to a different offset produces a different key.

Third, it decrypts the page with AES-128-CBC using that page key and the SUPF IV, writes the plaintext into the output copy, and sets cryptid to 0. The result is an IPA whose executable pages are in the clear (core.py, decrypt.py).

The whole pipeline is deterministic. There is no random nonce from Apple, no server challenge, no device attestation and no per-launch secret. The only secret input is the 16-byte credential; everything else comes from the IPA.

Where does the key hierarchy fail?

The hierarchy fails at the root, not in the cipher. One 16-byte credential unwraps each app's work key, and the work key derives every page key for that app. The per-page keys provide domain separation: a key for one page cannot decrypt a different page at a different offset. They do not provide independent secrecy, because they all come from the same work key.

Input What it provides Secret?
16-byte credential unwraps each app's SINF priv record yes, the root
SINF priv the app's 16-byte work key, encrypted encrypted
SINF name, iviv IV derivation for the priv record no
SUPF 32-byte page record wrapped value and IV for one page no
Mach-O cryptoff, cryptsize page range and absolute page index no
work key input to every page-key derivation yes, per app
page key AES-CBC key for one 4 KB page derived

Three properties turn that table into an account-level weakness.

One credential covers many apps. Each app has its own SINF and its own work key, but the same credential unwraps every one of them. UnfairPlay states the consequence plainly: "One model can decrypt different IPAs personalized for the same enrollment. Each app still has its own key inside its personalized SINF." The per-app keys limit the damage of a single leaked work key; they do not limit the damage of a leaked credential.

The derivation is deterministic and offline. Once the credential exists, the IPA contains everything else: the wrapped work key, the page records, the IVs and the offsets. There is no second factor at decryption time. A copy of the credential on a laptop, a CI runner or a backup is as good as the original.

The credential is a bearer value. The repository contains no expiry or revocation logic. Whether Apple invalidates the material server-side is not established by the public code. The tool's own README warns that the credential is for the account that owns the IPA, and UnfairPlay tells users to keep the model private and back it up only in encrypted storage. That is the right advice, and it is also a description of the risk: anyone who holds the value can use it.

What is not the weakness matters too. AES-128-CBC is not broken. The MD5 call derives an IV from public values; it is not the confidentiality boundary. The custom mixer is obscure, not a security proof, and the public code reimplements it. Apple's obfuscation of fairplayd raises the cost of extraction: Nicolò Altamura's static analysis of macOS 13.5.1 describes opaque predicates, control-flow flattening and mixed boolean-arithmetic constants, and rates the opaque predicates as weak while finding the control-flow flattening effective. Obfuscation changes how hard the credential is to obtain. It does not change what the credential can do once obtained.

How far can one credential go?

The first step is one app. The second is the account's library. The third is an automated pipeline. Each step is a consequence of the same design.

  • One app to many. A credential works for apps from the account or enrollment it came from. Regular App Store apps and Apple Arcade apps use separate credentials, according to the fairplay-decrypt README, so an Arcade library needs its own extraction. Within one class, the credential is not tied to a bundle identifier.
  • iPhone to Mac. UnfairPlay recovers its model from an enrolled Mac, not a jailbroken iPhone. The README says the model is tied to the Mac App Store enrollment and can decrypt other IPAs personalized for the same enrollment. Decryption then runs on macOS or Linux, and the native fairplayd is never launched by the tool. This is the same trust concentration with a different capture point.
  • Manual to CI. Offline decryption is a pure function of the IPA and the credential. It can run in a container, be queued, retried and audited. A security team can analyze an app without attaching a device; an attacker can repackage one without a device farm. The cost of both goes down.
  • Plaintext to impact. Once the pages are in the clear, the app is an ordinary Mach-O. An analyst can read endpoints, embedded keys, feature flags and license logic; diff two versions; find vulnerabilities; patch a check; inject a dylib; or rebuild a modified app. FairPlay was the confidentiality barrier, not the integrity barrier. Code signing still matters for installation, but it does not make the recovered code secret again.
  • One credential to a library. The blast radius is the account, not the app. A shared testing account, a reseller account or a CI secret can cover a large catalog. A single leaked credential.bin or model file decrypts every app that account or enrollment owns. That is the difference between losing one binary and losing a collection.

Account-level FairPlay design has been abused before in a different way. Unit 42's 2016 AceDeceiver report describes a FairPlay man-in-the-middle that replayed stored purchase authorization to install apps without an enterprise certificate, and calls it "the first iOS malware we've seen that abuses certain design flaws in Apple's DRM protection mechanism". Apple removed the three apps from the App Store on 2016-02-25. AceDeceiver abused installation authorization, not offline decryption, so it is a precedent for account-scoped FairPlay design problems, not a description of this tool.

We found no CVE assigned to the decryption design. A keyword search of the NVD for fairplay returned no records on 2026-11-12, and the CISA KEV catalog version 2026.11.10 has no FairPlay entry. That is consistent with a design property demonstrated by a public tool, not a vendor software vulnerability with a patched version.

What should defenders and developers do?

If you acquire IPAs for authorized testing, the credential is now the most sensitive file in the pipeline. Treat it like an account secret, because that is what it is.

  • Use a dedicated Apple Account for the testing program. Do not reuse a personal account, a shared team account or an account that holds production purchases.
  • Keep credential.bin or the UnfairPlay model in a secrets manager, encrypted at rest, with access logging and a named owner. Do not put it in a repository, a build log or a ticket.
  • Limit who can run decryption and which IPAs enter the queue. Record the account, the app and the date for each run so the use can be audited.
  • Rotate or retire the account when the engagement ends. The public code has no expiry switch, so the operational control is to stop using the credential and remove it.
  • When sharing results, strip account data. UnfairPlay's --strip-account-data option removes the SC_Info members and the Apple ID and purchaser fields from iTunesMetadata.plist; the tool's README still says redaction does not authorize redistribution.
  • Never share SINF, SUPF, SIDB, a model file or a decrypted IPA outside the authorized scope. The license files identify the account, and the model is reusable.

If you build an iOS app, assume that a determined analyst can eventually obtain the binary. FairPlay raises the cost; it does not make client-side code a secret.

  • Do not ship long-lived API keys, private keys, server credentials or signing material in the app. Move the valuable logic and the secrets to a server you control.
  • Use short-lived, user- and device-bound tokens, server-side authorization and device attestation where the risk justifies it. Certificate pinning raises the cost of interception; it does not protect a secret that is already in the binary.
  • Treat client-side license checks as user experience, not as a security boundary. A patched binary can bypass them, and a decrypted binary makes the patch easier to write.
  • Keep the server side authoritative for entitlements, rate limits and paid features. If a feature must not be copied, enforce it where the user cannot edit the code.

The platform-side fixes are design changes, not a patch we can point to. A vendor could bind page keys to a device-held secret at every launch, issue short-lived license material and rotate it server-side, make the account credential non-exportable from the secure hardware, and detect debugger or exception-port attachment to fairplayd. Any scheme that decrypts code on a device the user controls still has to expose a key to that device at some point; the goal is to make the key per-launch, per-device and short-lived, not to pretend that client-side decryption can be made absolute.

For users, the practical advice is narrower. Enable two-factor authentication on the Apple Account, do not share the account, and do not install IPAs from untrusted sources. The main risk in this article is the confidentiality of app code and the blast radius of a shared account, not a remote compromise of a locked phone.

What could not be verified?

  • Apple's exact key-derivation scheme for App Store binaries, and whether the SINF priv work key is unique per app, per account or per transaction. The public tool's behavior is the evidence; Apple publishes no specification.
  • Whether Apple rotates or revokes the account credential server-side. The public code contains no expiry or revocation logic, but server-side invalidation cannot be ruled out from the repository.
  • Behavior on current iOS releases. The retriever's README says iOS 10 and later, and the code contains iOS 16.3.1-specific notes, but we did not test it on a device. A fairplayd change can break retrieval without changing the offline format.
  • The internals of UnfairPlay's model recovery. We read its README and its stated guarantees, not its full recovery code.
  • The exact SINF and SUPF layouts beyond the versions the tool supports: version-3 and version-5 SUPF, 440-byte and 448-byte priv, and arm64 slices. Other versions may exist.
  • The number of accounts, apps or credentials in use. No telemetry is published, and the repository's stars and forks measure interest, not exploitation.
  • Whether FairPlay Streaming video uses the same key hierarchy. The 2023 academic survey by Rafi, Shepherd and Markantonakis covers mobile DRM including FairPlay, but its focus is streaming content, not App Store IPA binaries, so it is context rather than evidence for this path.

FAQ

Is there a CVE for FairPlay app decryption?

We found none. A keyword search of the NVD for fairplay returned no records on 2026-11-12, and the CISA KEV catalog version 2026.11.10 has no FairPlay entry. The public tool and the design it demonstrates are not tracked as a CVE.

Is this a jailbreak exploit?

No. It does not exploit a kernel memory-corruption bug. It needs root access on a jailbroken device to attach to fairplayd and observe the FairPlay call. The decryption itself is offline and does not need a jailbreak.

Does one credential decrypt every IPA in the world?

No. The credential is scoped to the account or enrollment that produced it, and each app's SINF is personalized to that scope. Apple Arcade apps need a separate credential from regular App Store apps, according to the fairplay-decrypt README. The weakness is the breadth within one account, not a global key.

Does this affect FairPlay Streaming video?

Not established. The public tool targets App Store IPA binaries and their SINF/SUPF licenses. FairPlay Streaming is a separate media path, and the 2023 survey that covers it is context, not evidence that the same credential decrypts video.

Can Apple fix it?

Apple can change the design: bind keys to a device secret at every launch, shorten license lifetimes, rotate material server-side, make the credential non-exportable and harden the FairPlay IPC. Any client-side decryption still has to expose a key to the device that plays the content, so the practical goal is to reduce the lifetime and scope of that key, not to make it unextractable in absolute terms.

Is it exploited in the wild?

No attack using the tool is reported in the project or the sources reviewed as of 2026-11-12. The repository had more than 2,400 stars and 370 forks on that date, and UnfairPlay had more than 1,100 stars. Those numbers show interest, not exploitation.

References

  • EthanArbuckle/fairplay-decrypt, README, created 2026-10-19, latest commit 2026-11-08: https://github.com/EthanArbuckle/fairplay-decrypt (checked 2026-11-12)
  • core.py, the SINF priv unwrap, SUPF parsing, page-key derivation and cryptid clearing: https://github.com/EthanArbuckle/fairplay-decrypt/blob/main/core.py (checked 2026-11-12)
  • decrypt.py, the IPA ZIP handling and per-binary plan: https://github.com/EthanArbuckle/fairplay-decrypt/blob/main/decrypt.py (checked 2026-11-12)
  • credential-retrieval/README.md, iOS 10+, the Apple Arcade credential split and the device requirements: https://github.com/EthanArbuckle/fairplay-decrypt/blob/main/credential-retrieval/README.md (checked 2026-11-12)
  • credential.c, the SIDB decryption, the user/key selection and the credential derivation: https://github.com/EthanArbuckle/fairplay-decrypt/blob/main/credential-retrieval/credential/credential.c (checked 2026-11-12)
  • observer.c, task_for_pid(), the hardware breakpoints and the XNU 8792 workaround: https://github.com/EthanArbuckle/fairplay-decrypt/blob/main/credential-retrieval/credential/observer.c (checked 2026-11-12)
  • trigger.c, the mremap_encrypted() first-use request and its comment: https://github.com/EthanArbuckle/fairplay-decrypt/blob/main/credential-retrieval/trigger/trigger.c (checked 2026-11-12)
  • DrWhax/unfairplay, README, the enrollment model, the multiple-IPA scope, macOS/Linux decryption and the --strip-account-data option: https://github.com/DrWhax/unfairplay (checked 2026-11-12)
  • Apple, App code signing process in iOS, iPadOS, tvOS, visionOS, and watchOS, published 2024-12-19: https://support.apple.com/guide/security/app-code-signing-process-sec7c917bf14/web (checked 2026-11-12)
  • Apple, Security of runtime process in iOS, iPadOS, and visionOS, published 2024-12-19: https://support.apple.com/guide/security/security-of-runtime-process-sec15bfe098e/web (checked 2026-11-12)
  • Apple XNU, bsd/kern/mach_loader.c, set_code_unprotect(), LOAD_DECRYPTFAIL and EXEC_EXIT_REASON_FAIRPLAY_DECRYPT: https://github.com/apple-oss-distributions/xnu/blob/main/bsd/kern/mach_loader.c (checked 2026-11-12)
  • Apple XNU, osfmk/vm/vm_apple_protect.c, the external memory manager and the 4 KB page_decrypt loop: https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/vm/vm_apple_protect.c (checked 2026-11-12)
  • Apple XNU, EXTERNAL_HEADERS/mach-o/loader.h, the encryption_info_command fields: https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h (checked 2026-11-12)
  • SensePost, "Decrypting iPhone Apps", 2011-10-24, the cryptid fields and the memory-dump workflow: https://sensepost.com/blog/2011/decrypting-iphone-apps/ (checked 2026-11-12)
  • Noham, "Anatomy of an iOS crack", 2026-05-15, the SC_Info files and the SINF field layout: https://noh.am/en/posts/rev-dylib/ (checked 2026-11-12)
  • pwn0rz, fairplay_research, the FairPlayIOKit/fairplayd flow and the hardware AES stage: https://github.com/pwn0rz/fairplay_research (checked 2026-11-12)
  • Nicolò Altamura, "Analysis of Obfuscation Found in Apple FairPlay", 2023-08-28, macOS 13.5.1: https://nicolo.dev/en/blog/fairplay-apple-obfuscation/ (checked 2026-11-12)
  • Unit 42, Claud Xiao, "AceDeceiver: First iOS Trojan Exploiting Apple DRM Design Flaws to Infect Any iOS Device", 2016-03-16: https://unit42.paloaltonetworks.com/acedeceiver-first-ios-trojan-exploiting-apple-drm-design-flaws-to-infect-any-ios-device/ (checked 2026-11-12)
  • NVD keyword search for fairplay, zero records: https://nvd.nist.gov/vuln/search/results?query=fairplay&search_type=all (checked 2026-11-12)
  • CISA, Known Exploited Vulnerabilities catalog, version 2026.11.10, no FairPlay entry: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (checked 2026-11-12)
  • Amir Rafi, Carlton Shepherd and Konstantinos Markantonakis, "A First Look at Digital Rights Management Systems for Secure Mobile Content Delivery", arXiv:2308.00437, 2023-08: https://arxiv.org/abs/2308.00437 (checked 2026-11-12)