mirror of
https://github.com/xtool-org/xtool.git
synced 2026-11-15 12:04:55 +03:00
64fe8e30b3
## What does this PR do? Replaces zsign with the [apple-codesign](https://github.com/indygreg/apple-platform-rs/tree/0ebbd2ef3b96d5adccc30b1cad4deea38cd735c4/apple-codesign) Rust crate. It's much more usable as a library (while zsign has bare prints all over the place + we had to fork it to maintain a public interface), it supports macOS-style bundles, and also has other features that we would've needed to further patch zsign for. ## How was it tested? - [x] Ran an ad-hoc signed app on the simulator (with a subsequent PR) - [x] Ran an app with App Groups on device, confirmed that the app group is present ## AI tool usage How much of this PR was AI-assisted? (check one) - [ ] **0** - No AI was used to write code - [x] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Updated Signing** * App signing now uses an updated signing engine and supports certificate-based and ad hoc signing. * Entitlement data, including data provided through entitlement files, is passed through during signing. * Deep signing is enabled in the development command. * **Validation** * Signing rejects invalid app or entitlement file paths and duplicate entitlement paths. * **Compatibility** * Signing contexts can be created without selecting a signer. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
103 lines
3.6 KiB
Swift
103 lines
3.6 KiB
Swift
import Foundation
|
|
import Dependencies
|
|
|
|
/// Provisions and codesigns.
|
|
public struct AutoSigner {
|
|
|
|
@Dependency(\.signer) private var signer
|
|
|
|
public enum Error: LocalizedError {
|
|
case errorReading(String)
|
|
case errorWriting(String)
|
|
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case .errorReading(let file):
|
|
return "\(file)".withCString {
|
|
String.localizedStringWithFormat(
|
|
NSLocalizedString(
|
|
"signer.error.error_reading", value: "Error while reading %s", comment: ""
|
|
), $0
|
|
)
|
|
}
|
|
case .errorWriting(let file):
|
|
return "\(file)".withCString {
|
|
String.localizedStringWithFormat(
|
|
NSLocalizedString(
|
|
"signer.error.error_writing", value: "Error while writing %s", comment: ""
|
|
), $0
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public let context: SigningContext
|
|
public let confirmRevocation: @Sendable ([DeveloperServicesCertificate]) async -> Bool
|
|
public init(
|
|
context: SigningContext,
|
|
confirmRevocation: @escaping @Sendable ([DeveloperServicesCertificate]) async -> Bool
|
|
) {
|
|
self.context = context
|
|
self.confirmRevocation = confirmRevocation
|
|
}
|
|
|
|
public func sign(
|
|
app: URL,
|
|
status: @escaping (String) -> Void,
|
|
progress: @escaping @Sendable (Double?) -> Void,
|
|
didProvision: @escaping () throws -> Void = {}
|
|
) async throws -> String {
|
|
status(NSLocalizedString("signer.provisioning", value: "Provisioning", comment: ""))
|
|
let response = try await DeveloperServicesProvisioningOperation(
|
|
context: context,
|
|
app: app,
|
|
confirmRevocation: confirmRevocation,
|
|
progress: progress
|
|
).perform()
|
|
|
|
let provisioningDict = response.provisioningDict
|
|
let signingInfo = response.signingInfo
|
|
guard let mainInfo = provisioningDict[app] else {
|
|
throw Error.errorReading("app bundle ID")
|
|
}
|
|
|
|
try didProvision()
|
|
|
|
for (url, info) in provisioningDict {
|
|
let infoPlist = url.appendingPathComponent("Info.plist")
|
|
guard let data = try? Data(contentsOf: infoPlist),
|
|
let dict = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any]
|
|
else { throw Error.errorReading(infoPlist.lastPathComponent) }
|
|
let nsDict = NSMutableDictionary(dictionary: dict)
|
|
nsDict["CFBundleIdentifier"] = info.newBundleID
|
|
guard nsDict.write(to: infoPlist, atomically: true) else {
|
|
throw Error.errorWriting(infoPlist.lastPathComponent)
|
|
}
|
|
|
|
let profURL = url.appendingPathComponent("embedded.mobileprovision")
|
|
if profURL.exists {
|
|
try FileManager.default.removeItem(at: profURL)
|
|
}
|
|
try info.mobileprovision.data().write(to: profURL)
|
|
}
|
|
|
|
let encoder = PropertyListEncoder()
|
|
encoder.outputFormat = .xml
|
|
let entitlements = try provisioningDict.mapValues { try encoder.encode($0.entitlements) }
|
|
|
|
status(NSLocalizedString("signer.signing", value: "Signing", comment: ""))
|
|
try await signer.sign(
|
|
app: app,
|
|
identity: .real(signingInfo.certificate, signingInfo.privateKey),
|
|
entitlementMapping: entitlements,
|
|
deep: true,
|
|
progress: progress
|
|
)
|
|
progress(1)
|
|
|
|
return mainInfo.newBundleID
|
|
}
|
|
|
|
}
|