Skip to content

Commit

Permalink
Increase speed of build methods
Browse files Browse the repository at this point in the history
  • Loading branch information
fpseverino committed Jan 10, 2025
1 parent f0b8d92 commit 4e213ef
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 113 deletions.
75 changes: 31 additions & 44 deletions Sources/WalletOrders/OrderBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,59 +38,30 @@ public struct OrderBuilder: Sendable {
self.openSSLURL = URL(fileURLWithPath: openSSLPath)
}

private static func sourceFiles(in directory: URL) throws -> [String: Data] {
var files: [String: Data] = [:]

let paths = try FileManager.default.subpathsOfDirectory(atPath: directory.path)

for relativePath in paths {
let file = URL(fileURLWithPath: relativePath, relativeTo: directory)
guard !file.hasDirectoryPath else {
continue
}

guard !(file.lastPathComponent == ".gitkeep" || file.lastPathComponent == ".DS_Store") else {
continue
}

files[relativePath] = try Data(contentsOf: file)
}

return files
}

private func manifest(for sourceFiles: [String: Data]) throws -> Data {
let manifest = sourceFiles.mapValues { data in
SHA256.hash(data: data).map { "0\(String($0, radix: 16))".suffix(2) }.joined()
}

return try self.encoder.encode(manifest)
}

private func signature(for manifest: Data) throws -> Data {
// Swift Crypto doesn't support encrypted PEM private keys, so we have to use OpenSSL for that.
if let pemPrivateKeyPassword {
guard FileManager.default.fileExists(atPath: self.openSSLURL.path) else {
throw WalletOrdersError.noOpenSSLExecutable
}

let dir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: dir) }
let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: tempDir) }

let manifestURL = dir.appendingPathComponent(Self.manifestFileName)
let wwdrURL = dir.appendingPathComponent("wwdr.pem")
let certificateURL = dir.appendingPathComponent("certificate.pem")
let privateKeyURL = dir.appendingPathComponent("private.pem")
let signatureURL = dir.appendingPathComponent(Self.signatureFileName)
let manifestURL = tempDir.appendingPathComponent(Self.manifestFileName)
let wwdrURL = tempDir.appendingPathComponent("wwdr.pem")
let certificateURL = tempDir.appendingPathComponent("certificate.pem")
let privateKeyURL = tempDir.appendingPathComponent("private.pem")
let signatureURL = tempDir.appendingPathComponent(Self.signatureFileName)

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

let process = Process()
process.currentDirectoryURL = dir
process.currentDirectoryURL = tempDir
process.executableURL = self.openSSLURL
process.arguments = [
"smime", "-binary", "-sign",
Expand Down Expand Up @@ -149,16 +120,32 @@ public struct OrderBuilder: Sendable {
try orderJSON.write(to: tempDir.appendingPathComponent("order.json"))
archiveFiles.append(ArchiveFile(filename: "order.json", data: orderJSON))

let sourceFiles = try Self.sourceFiles(in: tempDir)
let sourceFilesPaths = try FileManager.default.subpathsOfDirectory(atPath: tempDir.path)

var manifestJSON: [String: String] = [:]

let manifest = try self.manifest(for: sourceFiles)
archiveFiles.append(ArchiveFile(filename: Self.manifestFileName, data: manifest))
try archiveFiles.append(ArchiveFile(filename: Self.signatureFileName, data: self.signature(for: manifest)))
for relativePath in sourceFilesPaths {
let fileURL = URL(fileURLWithPath: relativePath, relativeTo: tempDir)

guard !fileURL.hasDirectoryPath else {
continue
}

for file in sourceFiles {
archiveFiles.append(ArchiveFile(filename: file.key, data: file.value))
guard !(fileURL.lastPathComponent == ".gitkeep" || fileURL.lastPathComponent == ".DS_Store") else {
continue
}

let fileData = try Data(contentsOf: fileURL)

archiveFiles.append(ArchiveFile(filename: relativePath, data: fileData))

manifestJSON[relativePath] = SHA256.hash(data: fileData).map { "0\(String($0, radix: 16))".suffix(2) }.joined()
}

let manifestData = try self.encoder.encode(manifestJSON)
archiveFiles.append(ArchiveFile(filename: Self.manifestFileName, data: manifestData))
try archiveFiles.append(ArchiveFile(filename: Self.signatureFileName, data: self.signature(for: manifestData)))

let zipFile = tempDir.appendingPathComponent("\(UUID().uuidString).order")
try Zip.zipData(archiveFiles: archiveFiles, zipFilePath: zipFile)
return try Data(contentsOf: zipFile)
Expand Down
125 changes: 56 additions & 69 deletions Sources/WalletPasses/PassBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,84 +38,35 @@ public struct PassBuilder: Sendable {
self.openSSLURL = URL(fileURLWithPath: openSSLPath)
}

private static func sourceFiles(in directory: URL, isPersonalized: Bool = false) throws -> [String: Data] {
var files: [String: Data] = [:]

let paths = try FileManager.default.subpathsOfDirectory(atPath: directory.path)

if isPersonalized {
guard
paths.contains("personalizationLogo.png")
|| paths.contains("[email protected]")
|| paths.contains("[email protected]")
|| paths.contains("[email protected]")
else {
throw WalletPassesError.noPersonalizationLogo
}
}

guard
paths.contains("icon.png")
|| paths.contains("[email protected]")
|| paths.contains("[email protected]")
|| paths.contains("[email protected]")
else {
throw WalletPassesError.noIcon
}

for relativePath in paths {
let file = URL(fileURLWithPath: relativePath, relativeTo: directory)
guard !file.hasDirectoryPath else {
continue
}

guard !(file.lastPathComponent == ".gitkeep" || file.lastPathComponent == ".DS_Store") else {
continue
}

files[relativePath] = try Data(contentsOf: file)
}

return files
}

private func manifest(for sourceFiles: [String: Data]) throws -> Data {
let manifest = sourceFiles.mapValues { data in
Insecure.SHA1.hash(data: data).map { "0\(String($0, radix: 16))".suffix(2) }.joined()
}

return try self.encoder.encode(manifest)
}

/// Generates a signature for a given manifest or personalization token.
/// Generates a signature for a given personalization token.
///
/// - Parameter manifest: The manifest or personalization token data to sign.
/// - Parameter data: The personalization token data to sign.
///
/// - Returns: The generated signature as `Data`.
public func signature(for manifest: Data) throws -> Data {
public func signature(for data: Data) throws -> Data {
// Swift Crypto doesn't support encrypted PEM private keys, so we have to use OpenSSL for that.
if let pemPrivateKeyPassword {
guard FileManager.default.fileExists(atPath: self.openSSLURL.path) else {
throw WalletPassesError.noOpenSSLExecutable
}

let dir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: dir) }
let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: tempDir) }

let manifestURL = dir.appendingPathComponent(Self.manifestFileName)
let wwdrURL = dir.appendingPathComponent("wwdr.pem")
let certificateURL = dir.appendingPathComponent("certificate.pem")
let privateKeyURL = dir.appendingPathComponent("private.pem")
let signatureURL = dir.appendingPathComponent(Self.signatureFileName)
let manifestURL = tempDir.appendingPathComponent(Self.manifestFileName)
let wwdrURL = tempDir.appendingPathComponent("wwdr.pem")
let certificateURL = tempDir.appendingPathComponent("certificate.pem")
let privateKeyURL = tempDir.appendingPathComponent("private.pem")
let signatureURL = tempDir.appendingPathComponent(Self.signatureFileName)

try manifest.write(to: manifestURL)
try data.write(to: manifestURL)
try self.pemWWDRCertificate.write(to: wwdrURL, atomically: true, encoding: .utf8)
try self.pemCertificate.write(to: certificateURL, atomically: true, encoding: .utf8)
try self.pemPrivateKey.write(to: privateKeyURL, atomically: true, encoding: .utf8)

let process = Process()
process.currentDirectoryURL = dir
process.currentDirectoryURL = tempDir
process.executableURL = self.openSSLURL
process.arguments = [
"smime", "-binary", "-sign",
Expand All @@ -133,7 +84,7 @@ public struct PassBuilder: Sendable {
return try Data(contentsOf: signatureURL)
} else {
let signature = try CMS.sign(
manifest,
data,
signatureAlgorithm: .sha256WithRSAEncryption,
additionalIntermediateCertificates: [
Certificate(pemEncoded: self.pemWWDRCertificate)
Expand Down Expand Up @@ -183,16 +134,52 @@ public struct PassBuilder: Sendable {
archiveFiles.append(ArchiveFile(filename: "personalization.json", data: personalizationJSONData))
}

let sourceFiles = try Self.sourceFiles(in: tempDir, isPersonalized: personalization != nil)
let sourceFilesPaths = try FileManager.default.subpathsOfDirectory(atPath: tempDir.path)

if personalization != nil {
guard
sourceFilesPaths.contains("personalizationLogo.png")
|| sourceFilesPaths.contains("[email protected]")
|| sourceFilesPaths.contains("[email protected]")
|| sourceFilesPaths.contains("[email protected]")
else {
throw WalletPassesError.noPersonalizationLogo
}
}

guard
sourceFilesPaths.contains("icon.png")
|| sourceFilesPaths.contains("[email protected]")
|| sourceFilesPaths.contains("[email protected]")
|| sourceFilesPaths.contains("[email protected]")
else {
throw WalletPassesError.noIcon
}

var manifestJSON: [String: String] = [:]

for relativePath in sourceFilesPaths {
let fileURL = URL(fileURLWithPath: relativePath, relativeTo: tempDir)

guard !fileURL.hasDirectoryPath else {
continue
}

guard !(fileURL.lastPathComponent == ".gitkeep" || fileURL.lastPathComponent == ".DS_Store") else {
continue
}

let fileData = try Data(contentsOf: fileURL)

let manifest = try self.manifest(for: sourceFiles)
archiveFiles.append(ArchiveFile(filename: Self.manifestFileName, data: manifest))
try archiveFiles.append(ArchiveFile(filename: Self.signatureFileName, data: self.signature(for: manifest)))
archiveFiles.append(ArchiveFile(filename: relativePath, data: fileData))

for file in sourceFiles {
archiveFiles.append(ArchiveFile(filename: file.key, data: file.value))
manifestJSON[relativePath] = Insecure.SHA1.hash(data: fileData).map { "0\(String($0, radix: 16))".suffix(2) }.joined()
}

let manifestData = try self.encoder.encode(manifestJSON)
archiveFiles.append(ArchiveFile(filename: Self.manifestFileName, data: manifestData))
try archiveFiles.append(ArchiveFile(filename: Self.signatureFileName, data: self.signature(for: manifestData)))

let zipFile = tempDir.appendingPathComponent("\(UUID().uuidString).pkpass")
try Zip.zipData(archiveFiles: archiveFiles, zipFilePath: zipFile)
return try Data(contentsOf: zipFile)
Expand Down

0 comments on commit 4e213ef

Please sign in to comment.