Files
SideStore/AltStore/Operations/VerifyAppOperation.swift
2023-05-11 18:57:54 -05:00

103 lines
3.3 KiB
Swift

//
// VerifyAppOperation.swift
// AltStore
//
// Created by Riley Testut on 5/2/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import CryptoKit
import AltStoreCore
import AltSign
import Roxas
@objc(VerifyAppOperation)
class VerifyAppOperation: ResultOperation<Void>
{
let context: InstallAppOperationContext
var verificationHandler: ((VerificationError) -> Bool)?
init(context: InstallAppOperationContext)
{
self.context = context
super.init()
}
override func main()
{
super.main()
do
{
if let error = self.context.error
{
throw error
}
let appName = self.context.app?.name ?? NSLocalizedString("The app", comment: "")
self.localizedFailure = String(format: NSLocalizedString("%@ could not be installed.", comment: ""), appName)
guard let app = self.context.app else { throw OperationError.invalidParameters }
guard app.bundleIdentifier == self.context.bundleIdentifier else {
throw VerificationError.mismatchedBundleIdentifiers(sourceBundleID: self.context.bundleIdentifier, app: app)
}
guard ProcessInfo.processInfo.isOperatingSystemAtLeast(app.minimumiOSVersion) else {
throw VerificationError.iOSVersionNotSupported(app: app, requiredOSVersion: app.minimumiOSVersion)
}
guard let appVersion = self.context.appVersion else {
return self.finish(.success(()))
}
Task<Void, Never> {
do
{
guard let ipaURL = self.context.ipaURL else { throw OperationError.appNotFound(name: app.name) }
try await self.verifyHash(of: app, at: ipaURL, matches: appVersion)
try await self.verifyDownloadedVersion(of: app, matches: appVersion)
self.finish(.success(()))
}
catch
{
self.finish(.failure(error))
}
}
}
catch
{
self.finish(.failure(error))
}
}
}
private extension VerifyAppOperation
{
func verifyHash(of app: ALTApplication, at ipaURL: URL, @AsyncManaged matches appVersion: AppVersion) async throws
{
// Do nothing if source doesn't provide hash.
guard let expectedHash = await $appVersion.sha256 else { return }
let data = try Data(contentsOf: ipaURL)
let sha256Hash = SHA256.hash(data: data)
let hashString = sha256Hash.compactMap { String(format: "%02x", $0) }.joined()
print("[ALTLog] Comparing app hash (\(hashString)) against expected hash (\(expectedHash))...")
guard hashString == expectedHash else { throw VerificationError.mismatchedHash(hashString, expectedHash: expectedHash, app: app) }
}
func verifyDownloadedVersion(of app: ALTApplication, @AsyncManaged matches appVersion: AppVersion) async throws
{
let version = await $appVersion.version
guard version == app.version else { throw VerificationError.mismatchedVersion(app.version, expectedVersion: version, app: app) }
}
}