Skip to content

Commit 4e213ef

Browse files
committed
Increase speed of build methods
1 parent f0b8d92 commit 4e213ef

File tree

2 files changed

+87
-113
lines changed

2 files changed

+87
-113
lines changed

Sources/WalletOrders/OrderBuilder.swift

+31-44
Original file line numberDiff line numberDiff line change
@@ -38,59 +38,30 @@ public struct OrderBuilder: Sendable {
3838
self.openSSLURL = URL(fileURLWithPath: openSSLPath)
3939
}
4040

41-
private static func sourceFiles(in directory: URL) throws -> [String: Data] {
42-
var files: [String: Data] = [:]
43-
44-
let paths = try FileManager.default.subpathsOfDirectory(atPath: directory.path)
45-
46-
for relativePath in paths {
47-
let file = URL(fileURLWithPath: relativePath, relativeTo: directory)
48-
guard !file.hasDirectoryPath else {
49-
continue
50-
}
51-
52-
guard !(file.lastPathComponent == ".gitkeep" || file.lastPathComponent == ".DS_Store") else {
53-
continue
54-
}
55-
56-
files[relativePath] = try Data(contentsOf: file)
57-
}
58-
59-
return files
60-
}
61-
62-
private func manifest(for sourceFiles: [String: Data]) throws -> Data {
63-
let manifest = sourceFiles.mapValues { data in
64-
SHA256.hash(data: data).map { "0\(String($0, radix: 16))".suffix(2) }.joined()
65-
}
66-
67-
return try self.encoder.encode(manifest)
68-
}
69-
7041
private func signature(for manifest: Data) throws -> Data {
7142
// Swift Crypto doesn't support encrypted PEM private keys, so we have to use OpenSSL for that.
7243
if let pemPrivateKeyPassword {
7344
guard FileManager.default.fileExists(atPath: self.openSSLURL.path) else {
7445
throw WalletOrdersError.noOpenSSLExecutable
7546
}
7647

77-
let dir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
78-
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
79-
defer { try? FileManager.default.removeItem(at: dir) }
48+
let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
49+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
50+
defer { try? FileManager.default.removeItem(at: tempDir) }
8051

81-
let manifestURL = dir.appendingPathComponent(Self.manifestFileName)
82-
let wwdrURL = dir.appendingPathComponent("wwdr.pem")
83-
let certificateURL = dir.appendingPathComponent("certificate.pem")
84-
let privateKeyURL = dir.appendingPathComponent("private.pem")
85-
let signatureURL = dir.appendingPathComponent(Self.signatureFileName)
52+
let manifestURL = tempDir.appendingPathComponent(Self.manifestFileName)
53+
let wwdrURL = tempDir.appendingPathComponent("wwdr.pem")
54+
let certificateURL = tempDir.appendingPathComponent("certificate.pem")
55+
let privateKeyURL = tempDir.appendingPathComponent("private.pem")
56+
let signatureURL = tempDir.appendingPathComponent(Self.signatureFileName)
8657

8758
try manifest.write(to: manifestURL)
8859
try self.pemWWDRCertificate.write(to: wwdrURL, atomically: true, encoding: .utf8)
8960
try self.pemCertificate.write(to: certificateURL, atomically: true, encoding: .utf8)
9061
try self.pemPrivateKey.write(to: privateKeyURL, atomically: true, encoding: .utf8)
9162

9263
let process = Process()
93-
process.currentDirectoryURL = dir
64+
process.currentDirectoryURL = tempDir
9465
process.executableURL = self.openSSLURL
9566
process.arguments = [
9667
"smime", "-binary", "-sign",
@@ -149,16 +120,32 @@ public struct OrderBuilder: Sendable {
149120
try orderJSON.write(to: tempDir.appendingPathComponent("order.json"))
150121
archiveFiles.append(ArchiveFile(filename: "order.json", data: orderJSON))
151122

152-
let sourceFiles = try Self.sourceFiles(in: tempDir)
123+
let sourceFilesPaths = try FileManager.default.subpathsOfDirectory(atPath: tempDir.path)
124+
125+
var manifestJSON: [String: String] = [:]
153126

154-
let manifest = try self.manifest(for: sourceFiles)
155-
archiveFiles.append(ArchiveFile(filename: Self.manifestFileName, data: manifest))
156-
try archiveFiles.append(ArchiveFile(filename: Self.signatureFileName, data: self.signature(for: manifest)))
127+
for relativePath in sourceFilesPaths {
128+
let fileURL = URL(fileURLWithPath: relativePath, relativeTo: tempDir)
129+
130+
guard !fileURL.hasDirectoryPath else {
131+
continue
132+
}
157133

158-
for file in sourceFiles {
159-
archiveFiles.append(ArchiveFile(filename: file.key, data: file.value))
134+
guard !(fileURL.lastPathComponent == ".gitkeep" || fileURL.lastPathComponent == ".DS_Store") else {
135+
continue
136+
}
137+
138+
let fileData = try Data(contentsOf: fileURL)
139+
140+
archiveFiles.append(ArchiveFile(filename: relativePath, data: fileData))
141+
142+
manifestJSON[relativePath] = SHA256.hash(data: fileData).map { "0\(String($0, radix: 16))".suffix(2) }.joined()
160143
}
161144

145+
let manifestData = try self.encoder.encode(manifestJSON)
146+
archiveFiles.append(ArchiveFile(filename: Self.manifestFileName, data: manifestData))
147+
try archiveFiles.append(ArchiveFile(filename: Self.signatureFileName, data: self.signature(for: manifestData)))
148+
162149
let zipFile = tempDir.appendingPathComponent("\(UUID().uuidString).order")
163150
try Zip.zipData(archiveFiles: archiveFiles, zipFilePath: zipFile)
164151
return try Data(contentsOf: zipFile)

Sources/WalletPasses/PassBuilder.swift

+56-69
Original file line numberDiff line numberDiff line change
@@ -38,84 +38,35 @@ public struct PassBuilder: Sendable {
3838
self.openSSLURL = URL(fileURLWithPath: openSSLPath)
3939
}
4040

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.
9142
///
92-
/// - Parameter manifest: The manifest or personalization token data to sign.
43+
/// - Parameter data: The personalization token data to sign.
9344
///
9445
/// - Returns: The generated signature as `Data`.
95-
public func signature(for manifest: Data) throws -> Data {
46+
public func signature(for data: Data) throws -> Data {
9647
// Swift Crypto doesn't support encrypted PEM private keys, so we have to use OpenSSL for that.
9748
if let pemPrivateKeyPassword {
9849
guard FileManager.default.fileExists(atPath: self.openSSLURL.path) else {
9950
throw WalletPassesError.noOpenSSLExecutable
10051
}
10152

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) }
10556

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)
11162

112-
try manifest.write(to: manifestURL)
63+
try data.write(to: manifestURL)
11364
try self.pemWWDRCertificate.write(to: wwdrURL, atomically: true, encoding: .utf8)
11465
try self.pemCertificate.write(to: certificateURL, atomically: true, encoding: .utf8)
11566
try self.pemPrivateKey.write(to: privateKeyURL, atomically: true, encoding: .utf8)
11667

11768
let process = Process()
118-
process.currentDirectoryURL = dir
69+
process.currentDirectoryURL = tempDir
11970
process.executableURL = self.openSSLURL
12071
process.arguments = [
12172
"smime", "-binary", "-sign",
@@ -133,7 +84,7 @@ public struct PassBuilder: Sendable {
13384
return try Data(contentsOf: signatureURL)
13485
} else {
13586
let signature = try CMS.sign(
136-
manifest,
87+
data,
13788
signatureAlgorithm: .sha256WithRSAEncryption,
13889
additionalIntermediateCertificates: [
13990
Certificate(pemEncoded: self.pemWWDRCertificate)
@@ -183,16 +134,52 @@ public struct PassBuilder: Sendable {
183134
archiveFiles.append(ArchiveFile(filename: "personalization.json", data: personalizationJSONData))
184135
}
185136

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)
187173

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))
191175

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()
194177
}
195178

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+
196183
let zipFile = tempDir.appendingPathComponent("\(UUID().uuidString).pkpass")
197184
try Zip.zipData(archiveFiles: archiveFiles, zipFilePath: zipFile)
198185
return try Data(contentsOf: zipFile)

0 commit comments

Comments
 (0)