ipasign 1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ipasign/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """ipasign: pure-Python iOS code signing.
2
+
3
+ Re-sign iOS ``.ipa`` archives, ``.app`` bundles, frameworks, dylibs and bare
4
+ Mach-O executables with a ``.p12`` identity and a ``.mobileprovision`` profile.
5
+
6
+ import ipasign
7
+
8
+ key = ipasign.Key(pkey, prov, password)
9
+ out = key.sign(input_file, output_file)
10
+ print(f"Successfully signed: {out.output_path}")
11
+ """
12
+
13
+ from .errors import (
14
+ ArchiveError,
15
+ BlobError,
16
+ BundleError,
17
+ CredentialError,
18
+ InvalidInputError,
19
+ IpasignError,
20
+ MachOError,
21
+ NotEnoughSpaceError,
22
+ ProfileError,
23
+ )
24
+ from .key import Key, SignResult
25
+
26
+ __version__ = "1.0"
27
+
28
+ __all__ = [
29
+ "Key",
30
+ "SignResult",
31
+ "IpasignError",
32
+ "InvalidInputError",
33
+ "MachOError",
34
+ "BlobError",
35
+ "CredentialError",
36
+ "ProfileError",
37
+ "BundleError",
38
+ "ArchiveError",
39
+ "NotEnoughSpaceError",
40
+ ]
ipasign/_apple_ca.py ADDED
@@ -0,0 +1,284 @@
1
+ """Apple Developer Relations intermediate and root certificates.
2
+
3
+ These are the public CA certificates that terminate an Apple code signing
4
+ chain. They are distributed by Apple and are needed to build the chain that
5
+ a verifier walks from the signing leaf up to a trusted root.
6
+ """
7
+
8
+ WWDR_G1 = (
9
+ '-----BEGIN CERTIFICATE-----\n'
10
+ 'MIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UE\n'
11
+ 'BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp\n'
12
+ 'ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEz\n'
13
+ 'MDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVTMRMwEQYD\n'
14
+ 'VQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv\n'
15
+ 'cGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3Bl\n'
16
+ 'ciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3\n'
17
+ 'DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0U3rOfGOA\n'
18
+ 'YXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkVCBmsqtsq\n'
19
+ 'Mu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8V25nNYB2\n'
20
+ 'NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHld0WNUEi6\n'
21
+ 'Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1qarunFjVg\n'
22
+ '0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGjgaYwgaMw\n'
23
+ 'HQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQFMAMBAf8w\n'
24
+ 'HwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcwJTAjoCGg\n'
25
+ 'H4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgGG\n'
26
+ 'MBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Zviz1smwv\n'
27
+ 'j+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/Nw0Uwj6OD\n'
28
+ 'Dc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJTleMa1s8\n'
29
+ 'Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1VAKmuu0sw\n'
30
+ 'ruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur+cmV6U/k\n'
31
+ 'TecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxRpVzscYqC\n'
32
+ 'tGwPDBUf\n'
33
+ '-----END CERTIFICATE-----\n'
34
+ )
35
+
36
+ WWDR_G2 = (
37
+ '-----BEGIN CERTIFICATE-----\n'
38
+ 'MIIC9zCCAnygAwIBAgIIb+/Y9emjp+4wCgYIKoZIzj0EAwIwZzEbMBkGA1UEAwwS\n'
39
+ 'QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u\n'
40
+ 'IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN\n'
41
+ 'MTQwNTA2MjM0MzI0WhcNMjkwNTA2MjM0MzI0WjCBgDE0MDIGA1UEAwwrQXBwbGUg\n'
42
+ 'V29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ0EgLSBHMjEmMCQGA1UECwwd\n'
43
+ 'QXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIElu\n'
44
+ 'Yy4xCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3fC3BkvP\n'
45
+ '3XMEE8RDiQOTgPte9nStQmFSWAImUxnIYyIHCVJhysTZV+9tJmiLdJGMxPmAaCj8\n'
46
+ 'CWjwENrp0C7JGqOB9zCB9DBGBggrBgEFBQcBAQQ6MDgwNgYIKwYBBQUHMAGGKmh0\n'
47
+ 'dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDQtYXBwbGVyb290Y2FnMzAdBgNVHQ4E\n'
48
+ 'FgQUhLaEzDqGYnIWWZToGqO9SN863wswDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME\n'
49
+ 'GDAWgBS7sN6hWDOImqSKmd6+veuv2sskqzA3BgNVHR8EMDAuMCygKqAohiZodHRw\n'
50
+ 'Oi8vY3JsLmFwcGxlLmNvbS9hcHBsZXJvb3RjYWczLmNybDAOBgNVHQ8BAf8EBAMC\n'
51
+ 'AQYwEAYKKoZIhvdjZAYCDwQCBQAwCgYIKoZIzj0EAwIDaQAwZgIxANmxxzHGI/ZP\n'
52
+ 'TdDZR8V9GGkRh3En02it4Jtlmr5s3z9GppAJvm6hOyywUYlBPIfSvwIxAPxkUolL\n'
53
+ 'PF2/axzCiZgvcq61m6oaCyNUd1ToFUOixRLal1BzfF7QbrJcYlDXUfE6Wg==\n'
54
+ '-----END CERTIFICATE-----\n'
55
+ )
56
+
57
+ WWDR_G3 = (
58
+ '-----BEGIN CERTIFICATE-----\n'
59
+ 'MIIEUTCCAzmgAwIBAgIQfK9pCiW3Of57m0R6wXjF7jANBgkqhkiG9w0BAQsFADBi\n'
60
+ 'MQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBw\n'
61
+ 'bGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3Qg\n'
62
+ 'Q0EwHhcNMjAwMjE5MTgxMzQ3WhcNMzAwMjIwMDAwMDAwWjB1MUQwQgYDVQQDDDtB\n'
63
+ 'cHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9u\n'
64
+ 'IEF1dGhvcml0eTELMAkGA1UECwwCRzMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJ\n'
65
+ 'BgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2PWJ/KhZ\n'
66
+ 'C4fHTJEuLVaQ03gdpDDppUjvC0O/LYT7JF1FG+XrWTYSXFRknmxiLbTGl8rMPPbW\n'
67
+ 'BpH85QKmHGq0edVny6zpPwcR4YS8Rx1mjjmi6LRJ7TrS4RBgeo6TjMrA2gzAg9Dj\n'
68
+ '+ZHWp4zIwXPirkbRYp2SqJBgN31ols2N4Pyb+ni743uvLRfdW/6AWSN1F7gSwe0b\n'
69
+ '5TTO/iK1nkmw5VW/j4SiPKi6xYaVFuQAyZ8D0MyzOhZ71gVcnetHrg21LYwOaU1A\n'
70
+ '0EtMOwSejSGxrC5DVDDOwYqGlJhL32oNP/77HK6XF8J4CjDgXx9UO0m3JQAaN4LS\n'
71
+ 'VpelUkl8YDib7wIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0j\n'
72
+ 'BBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wRAYIKwYBBQUHAQEEODA2MDQGCCsG\n'
73
+ 'AQUFBzABhihodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLWFwcGxlcm9vdGNh\n'
74
+ 'MC4GA1UdHwQnMCUwI6AhoB+GHWh0dHA6Ly9jcmwuYXBwbGUuY29tL3Jvb3QuY3Js\n'
75
+ 'MB0GA1UdDgQWBBQJ/sAVkPmvZAqSErkmKGMMl+ynsjAOBgNVHQ8BAf8EBAMCAQYw\n'
76
+ 'EAYKKoZIhvdjZAYCAQQCBQAwDQYJKoZIhvcNAQELBQADggEBAK1lE+j24IF3RAJH\n'
77
+ 'Qr5fpTkg6mKp/cWQyXMT1Z6b0KoPjY3L7QHPbChAW8dVJEH4/M/BtSPp3Ozxb8qA\n'
78
+ 'HXfCxGFJJWevD8o5Ja3T43rMMygNDi6hV0Bz+uZcrgZRKe3jhQxPYdwyFot30ETK\n'
79
+ 'XXIDMUacrptAGvr04NM++i+MZp+XxFRZ79JI9AeZSWBZGcfdlNHAwWx/eCHvDOs7\n'
80
+ 'bJmCS1JgOLU5gm3sUjFTvg+RTElJdI+mUcuER04ddSduvfnSXPN/wmwLCTbiZOTC\n'
81
+ 'NwMUGdXqapSqqdv+9poIZ4vvK7iqF0mDr8/LvOnP6pVxsLRFoszlh6oKw0E6eVza\n'
82
+ 'UDSdlTs=\n'
83
+ '-----END CERTIFICATE-----\n'
84
+ )
85
+
86
+ WWDR_G4 = (
87
+ '-----BEGIN CERTIFICATE-----\n'
88
+ 'MIIEVTCCAz2gAwIBAgIUE9x3lVJx5T3GMujM/+Uh88zFztIwDQYJKoZIhvcNAQEL\n'
89
+ 'BQAwYjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsT\n'
90
+ 'HUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBS\n'
91
+ 'b290IENBMB4XDTIwMTIxNjE5MzYwNFoXDTMwMTIxMDAwMDAwMFowdTFEMEIGA1UE\n'
92
+ 'Aww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNh\n'
93
+ 'dGlvbiBBdXRob3JpdHkxCzAJBgNVBAsMAkc0MRMwEQYDVQQKDApBcHBsZSBJbmMu\n'
94
+ 'MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAf\n'
95
+ 'eKp6JzKwRl/nF3bYoJ0OKY6tPTKlxGs3yeRBkWq3eXFdDDQEYHX3rkOPR8SGHgjo\n'
96
+ 'v9Y5Ui8eZ/xx8YJtPH4GUnadLLzVQ+mxtLxAOnhRXVGhJeG+bJGdayFZGEHVD41t\n'
97
+ 'QSo5SiHgkJ9OE0/QjJoyuNdqkh4laqQyziIZhQVg3AJK8lrrd3kCfcCXVGySjnYB\n'
98
+ '5kaP5eYq+6KwrRitbTOFOCOL6oqW7Z+uZk+jDEAnbZXQYojZQykn/e2kv1MukBVl\n'
99
+ 'PNkuYmQzHWxq3Y4hqqRfFcYw7V/mjDaSlLfcOQIA+2SM1AyB8j/VNJeHdSbCb64D\n'
100
+ 'YyEMe9QbsWLFApy9/a8CAwEAAaOB7zCB7DASBgNVHRMBAf8ECDAGAQH/AgEAMB8G\n'
101
+ 'A1UdIwQYMBaAFCvQaUeUdgn+9GuNLkCm90dNfwheMEQGCCsGAQUFBwEBBDgwNjA0\n'
102
+ 'BggrBgEFBQcwAYYoaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwMy1hcHBsZXJv\n'
103
+ 'b3RjYTAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vY3JsLmFwcGxlLmNvbS9yb290\n'
104
+ 'LmNybDAdBgNVHQ4EFgQUW9n6HeeaGgujmXYiUIY+kchbd6gwDgYDVR0PAQH/BAQD\n'
105
+ 'AgEGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQA/Vj2e5bbD\n'
106
+ 'eeZFIGi9v3OLLBKeAuOugCKMBB7DUshwgKj7zqew1UJEggOCTwb8O0kU+9h0UoWv\n'
107
+ 'p50h5wESA5/NQFjQAde/MoMrU1goPO6cn1R2PWQnxn6NHThNLa6B5rmluJyJlPef\n'
108
+ 'x4elUWY0GzlxOSTjh2fvpbFoe4zuPfeutnvi0v/fYcZqdUmVIkSoBPyUuAsuORFJ\n'
109
+ 'EtHlgepZAE9bPFo22noicwkJac3AfOriJP6YRLj477JxPxpd1F1+M02cHSS+APCQ\n'
110
+ 'A1iZQT0xWmJArzmoUUOSqwSonMJNsUvSq3xKX+udO7xPiEAGE/+QF4oIRynoYpgp\n'
111
+ 'pU8RBWk6z/Kf\n'
112
+ '-----END CERTIFICATE-----\n'
113
+ )
114
+
115
+ WWDR_G5 = (
116
+ '-----BEGIN CERTIFICATE-----\n'
117
+ 'MIIEVTCCAz2gAwIBAgIUO36ACu7TAqHm7NuX2cqsKJzxaZQwDQYJKoZIhvcNAQEL\n'
118
+ 'BQAwYjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsT\n'
119
+ 'HUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBS\n'
120
+ 'b290IENBMB4XDTIwMTIxNjE5Mzg1NloXDTMwMTIxMDAwMDAwMFowdTFEMEIGA1UE\n'
121
+ 'Aww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNh\n'
122
+ 'dGlvbiBBdXRob3JpdHkxCzAJBgNVBAsMAkc1MRMwEQYDVQQKDApBcHBsZSBJbmMu\n'
123
+ 'MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ9d\n'
124
+ '2h/7+rzQSyI8x9Ym+hf39J8ePmQRZprvXr6rNL2qLCFu1h6UIYUsdMEOEGGqPGNK\n'
125
+ 'fkrjyHXWz8KcCEh7arkpsclm/ciKFtGyBDyCuoBs4v8Kcuus/jtvSL6eixFNlX2y\n'
126
+ 'e5AvAhxO/Em+12+1T754xtress3J2WYRO1rpCUVziVDUTuJoBX7adZxLAa7a489t\n'
127
+ 'dE3eU9DVGjiCOtCd410pe7GB6iknC/tgfIYS+/BiTwbnTNEf2W2e7XPaeCENnXDZ\n'
128
+ 'RleQX2eEwXN3CqhiYraucIa7dSOJrXn25qTU/YMmMgo7JJJbIKGc0S+AGJvdPAvn\n'
129
+ 'tf3sgFcPF54/K4cnu/cCAwEAAaOB7zCB7DASBgNVHRMBAf8ECDAGAQH/AgEAMB8G\n'
130
+ 'A1UdIwQYMBaAFCvQaUeUdgn+9GuNLkCm90dNfwheMEQGCCsGAQUFBwEBBDgwNjA0\n'
131
+ 'BggrBgEFBQcwAYYoaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwMy1hcHBsZXJv\n'
132
+ 'b3RjYTAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vY3JsLmFwcGxlLmNvbS9yb290\n'
133
+ 'LmNybDAdBgNVHQ4EFgQUGYuXjUpbYXhX9KVcNRKKOQjjsHUwDgYDVR0PAQH/BAQD\n'
134
+ 'AgEGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQBaxDWi2eYK\n'
135
+ 'nlKiAIIid81yL5D5Iq8UJcyqCkJgksK9dR3rTMoV5X5rQBBe+1tFdA3wen2Ikc7e\n'
136
+ 'Y4tCidIY30GzWJ4GCIdI3UCvI9Xt6yxg5eukfxzpnIPWlF9MYjmKTq4TjX1DuNxe\n'
137
+ 'rL4YQPLmDyxdE5Pxe2WowmhI3v+0lpsM+zI2np4NlV84CouW0hJst4sLjtc+7G8B\n'
138
+ 'qs5NRWDbhHFmYuUZZTDNiv9FU/tu+4h3Q8NIY/n3UbNyXnniVs+8u4S5OFp4rhFI\n'
139
+ 'UrsNNYuU3sx0mmj1SWCUrPKosxWGkNDMMEOG0+VwAlG0gcCol9Tq6rCMCUDvOJOy\n'
140
+ 'zSID62dDZchF\n'
141
+ '-----END CERTIFICATE-----\n'
142
+ )
143
+
144
+ WWDR_G6 = (
145
+ '-----BEGIN CERTIFICATE-----\n'
146
+ 'MIIDFjCCApygAwIBAgIUIsGhRwp0c2nvU4YSycafPTjzbNcwCgYIKoZIzj0EAwMw\n'
147
+ 'ZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBD\n'
148
+ 'ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkG\n'
149
+ 'A1UEBhMCVVMwHhcNMjEwMzE3MjAzNzEwWhcNMzYwMzE5MDAwMDAwWjB1MUQwQgYD\n'
150
+ 'VQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZp\n'
151
+ 'Y2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzYxEzARBgNVBAoMCkFwcGxlIElu\n'
152
+ 'Yy4xCzAJBgNVBAYTAlVTMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEbsQKC94PrlWm\n'
153
+ 'ZXnXgtxzdVJL8T0SGYngDRGpngn3N6PT8JMEb7FDi4bBmPhCnZ3/sq6PF/cGcKXW\n'
154
+ 'sL5vOteRhyJ45x3ASP7cOB+aao90fcpxSv/EZFbniAbNgZGhIhpIo4H6MIH3MBIG\n'
155
+ 'A1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUu7DeoVgziJqkipnevr3rr9rL\n'
156
+ 'JKswRgYIKwYBBQUHAQEEOjA4MDYGCCsGAQUFBzABhipodHRwOi8vb2NzcC5hcHBs\n'
157
+ 'ZS5jb20vb2NzcDAzLWFwcGxlcm9vdGNhZzMwNwYDVR0fBDAwLjAsoCqgKIYmaHR0\n'
158
+ 'cDovL2NybC5hcHBsZS5jb20vYXBwbGVyb290Y2FnMy5jcmwwHQYDVR0OBBYEFD8v\n'
159
+ 'lCNR01DJmig97bB85c+lkGKZMA4GA1UdDwEB/wQEAwIBBjAQBgoqhkiG92NkBgIB\n'
160
+ 'BAIFADAKBggqhkjOPQQDAwNoADBlAjBAXhSq5IyKogMCPtw490BaB677CaEGJXuf\n'
161
+ 'QB/EqZGd6CSjiCtOnuMTbXVXmxxcxfkCMQDTSPxarZXvNrkxU3TkUMI33yzvFVVR\n'
162
+ 'T4wxWJC994OsdcZ4+RGNsYDyR5gmdr0nDGg=\n'
163
+ '-----END CERTIFICATE-----\n'
164
+ )
165
+
166
+ WWDR_G7 = (
167
+ '-----BEGIN CERTIFICATE-----\n'
168
+ 'MIIEVTCCAz2gAwIBAgIUNBhY/wH+Bj+O8Z8f6TwBtMFG/8kwDQYJKoZIhvcNAQEF\n'
169
+ 'BQAwYjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsT\n'
170
+ 'HUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBS\n'
171
+ 'b290IENBMB4XDTIyMTExNzIwNDA1M1oXDTIzMTExNzIwNDA1MlowdTELMAkGA1UE\n'
172
+ 'BhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAsMAkc3MUQwQgYDVQQD\n'
173
+ 'DDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0\n'
174
+ 'aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKyu\n'
175
+ '0dO2irEbKJWt3lFRTD8z4U5cr7P8AtJlTyrUdGiMdRdlzyjkSAmYcVIyLBZOeI6S\n'
176
+ 'VmSp3YvN4tTHO6ISRTcCGWJkL39hxtNZIr+r+RSj7baembov8bHcMEJPtrayxnSq\n'
177
+ 'Yla77UQ2D9HlIHSTVzpdntwB/HhvaRY1w24Bwp5y1HE2sXYJer4NKpfxsF4LGxKt\n'
178
+ 'K6sH32Mt9YjpMhKiVVhDdjw9F4AfKduxqZ+rlgWdFdzd204P5xN8WisuAkH27npq\n'
179
+ 'tnNg95cZFIuVMziT2gAlNq5VWnyf+fRiBAd06R2nlVcjrCsk2mRPKHLplrAIPIgb\n'
180
+ 'FGND14mumMHyLY7jUSUCAwEAAaOB7zCB7DASBgNVHRMBAf8ECDAGAQH/AgEAMB8G\n'
181
+ 'A1UdIwQYMBaAFCvQaUeUdgn+9GuNLkCm90dNfwheMEQGCCsGAQUFBwEBBDgwNjA0\n'
182
+ 'BggrBgEFBQcwAYYoaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwMy1hcHBsZXJv\n'
183
+ 'b3RjYTAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vY3JsLmFwcGxlLmNvbS9yb290\n'
184
+ 'LmNybDAdBgNVHQ4EFgQUXUIQbBu7x1KXTkS9Eye5OhJ3gyswDgYDVR0PAQH/BAQD\n'
185
+ 'AgEGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBSowgpE2W3\n'
186
+ 'tR/mNAPt9hh3vD3KJ7Vw7OxsM0v2mSWUB54hMwNq9X0KLivfCKmC3kp/4ecLSwW4\n'
187
+ 'J5hJ3cEMhteBZK6CnMRF8eqPHCIw46IlYUSJ/oV6VvByknwMRFQkt7WknybwMvlX\n'
188
+ 'nWp5bEDtDzQGBkL/2A4xZW3mLgHZBr/Fyg2uR9QFF4g86ZzkGWRtipStEdwB9uV4\n'
189
+ 'r63ocNcNXYE+RiosriShx9Lgfb8d9TZrxd6pCpqAsRFesmR+s8FXzMJsWZm39LDd\n'
190
+ 'MdpI1mqB7rKLUDUW5udccWJusPJR4qht+CrLaHPGpsQaQ0kBPqmpAIqGbIOI0lxw\n'
191
+ 'V3ra+HbMGdWw\n'
192
+ '-----END CERTIFICATE-----\n'
193
+ )
194
+
195
+ WWDR_G8 = (
196
+ '-----BEGIN CERTIFICATE-----\n'
197
+ 'MIIEVTCCAz2gAwIBAgIUVLULr3kNjX+Mr2hMVi9QaQoaul8wDQYJKoZIhvcNAQEF\n'
198
+ 'BQAwYjELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsT\n'
199
+ 'HUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBS\n'
200
+ 'b290IENBMB4XDTIzMDYyMDIzMzcxNVoXDTI1MDEyNDAwMDAwMFowdTELMAkGA1UE\n'
201
+ 'BhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAsMAkc4MUQwQgYDVQQD\n'
202
+ 'DDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0\n'
203
+ 'aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANBA\n'
204
+ 'ENQI+VIhY088aPfUnIICjINovLeNf4jnQk0s7yKlwonevQzXTWFQLTnkMHOl0tVo\n'
205
+ 'mjPy79kqrS4fA7r4pfFCC1cuRsbQWNNwX/eyN+9qHz6/iTnCrf71BftYljHIhyzV\n'
206
+ 'I7p1sCz1q6C68iAMTOskY2npIkDwjlhb3mR7iRtREgTgF7JZzd/x586vLDLoacHQ\n'
207
+ 'CH4dokdz0Us7/bmF3EenKIJ5KUiJAijiwewsH1uG/Ni2y3HAcwFL/AUREWwBAzRa\n'
208
+ '9oHCXh98FA7eP2shy0/112HmhAOSvOclKZ7NWwzB2+PEOtl2V6wvOBQZyLexolVP\n'
209
+ 'X06OGVmp2v1y2rAEIQUCAwEAAaOB7zCB7DASBgNVHRMBAf8ECDAGAQH/AgEAMB8G\n'
210
+ 'A1UdIwQYMBaAFCvQaUeUdgn+9GuNLkCm90dNfwheMEQGCCsGAQUFBwEBBDgwNjA0\n'
211
+ 'BggrBgEFBQcwAYYoaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwMy1hcHBsZXJv\n'
212
+ 'b3RjYTAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vY3JsLmFwcGxlLmNvbS9yb290\n'
213
+ 'LmNybDAdBgNVHQ4EFgQUtb28gMQM4zik9LetI7PvRM65WoUwDgYDVR0PAQH/BAQD\n'
214
+ 'AgEGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBMs+t6OZRK\n'
215
+ 'lWb6FjHqDYqPXUI4xgfN6MkirPwIQn5fk18xKqgiwXYZK+6ucum9Vs9JJJII980Z\n'
216
+ 'dcP5GicNDtwpjT+226VPTHLEYJGJEX4klUMiYGe83/+r5TwWF52CFE6d9HX+ULmt\n'
217
+ 'BbK4efaV1hDl9lP0zyPmdw/suEtp+OKeAjHZjtnKvmNeX+Ggac7BzW5Jo3hhrzk8\n'
218
+ 'aksKNCVk1TC1PKvdEYE5cejAw1iAERAaEdLCvFnwitk1c8DmbeTJfWIUPoICqRBp\n'
219
+ 'N3lhb/BGlD419ausY9DYXllXadG4S25d1F8TnHBOJRHcJCweFp6WWgTtRe467mdd\n'
220
+ 'j8OGsPVMH2gQ\n'
221
+ '-----END CERTIFICATE-----\n'
222
+ )
223
+
224
+ ROOT_CA = (
225
+ '-----BEGIN CERTIFICATE-----\n'
226
+ 'MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET\n'
227
+ 'MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv\n'
228
+ 'biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0\n'
229
+ 'MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw\n'
230
+ 'bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx\n'
231
+ 'FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\n'
232
+ 'ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+\n'
233
+ '+FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1\n'
234
+ 'XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w\n'
235
+ 'tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW\n'
236
+ 'q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM\n'
237
+ 'aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E\n'
238
+ 'BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3\n'
239
+ 'R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE\n'
240
+ 'ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93\n'
241
+ 'd3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl\n'
242
+ 'IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0\n'
243
+ 'YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj\n'
244
+ 'b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp\n'
245
+ 'Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc\n'
246
+ 'NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP\n'
247
+ 'y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7\n'
248
+ 'R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg\n'
249
+ 'xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP\n'
250
+ 'IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX\n'
251
+ 'UKqK1drk/NAJBzewdXUh\n'
252
+ '-----END CERTIFICATE-----\n'
253
+ )
254
+
255
+ ROOT_CA_G3 = (
256
+ '-----BEGIN CERTIFICATE-----\n'
257
+ 'MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS\n'
258
+ 'QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u\n'
259
+ 'IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN\n'
260
+ 'MTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS\n'
261
+ 'b290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y\n'
262
+ 'aXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49\n'
263
+ 'AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf\n'
264
+ 'TjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517\n'
265
+ 'IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr\n'
266
+ 'MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA\n'
267
+ 'MGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4\n'
268
+ 'at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM\n'
269
+ '6BgD56KyKA==\n'
270
+ '-----END CERTIFICATE-----\n'
271
+ )
272
+
273
+ WWDR_BY_ISSUER_HASH = {
274
+ 0x12dfbd5d: WWDR_G5,
275
+ 0x1eaac8b7: WWDR_G4,
276
+ 0x3da0a3ad: WWDR_G8,
277
+ 0x817d2f7a: WWDR_G1,
278
+ 0x975904ef: WWDR_G2,
279
+ 0x9b16b75c: WWDR_G3,
280
+ 0xdbc90301: WWDR_G7,
281
+ 0xf8ecc621: WWDR_G6,
282
+ }
283
+
284
+ ROOTS = (ROOT_CA, ROOT_CA_G3)
ipasign/_plist.py ADDED
@@ -0,0 +1,28 @@
1
+ """Plist serialisation matching what Apple's own tooling emits.
2
+
3
+ ``plistlib`` gets the structure right but writes every ``<real>`` with a decimal
4
+ part, so an integral weight comes out as ``1000.0`` where Apple writes ``1000``.
5
+ Signing hashes these bytes, so the difference would invalidate a seal. A single
6
+ post-processing pass closes the gap.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import plistlib
12
+ import re
13
+
14
+ _INTEGRAL_REAL = re.compile(rb"<real>(-?\d+)\.0</real>")
15
+
16
+
17
+ def dumps(value: object) -> bytes:
18
+ """Serialise ``value`` as an XML plist with insertion order preserved."""
19
+ raw = plistlib.dumps(value, fmt=plistlib.FMT_XML, sort_keys=False)
20
+ return _INTEGRAL_REAL.sub(rb"<real>\1</real>", raw)
21
+
22
+
23
+ def loads(data: bytes) -> object:
24
+ """Parse an XML or binary plist."""
25
+ return plistlib.loads(data)
26
+
27
+
28
+ __all__ = ["dumps", "loads"]
ipasign/archive.py ADDED
@@ -0,0 +1,173 @@
1
+ """IPA unpack and repack.
2
+
3
+ An ``.ipa`` is a zip holding a ``Payload`` folder with the app bundle inside.
4
+ Unpacking happens under a scratch directory named ``.ipasign_tmp`` beside the
5
+ input, so the work stays on the same filesystem and inside the project.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import shutil
12
+ import stat
13
+ import zipfile
14
+ from dataclasses import dataclass
15
+ from pathlib import Path
16
+
17
+ from .errors import ArchiveError
18
+
19
+ SCRATCH_DIR = ".ipasign_tmp"
20
+
21
+
22
+ def scratch_root(anchor: Path) -> Path:
23
+ """The scratch directory used for unpacking ``anchor``.
24
+
25
+ A file anchor uses its parent, so the work sits next to the input rather
26
+ than in a system temp location.
27
+ """
28
+ base = anchor if anchor.is_dir() else anchor.parent
29
+ return base / SCRATCH_DIR
30
+
31
+
32
+ def _safe_member(name: str) -> bool:
33
+ """Reject absolute paths and parent traversal in an archive member."""
34
+ if not name or name.startswith("/") or name.startswith("\\"):
35
+ return False
36
+ if ":" in name:
37
+ return False
38
+ return ".." not in name.replace("\\", "/").split("/")
39
+
40
+
41
+ @dataclass(slots=True)
42
+ class Unpacked:
43
+ """Where an archive was unpacked and what was found inside."""
44
+
45
+ root: Path
46
+ payload: Path
47
+ app: Path
48
+
49
+
50
+ def find_app(root: Path) -> Path:
51
+ """The single ``.app`` bundle under ``Payload``."""
52
+ payload = root / "Payload"
53
+ if not payload.is_dir():
54
+ raise ArchiveError(f"archive has no Payload directory: {root}")
55
+ apps = sorted(path for path in payload.iterdir() if path.name.endswith(".app"))
56
+ if not apps:
57
+ raise ArchiveError(f"Payload holds no .app bundle: {payload}")
58
+ if len(apps) > 1:
59
+ raise ArchiveError(f"Payload holds more than one .app bundle: {payload}")
60
+ return apps[0]
61
+
62
+
63
+ def unpack(ipa_path: Path, work_dir: Path | None = None) -> Unpacked:
64
+ """Extract an ``.ipa`` into ``work_dir`` and locate the app bundle."""
65
+ ipa_path = Path(ipa_path)
66
+ if not ipa_path.is_file():
67
+ raise ArchiveError(f"archive not found: {ipa_path}")
68
+
69
+ if work_dir is None:
70
+ root = scratch_root(ipa_path) / ipa_path.stem
71
+ else:
72
+ root = Path(work_dir)
73
+
74
+ if root.exists():
75
+ shutil.rmtree(root)
76
+ root.mkdir(parents=True, exist_ok=True)
77
+
78
+ try:
79
+ with zipfile.ZipFile(ipa_path) as archive:
80
+ for info in archive.infolist():
81
+ if not _safe_member(info.filename):
82
+ raise ArchiveError(f"archive holds an unsafe path: {info.filename}")
83
+ archive.extract(info, root)
84
+ _restore_mode(root / info.filename, info)
85
+ except zipfile.BadZipFile as exc:
86
+ shutil.rmtree(root, ignore_errors=True)
87
+ raise ArchiveError(f"{ipa_path} is not a zip archive: {exc}") from exc
88
+ except Exception:
89
+ shutil.rmtree(root, ignore_errors=True)
90
+ raise
91
+
92
+ return Unpacked(root=root, payload=root / "Payload", app=find_app(root))
93
+
94
+
95
+ def _restore_mode(path: Path, info: zipfile.ZipInfo) -> None:
96
+ """Give extracted files the permissions the archive recorded.
97
+
98
+ Mach-O executables must stay executable, and zipfile only applies the mode
99
+ when it is told to.
100
+ """
101
+ if info.is_dir():
102
+ return
103
+ mode = info.external_attr >> 16
104
+ if not mode:
105
+ return
106
+ try:
107
+ path.chmod(stat.S_IMODE(mode))
108
+ except OSError:
109
+ pass
110
+
111
+
112
+ def pack(source: Path, output: Path) -> Path:
113
+ """Zip everything under ``source`` into ``output``.
114
+
115
+ Directory entries are written first so an unzip that ignores ordering still
116
+ creates the tree. Files keep their recorded modification time.
117
+ """
118
+ source = Path(source)
119
+ output = Path(output)
120
+ output.parent.mkdir(parents=True, exist_ok=True)
121
+
122
+ temp = output.with_name(output.name + ".is_tmp")
123
+ try:
124
+ with zipfile.ZipFile(temp, "w", zipfile.ZIP_DEFLATED, compresslevel=6) as archive:
125
+ for dirpath, dirnames, filenames in os.walk(source):
126
+ dirnames.sort()
127
+ filenames.sort()
128
+ for name in dirnames:
129
+ path = Path(dirpath) / name
130
+ info = zipfile.ZipInfo(path.relative_to(source).as_posix() + "/")
131
+ info.external_attr = (stat.S_IFDIR | 0o755) << 16 | 0x10
132
+ info.date_time = _zip_time(path)
133
+ archive.writestr(info, b"")
134
+ for name in filenames:
135
+ path = Path(dirpath) / name
136
+ info = zipfile.ZipInfo(path.relative_to(source).as_posix())
137
+ info.external_attr = (stat.S_IFREG | stat.S_IMODE(path.stat().st_mode)) << 16
138
+ info.date_time = _zip_time(path)
139
+ info.compress_type = zipfile.ZIP_DEFLATED
140
+ with open(path, "rb") as handle:
141
+ archive.writestr(info, handle.read())
142
+ os.replace(temp, output)
143
+ except Exception:
144
+ try:
145
+ temp.unlink()
146
+ except OSError:
147
+ pass
148
+ raise
149
+ return output
150
+
151
+
152
+ def _zip_time(path: Path) -> tuple[int, int, int, int, int, int]:
153
+ """DOS timestamp for a path, clamped to the range the zip format allows."""
154
+ stamp = path.stat().st_mtime
155
+ import time
156
+
157
+ local = time.localtime(stamp)
158
+ year = max(1980, min(local.tm_year, 2107))
159
+ return (year, local.tm_mon, local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec)
160
+
161
+
162
+ def cleanup(root: Path) -> None:
163
+ """Remove a scratch directory, pruning the ``.ipasign_tmp`` root when empty."""
164
+ shutil.rmtree(root, ignore_errors=True)
165
+ parent = root.parent
166
+ if parent.name == SCRATCH_DIR and parent.is_dir() and not any(parent.iterdir()):
167
+ try:
168
+ parent.rmdir()
169
+ except OSError:
170
+ pass
171
+
172
+
173
+ __all__ = ["SCRATCH_DIR", "Unpacked", "cleanup", "find_app", "pack", "scratch_root", "unpack"]