Gatekeeper blocks my app for some minutes after download

I am working on an open source app. I have been testing the package installer, and something unexpected is happening: the .pkg won't run on my test machine and will instead show a banner saying "myApp.app can't be opened because Apple cannot check it for malicious software"; nevertheless, if I wait some minutes, the installer will run just fine!

After reading through many of ekimo's posts, I assumed it may have something to do with stapler. I was not stapling my .dmg originally, so that's something I may be missing (my app is installed by a .pkg inside a .dmg). Nevertheless, the computer where I am testing the app has internet connection, meaning stapler should not even come into play.

Regardless, I decided to staple my .dmg. Running xcrun stapler staple -v myApp.dmg after notarizing produces this result:

builder ~ % xcrun stapler staple -v /Users/builder/Data/HEAD/installation/Packages/myApp.dmg 
Processing: /Users/builder/Data/HEAD/installation/Packages/myApp.dmg
Properties are {
    NSURLIsDirectoryKey = 0;
    NSURLIsPackageKey = 0;
    NSURLIsSymbolicLinkKey = 0;
    NSURLLocalizedTypeDescriptionKey = "Disk Image";
    NSURLTypeIdentifierKey = "com.apple.disk-image-udif";
    "_NSURLIsApplicationKey" = 0;
}
Creating synthetic cdHash for unsigned disk image, myApp.dmg. Humanity must endure.
Signing information is {
    cdhashes =     (
        {length = 20, bytes = 0xdd018313b1c574a403f01dccc96c21705987d76c}
    );
    "cdhashes-full" =     {
        2 = {length = 32, bytes = 0xdd018313 b1c574a4 03f01dcc c96c2170 ... 918d33f3 d5a74dc3 };
    };
    cms = {length = 0, bytes = 0x};
    "digest-algorithm" = 2;
    "digest-algorithms" =     (
        2
    );
    flags = 2;
    format = "disk image";
    identifier = ADHOC;
    "main-executable" = "file:///Users/builder/Data/HEAD/installation/Packages/myApp.dmg";
    source = "explicit detached";
    unique = {length = 20, bytes = 0xdd018313b1c574a403f01dccc96c21705987d76c};
}
Stored Codesign length: 12 number of blobs: 0
Total Length: 12 Found blobs: 0
JSON Data is {
    records =     (
                {
            recordName = "2/2/dd018313b1c574a403f01dccc96c21705987d76c";
        }
    );
}
 Headers: {
    "Content-Type" = "application/json";
}
Domain is api.apple-cloudkit.com
Response is <NSHTTPURLResponse: 0x600003b85ba0> { URL: https://api.apple-cloudkit.com/database/1/com.apple.gk.ticket-delivery/production/public/records/lookup } { Status Code: 200, Headers {
    Connection =     (
        "keep-alive"
    );
    "Content-Encoding" =     (
        gzip
    );
    "Content-Type" =     (
        "application/json; charset=UTF-8"
    );
    Date =     (
        "Mon, 26 Feb 2024 15:34:15 GMT"
    );
    Server =     (
        "AppleHttpServer/78689afb4479"
    );
    "Strict-Transport-Security" =     (
        "max-age=31536000; includeSubDomains;"
    );
    "Transfer-Encoding" =     (
        Identity
    );
    Via =     (
        "xrail:st53p00ic-qujn15041902.me.com:8301:24R11:grp60,631194250daa17e24277dea86cf30319:59e17ac665e1de7388b8f4e69e92e383:defra2"
    );
    "X-Apple-CloudKit-Version" =     (
        "1.0"
    );
    "X-Apple-Edge-Response-Time" =     (
        99
    );
    "X-Apple-Request-UUID" =     (
        "9fc0fe2d-49fd-4e74-b718-660c56edb3bb"
    );
    "X-Responding-Instance" =     (
        "ckdatabasews:16306401:st42p63ic-ztfb05112901:8807:2409B432:afc827b7b1ebf24829e9c4856d4b69205f23804f"
    );
    "access-control-expose-headers" =     (
        "X-Apple-Request-UUID,X-Responding-Instance,Via"
    );
    "x-apple-user-partition" =     (
        63
    );
} }
Size of data is 165
JSON Response is: {
    records =     (
                {
            reason = "Record not found";
            recordName = "2/2/dd018313b1c574a403f01dccc96c21705987d76c";
            serverErrorCode = "NOT_FOUND";
        }
    );
}
CloudKit query for myApp.dmg (2/dd018313b1c574a403f01dccc96c21705987d76c) failed due to "Record not found".
Could not find base64 encoded ticket in response for 2/dd018313b1c574a403f01dccc96c21705987d76c
The staple and validate action failed! Error 65

What does this show?

Thank you.

Error 65 means that there’s no ticket for the supplied item. It’s tied to this:

Creating synthetic cdHash for unsigned disk image, myApp.dmg. Humanity must endure.

Your disk image is unsigned. The notary service allows that, but it doesn’t include the disk image’s cdhash in the final ticket because that cdhash is stable [1].

To fix this, sign your disk image. This is in line with my standard advice of:

  1. Sign everything from the inside out.

  2. Notarise the outmost container.

  3. Staple the outmost item that supports stapling.

I discuss this in detail in… oh wait… that’s now official documentation! So, yeah, check out Packaging Mac software for distribution.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Actually, it might include a cdhash, just a different cdhash than you’re seeing.

Hi eskimo, thank you so much for your quick reply (and your countless guides) :)

I had read the documentation you linked, but somehow indeed skipped the signing of the dmg. Unfortunately, error 65 persists after signing it, although now it of course does not include the message about the unsigned disk image. I tried signing with different identifiers, and settled for <BundleID>.diskImage, since it was what made the most sense to me after going through the documentation.

One of your replies on this thread suggests using --notarization-info (which I have modified to notarytool log due to the update) to compare cdHashes. With that, I can confirm that the cdHash of the dmg is the same on both the notarization server and the output of codesign -d -vvv. To be more precise, the notarytool output looks like this (redacted things aside):

{
  "logFormatVersion": 1,
  "jobId": "60b9cc13-f54f-4fcd-90ca-XXXXXXXXXX",
  "status": "Accepted",
  "statusSummary": "Ready for distribution",
  "statusCode": 0,
  "archiveFilename": "myApp.dmg",
  "uploadDate": "2024-02-27T14:33:15.158Z",
  "sha256": "7e1ed67f6097cbf78c5b08faf35422bc50677043c45393116f0d76XXXXXXXXXX",
  "ticketContents": [
    {
      "path": "myApp.dmg",
      "digestAlgorithm": "SHA-256",
      "cdhash": "70c3b2be3e1684e5cb5d7149b53605XXXXXXXXXX"
    },
  ...
  ],
  "issues": null
}

And codesign outputs:

Executable=/Users/myUser/myApp.dmg
Identifier=com.Company.myApp.diskImage
Format=disk image
CodeDirectory v=20200 size=334 flags=0x0(none) hashes=1+6 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=70c3b2be3e1684e5cb5d7149b53605XXXXXXXXXX
CandidateCDHashFull sha256=70c3b2be3e1684e5cb5d7149b536056d65d21bb5e43d603b2420edXXXXXXXXXX
Hash choices=sha256
CMSDigest=70c3b2be3e1684e5cb5d7149b536056d65d21bb5e43d603b2420edXXXXXXXXXX
CMSDigestType=2
CDHash=70c3b2be3e1684e5cb5d7149b53605XXXXXXXXXX
Signature size=8971
Authority=Developer ID Application: Company (XXXXXX)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=27 Feb 2024 at 15:33:04
Info.plist=not bound
TeamIdentifier=XXXXXXX
Sealed Resources=none
Internal requirements count=1 size=208

As you can see, CandidateCDHash matches the cdhash of the first element in ticketContents... and yet stapler fails.

Any ideas? Should I be checking cdHashes of all the bundled items too? If so, how would I obtain these with codesign (I guess I would have to open the dmg)?

Gatekeeper blocks my app for some minutes after download
 
 
Q