Receipt Validation and AirPlay 2
I hear rumblings about some App Store receipt validation issues relating to MAC addresses on macOS Catalina. The commonly-used Apple sample code has busted logic that—in the presence of a strange condition on a (small?) number of systems—will fail to return a value.
[…]
I don’t know yet, but it manifests on my machine as a service match for both
en0
as well asIOTimeSyncEthernetModernInterfaceAdapter
(see attached screenshot.) The latter causes a second go-around in the while loop, causing the MAC address to get overwritten withNULL
.
It makes no sense for Apple to provide buggy sample code for getting the MAC address when every app in the store needs to do this. With some other aspects of receipt validation, one could argue that you want each app to do it slightly differently to make them harder to crack. But the point of checking the MAC address is that you want to match it with the address provided by Apple when it generated the receipt. So you want to use the exact same logic they did.
BTW I don’t think this is a Catalina issue, have seen reports of what sounds like this for several OS versions. Also seems to work if tested right after reboot, but breaks if tested later.
The plot thickens!
After a reboot, my system no longer exhibits this “strange condition”.
Immediately after playing audio over AirPlay 2? BOOM—“strange condition”! 🤯
The “strange condition” appears to be the presence of IOTimeSyncEthernetModernInterfaceAdapter in ioreg.
Previously:
Update (2019-10-22): Chris Liscio (tweet, James Thomson):
While playing audio using AirPlay 2, there are two devices that have a
BSD Name
equal toen0
. Both the actualen0
interface, and a new device calledIOTimeSyncEthernetModernInterfaceAdapter
(orIOTimeSyncWiFiInterfaceAdapter
, depending on your Mac) that is only present while AirPlay is active.[…]
When
copy_mac_address
returnsNULL
, your receipt validation code fails, and probably results in your application callingexit(173)
to ask the App Store for a new receipt. Unfortunately, a new receipt isn’t going to help, and you get stuck in a very strange loop.For me, this resulted in the app repeatedly appearing & disappearing in the Dock for quite some time. Eventually, the system finally gave up and told me the app was “Damaged” and needed to be re-downloaded.
[…]
The keys to this alternate approach are as follows:
- It specifies the
kIOEthernetInterfaceClass
, which only matches network interfaces. (Before you ask—yes, this includes Wi-Fi adapters.)- It also specifies that
kIOPrimaryInterface
is set to true, which matches the built-in, primary network interface.
Update (2021-10-20): Paulo Andrade:
In reality the device identifier for macOS is the MAC for the primary network interface, which might not actually be named
en0
. To be on the safe side, instead of trying to determine which interface is the primary, I just grab all the MACs on that Mac and attempt validation with each one. Problem solved! Right?Well… not yet. There’s another issue I just encountered recently. The
IOBSDNameMatching
call to get an interface by name can actually return nil even whenifconfig
shows an interface with that name. I haven’t been able to understand why this happens (it’s very rare), but on one occasion Vallum Firewall seemed to be the culprit and on another, a simple restart seemed to fix it.I finally ended up with a solution based on Apple’s source code here. Instead of getting a list of interface names and then getting the MAC for each by name I just iterate all the interfaces irrespective of what they’re called and collect the MACs for each.