@@ -38,84 +38,35 @@ public struct PassBuilder: Sendable {
38
38
self . openSSLURL = URL ( fileURLWithPath: openSSLPath)
39
39
}
40
40
41
- private static func sourceFiles( in directory: URL , isPersonalized: Bool = false ) throws -> [ String : Data ] {
42
- var files : [ String : Data ] = [ : ]
43
-
44
- let paths = try FileManager . default. subpathsOfDirectory ( atPath: directory. path)
45
-
46
- if isPersonalized {
47
- guard
48
- paths. contains ( " personalizationLogo.png " )
49
- || paths
. contains ( " [email protected] " )
50
- || paths
. contains ( " [email protected] " )
51
- || paths
. contains ( " [email protected] " )
52
- else {
53
- throw WalletPassesError . noPersonalizationLogo
54
- }
55
- }
56
-
57
- guard
58
- paths. contains ( " icon.png " )
59
- || paths
. contains ( " [email protected] " )
60
- || paths
. contains ( " [email protected] " )
61
- || paths
. contains ( " [email protected] " )
62
- else {
63
- throw WalletPassesError . noIcon
64
- }
65
-
66
- for relativePath in paths {
67
- let file = URL ( fileURLWithPath: relativePath, relativeTo: directory)
68
- guard !file. hasDirectoryPath else {
69
- continue
70
- }
71
-
72
- guard !( file. lastPathComponent == " .gitkeep " || file. lastPathComponent == " .DS_Store " ) else {
73
- continue
74
- }
75
-
76
- files [ relativePath] = try Data ( contentsOf: file)
77
- }
78
-
79
- return files
80
- }
81
-
82
- private func manifest( for sourceFiles: [ String : Data ] ) throws -> Data {
83
- let manifest = sourceFiles. mapValues { data in
84
- Insecure . SHA1. hash ( data: data) . map { " 0 \( String ( $0, radix: 16 ) ) " . suffix ( 2 ) } . joined ( )
85
- }
86
-
87
- return try self . encoder. encode ( manifest)
88
- }
89
-
90
- /// Generates a signature for a given manifest or personalization token.
41
+ /// Generates a signature for a given personalization token.
91
42
///
92
- /// - Parameter manifest : The manifest or personalization token data to sign.
43
+ /// - Parameter data : The personalization token data to sign.
93
44
///
94
45
/// - Returns: The generated signature as `Data`.
95
- public func signature( for manifest : Data ) throws -> Data {
46
+ public func signature( for data : Data ) throws -> Data {
96
47
// Swift Crypto doesn't support encrypted PEM private keys, so we have to use OpenSSL for that.
97
48
if let pemPrivateKeyPassword {
98
49
guard FileManager . default. fileExists ( atPath: self . openSSLURL. path) else {
99
50
throw WalletPassesError . noOpenSSLExecutable
100
51
}
101
52
102
- let dir = FileManager . default. temporaryDirectory. appendingPathComponent ( UUID ( ) . uuidString, isDirectory: true )
103
- try FileManager . default. createDirectory ( at: dir , withIntermediateDirectories: true )
104
- defer { try ? FileManager . default. removeItem ( at: dir ) }
53
+ let tempDir = FileManager . default. temporaryDirectory. appendingPathComponent ( UUID ( ) . uuidString, isDirectory: true )
54
+ try FileManager . default. createDirectory ( at: tempDir , withIntermediateDirectories: true )
55
+ defer { try ? FileManager . default. removeItem ( at: tempDir ) }
105
56
106
- let manifestURL = dir . appendingPathComponent ( Self . manifestFileName)
107
- let wwdrURL = dir . appendingPathComponent ( " wwdr.pem " )
108
- let certificateURL = dir . appendingPathComponent ( " certificate.pem " )
109
- let privateKeyURL = dir . appendingPathComponent ( " private.pem " )
110
- let signatureURL = dir . appendingPathComponent ( Self . signatureFileName)
57
+ let manifestURL = tempDir . appendingPathComponent ( Self . manifestFileName)
58
+ let wwdrURL = tempDir . appendingPathComponent ( " wwdr.pem " )
59
+ let certificateURL = tempDir . appendingPathComponent ( " certificate.pem " )
60
+ let privateKeyURL = tempDir . appendingPathComponent ( " private.pem " )
61
+ let signatureURL = tempDir . appendingPathComponent ( Self . signatureFileName)
111
62
112
- try manifest . write ( to: manifestURL)
63
+ try data . write ( to: manifestURL)
113
64
try self . pemWWDRCertificate. write ( to: wwdrURL, atomically: true , encoding: . utf8)
114
65
try self . pemCertificate. write ( to: certificateURL, atomically: true , encoding: . utf8)
115
66
try self . pemPrivateKey. write ( to: privateKeyURL, atomically: true , encoding: . utf8)
116
67
117
68
let process = Process ( )
118
- process. currentDirectoryURL = dir
69
+ process. currentDirectoryURL = tempDir
119
70
process. executableURL = self . openSSLURL
120
71
process. arguments = [
121
72
" smime " , " -binary " , " -sign " ,
@@ -133,7 +84,7 @@ public struct PassBuilder: Sendable {
133
84
return try Data ( contentsOf: signatureURL)
134
85
} else {
135
86
let signature = try CMS . sign (
136
- manifest ,
87
+ data ,
137
88
signatureAlgorithm: . sha256WithRSAEncryption,
138
89
additionalIntermediateCertificates: [
139
90
Certificate ( pemEncoded: self . pemWWDRCertificate)
@@ -183,16 +134,52 @@ public struct PassBuilder: Sendable {
183
134
archiveFiles. append ( ArchiveFile ( filename: " personalization.json " , data: personalizationJSONData) )
184
135
}
185
136
186
- let sourceFiles = try Self . sourceFiles ( in: tempDir, isPersonalized: personalization != nil )
137
+ let sourceFilesPaths = try FileManager . default. subpathsOfDirectory ( atPath: tempDir. path)
138
+
139
+ if personalization != nil {
140
+ guard
141
+ sourceFilesPaths. contains ( " personalizationLogo.png " )
142
+ || sourceFilesPaths
. contains ( " [email protected] " )
143
+ || sourceFilesPaths
. contains ( " [email protected] " )
144
+ || sourceFilesPaths
. contains ( " [email protected] " )
145
+ else {
146
+ throw WalletPassesError . noPersonalizationLogo
147
+ }
148
+ }
149
+
150
+ guard
151
+ sourceFilesPaths. contains ( " icon.png " )
152
+ || sourceFilesPaths
. contains ( " [email protected] " )
153
+ || sourceFilesPaths
. contains ( " [email protected] " )
154
+ || sourceFilesPaths
. contains ( " [email protected] " )
155
+ else {
156
+ throw WalletPassesError . noIcon
157
+ }
158
+
159
+ var manifestJSON : [ String : String ] = [ : ]
160
+
161
+ for relativePath in sourceFilesPaths {
162
+ let fileURL = URL ( fileURLWithPath: relativePath, relativeTo: tempDir)
163
+
164
+ guard !fileURL. hasDirectoryPath else {
165
+ continue
166
+ }
167
+
168
+ guard !( fileURL. lastPathComponent == " .gitkeep " || fileURL. lastPathComponent == " .DS_Store " ) else {
169
+ continue
170
+ }
171
+
172
+ let fileData = try Data ( contentsOf: fileURL)
187
173
188
- let manifest = try self . manifest ( for: sourceFiles)
189
- archiveFiles. append ( ArchiveFile ( filename: Self . manifestFileName, data: manifest) )
190
- try archiveFiles. append ( ArchiveFile ( filename: Self . signatureFileName, data: self . signature ( for: manifest) ) )
174
+ archiveFiles. append ( ArchiveFile ( filename: relativePath, data: fileData) )
191
175
192
- for file in sourceFiles {
193
- archiveFiles. append ( ArchiveFile ( filename: file. key, data: file. value) )
176
+ manifestJSON [ relativePath] = Insecure . SHA1. hash ( data: fileData) . map { " 0 \( String ( $0, radix: 16 ) ) " . suffix ( 2 ) } . joined ( )
194
177
}
195
178
179
+ let manifestData = try self . encoder. encode ( manifestJSON)
180
+ archiveFiles. append ( ArchiveFile ( filename: Self . manifestFileName, data: manifestData) )
181
+ try archiveFiles. append ( ArchiveFile ( filename: Self . signatureFileName, data: self . signature ( for: manifestData) ) )
182
+
196
183
let zipFile = tempDir. appendingPathComponent ( " \( UUID ( ) . uuidString) .pkpass " )
197
184
try Zip . zipData ( archiveFiles: archiveFiles, zipFilePath: zipFile)
198
185
return try Data ( contentsOf: zipFile)
0 commit comments