- 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