Paulo Andrade

Keeper of Secrets.

twitter github stackoverflow linkedin email
Pitfalls When Validating App Store Receipts on macOS
Sep 23, 2021
2 minutes read

To activate paid features in the app, Secrets has always done local receipt validation.

Succinctly validation involves two steps:

  1. Checking the signature on the receipt is valid.
  2. Checking if that receipt was generated for the current device.

For the second step you need a device identifier which must match the one the App Store used to generate the receipt. And here lies the problem.

On iOS getting a device identifier is very simple, you just call identifierForVendor on UIDevice. On macOS, however, Apple provides this function on their documentation that you should copy to your project and get the identifier using that.

A single API call would be better, but at least they give you a function you can just copy/paste right? Well… the thing is… it doesn’t work.

If you look a the code you’ll see that, apparently, the device identifier on macOS is just the MAC address of your en0 interface. But that’s not actually true. What happens if the user doesn’t have an en0 interface? Sounds unlikely? It’s not:

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 when ifconfig 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.

And that’s it. Or at least I hope so… 😥

Back to posts