Skip to content

Instantly share code, notes, and snippets.

@obfusk
Last active August 9, 2024 17:00
Show Gist options
  • Save obfusk/0dc1c4be48ffb0714579aff998585b2f to your computer and use it in GitHub Desktop.
Save obfusk/0dc1c4be48ffb0714579aff998585b2f to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
import json
import zipfile
from hashlib import sha1, sha256
from asn1crypto import cms # type: ignore[import-untyped]
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.hashes import SHA1, SHA256
HASHERS = dict(sha1=(sha1, SHA1), sha256=(sha256, SHA256))
PADDING = dict(rsassa_pkcs1v15=PKCS1v15)
def verify_signature(pkpass: str) -> bool:
valid = None
with zipfile.ZipFile(pkpass) as zf:
sig = cms.ContentInfo.load(zf.read("signature"))
msg = zf.read("manifest.json")
for si in sig["content"]["signer_infos"]:
for c in sig["content"]["certificates"]:
if c.chosen["tbs_certificate"]["serial_number"] == si["sid"].chosen["serial_number"]:
cert = c.chosen
for a in si["signed_attrs"]:
if a["type"].native == "message_digest":
digest = a["values"][0].native
data = b"\x31" + si["signed_attrs"].dump()[1:]
h0, h1 = HASHERS[si["digest_algorithm"]["algorithm"].native]
pad = PADDING[si["signature_algorithm"]["algorithm"].native]
pubkey = serialization.load_der_public_key(cert.public_key.dump())
signature = si["signature"].native
if h0(msg).digest() == digest:
try:
pubkey.verify(signature, data, pad(), h1()) # type: ignore
except InvalidSignature:
print("invalid signature")
valid = False
else:
print("valid signature")
if valid is None:
valid = True
else:
print("digest mismatch")
valid = False
return valid or False
def verify_manifest(pkpass: str) -> bool:
valid = True
with zipfile.ZipFile(pkpass) as zf:
files = set(zf.namelist()) - {"manifest.json", "signature"}
with zf.open("manifest.json") as fh:
manifest = json.load(fh)
for file, sha in manifest.items():
files.remove(file)
if sha1(zf.read(file)).hexdigest() == sha:
print(f"{file!r}: valid sha1")
else:
print(f"{file!r}: invalid sha1")
valid = False
missing = [f for f in files if not (f.endswith("/") or f.startswith("__MACOSX/"))]
if missing:
print(f"files missing sha1: {sorted(missing)}")
valid = False
return valid
if __name__ == "__main__":
import sys
pkpass, valid = sys.argv[1], True
if not verify_signature(pkpass):
valid = False
if not verify_manifest(pkpass):
valid = False
if not valid:
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment