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 +40 -0
- ipasign/_apple_ca.py +284 -0
- ipasign/_plist.py +28 -0
- ipasign/archive.py +173 -0
- ipasign/blobs.py +362 -0
- ipasign/bundle.py +282 -0
- ipasign/cms.py +164 -0
- ipasign/credentials.py +259 -0
- ipasign/errors.py +56 -0
- ipasign/key.py +251 -0
- ipasign/macho.py +504 -0
- ipasign/signer.py +275 -0
- ipasign-1.0.dist-info/METADATA +96 -0
- ipasign-1.0.dist-info/RECORD +17 -0
- ipasign-1.0.dist-info/WHEEL +5 -0
- ipasign-1.0.dist-info/licenses/LICENSE +21 -0
- ipasign-1.0.dist-info/top_level.txt +1 -0
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"]
|