Programmatically Detected if a Notarization Ticket has been Revoked

I've encountered some applications that are validly signed (as reported by codesign dvv / SecStaticCodeCheckValidity), but the notarization ticket has been revoked:

% codesign -v revoked.app 

% stapler validate revoked.app                            
The ticket for revoked.app has been revoked. Gatekeeper will prevent it from running.

% spctl -a -vvv -t install revoked.app 
revoked.app: notarization indicates this code has been revoked

What is the recommended / supported approach to perform this check programmatically? In other words, replicate stapler validate or spctl -a -vvv -t install but with APIs). Ideally an API that returns errSecCSRevokedNotarization

I can extract the app's code signing information, cdhashes or notarization ticket (from Contents/CodeResources). Was toying with SecAssessmentTicketLookup and SecTrustEvaluateWithError but so far, no luck.

And SecRequirementCreateWithString(CFSTR("notarized")... and SecStaticCodeCheckValidity just returns errSecCSReqFailed which yes is correct, but doesn't tell us that the ticket was revoked.

Answered by DTS Engineer in 756087022

The solution is to use the undocumented SecAssessmentTicketLookup API

Just to be clear, there are not undocumented APIs [1]. Stuff is either in the public SDK or it’s not. If it’s not in the public SDK, it’s not an API and we don’t support third-party folks using it. Such things are implementation details that can change without notice. Please don’t build products that rely on such implementation details.

Share and Enjoy

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

[1] Well, exception for stuff that we haven’t got around to documenting yet, but you know what I mean.

Accepted Answer

The solution is to use the undocumented SecAssessmentTicketLookup API, with the cdhash of the item. However, there are a few nuances to be aware of (that tripped me up), such as making sure to specify the right SecCSDigestAlgorithm ...which is a bit tricky as the cdhash can be truncated!

This is what worked for me:

  1. Obtain a SecStaticCodeRef or SecCodeRef for the item, for example via SecStaticCodeCreateWithPath.
  2. Obtain the code signing dictionary via SecCodeCopySigningInformation.
  3. Iterate over the code directory hashes found in the cdhashes key of the code signing dictionary.
  4. For each cdhash, pass to the undocumented SecAssessmentTicketLookup API. It appears that the hashType should be set to kSecCodeSignatureHashSHA256 even though the hash length may be 20 (which is the length of SHA-1 hash CC_SHA1_DIGEST_LENGTH). In other words it appears to the hash is truncated. For the flag, SecAssessmentTicketFlags parameter, pass kSecAssessmentTicketFlagForceOnlineCheck
  5. If SecAssessmentTicketLookup returns EACCES, this means the item has been revoked.

Additional info:

SecAssessmentTicketLookup function definition:

Boolean SecAssessmentTicketLookup(CFDataRef hash, SecCSDigestAlgorithm hashType, SecAssessmentTicketFlags flags, double *date, CFErrorRef *errors);

Example of Apple code (file: notarization.cpp) that implements this in libsecurity_codesigning:

bool checkNotarizationServiceForRevocation(CFDataRef hash, SecCSDigestAlgorithm hashType, double *date)
{
	bool is_revoked = false;

	secinfo("notarization", "checking with online notarization service for hash: %@", hash);

#if TARGET_OS_OSX
	CFRef<CFErrorRef> error;
	if (!SecAssessmentTicketLookup(hash, hashType, kSecAssessmentTicketFlagForceOnlineCheck, date, &error.aref())) {
		CFIndex err = CFErrorGetCode(error);
		if (err == EACCES) {
			secerror("Notarization daemon found revoked hash: %@", hash);
			is_revoked = true;
		} else {
			secerror("Error checking with notarization daemon: %ld", err);
		}
	}
#endif

	return is_revoked;
}

The solution is to use the undocumented SecAssessmentTicketLookup API

Just to be clear, there are not undocumented APIs [1]. Stuff is either in the public SDK or it’s not. If it’s not in the public SDK, it’s not an API and we don’t support third-party folks using it. Such things are implementation details that can change without notice. Please don’t build products that rely on such implementation details.

Share and Enjoy

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

[1] Well, exception for stuff that we haven’t got around to documenting yet, but you know what I mean.

Programmatically Detected if a Notarization Ticket has been Revoked
 
 
Q