Programmatically Detected if a Notarization Ticket has been Revoked

Apple Recommended

  • Understood. What would be "official" way then to programmatically detect if a notarization ticket has been revoked? ...as, of course, building products that rely solely on supported approaches to implement requested capabilities by/for users, would be ideal! ☺️

Add a Comment

Replies

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;
}
  • Understood. What would be "official" way then to programmatically detect if a notarization ticket has been revoked? ...as, of course, building products that rely solely on supported approaches to implement requested capabilities by/for users, would be ideal! ☺️

Add a Comment