how macOS signs binaries

  • computers have lots of programs. these programs sit on disk as files. how exactly does macOS know that a file is safe to run?
  • some questions you might ask yourself before running a program on your computer could be
    • (1) who made this program? do we trust them?
    • (2) has it been tampered with? let’s say we already deemed the binary safe to download, but since then did something else maliciously edit the file?
  • the way we answer both of these questions is with a signature, which is just a chunk of bytes that is calculated from the file’s contents + the developer’s private key
  • what stops an attacker from tampering with the binary, and resigning the change their own private/public key pair?
    • one naive idea is that we create a whitelist of developer keys that we trust. however, this is not realistic since there are millions of developers, possibly new ones daily, and so apple would have to ship a giant list every time a new developer appears
    • instead of whitelisting, apple uses certificates, which is just a developer’s public key + identity that is signed by apple. so, to create binaries that are distributed to other apple machines you need to apply + be approved for the apple developer program, which involves a yearly fee + identity verification. then, apple signs your public key into a certificate & then you sign any binaries you create with your matching private key
# apple has public/private key
apple_private_key = ec.generate_private_key(ec.SECP256R1())
APPLE_PUBLIC_KEY = apple_private_key.public_key()

# developer has to enroll in appple's developer program
dev_private_key = ec.generate_private_key(ec.SECP256R1())
dev_public_key = dev_private_key.public_key())

# apple signs developer's public key + identity, creating a certificate
cert_body = b'dev_public_key + com.corp'
cert_signature = apple_private_key.sign(cert_body, ec.ECDSA(hashes.SHA256())

certificate = {
			"dev_public_key": dev_public_key,
			"apple_signature": cert_signature,
}
  • how do we check for tampering?
    • at launch/during runtime, the macOS kernel checks the signature of the binary
# developer creates a binary 

binary = b'1111'


# developer signs binary with private key
signature = dev_private_key.sign(binary, ec.ECDSA(hashes.SHA256()))
signed_binary = {
			"content": binary,
			"signature": signature,
			"certificate": certificate,
}

# macOS kernel verifies binary at runtime

def verify(signed_binary):
			certification = signed_binary["certificate"]
			try:
					# (1) did apple certifiy this developer's key?
					APPLE_PUBLIC_KEY.verify(cert["apple_signature"], cert_body, ec.ECDSA(hashes.SHA256()))
					
					# (2) did that developer key sign this exact binary
					cert["dev_public_key").verify(signed_binary["signature"], signed_binary["content"], ec,ECDSA(hashes.SHA256())
					return True
			except InvalidSignature:
					return False