commit c02cb61aeadd283e0e497e75d6d97be4f9438cea Author: Selim Mustafaev Date: Fri Apr 14 00:07:39 2023 +0300 Initial Commit diff --git a/reSign.xcodeproj/project.pbxproj b/reSign.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7ab7678 --- /dev/null +++ b/reSign.xcodeproj/project.pbxproj @@ -0,0 +1,373 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 7A064BE429DE107000C5D978 /* Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A064BE329DE107000C5D978 /* Category.swift */; }; + 7A064BE929DE18C700C5D978 /* SignInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A064BE829DE18C700C5D978 /* SignInfoView.swift */; }; + 7A064BEB29DF5BB800C5D978 /* AppPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A064BEA29DF5BB800C5D978 /* AppPackage.swift */; }; + 7A064BED29E2C91D00C5D978 /* Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A064BEC29E2C91D00C5D978 /* Certificate.swift */; }; + 7A72231329DCABE400503F78 /* reSignApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A72231229DCABE400503F78 /* reSignApp.swift */; }; + 7A72231529DCABE400503F78 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A72231429DCABE400503F78 /* ContentView.swift */; }; + 7A72231729DCABE500503F78 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A72231629DCABE500503F78 /* Assets.xcassets */; }; + 7A72231A29DCABE500503F78 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A72231929DCABE500503F78 /* Preview Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 7A064BE329DE107000C5D978 /* Category.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Category.swift; sourceTree = ""; }; + 7A064BE829DE18C700C5D978 /* SignInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInfoView.swift; sourceTree = ""; }; + 7A064BEA29DF5BB800C5D978 /* AppPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPackage.swift; sourceTree = ""; }; + 7A064BEC29E2C91D00C5D978 /* Certificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Certificate.swift; sourceTree = ""; }; + 7A72230F29DCABE400503F78 /* reSign.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = reSign.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A72231229DCABE400503F78 /* reSignApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = reSignApp.swift; sourceTree = ""; }; + 7A72231429DCABE400503F78 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7A72231629DCABE500503F78 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7A72231929DCABE500503F78 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 7A72231B29DCABE500503F78 /* reSign.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = reSign.entitlements; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7A72230C29DCABE400503F78 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7A064BE129DE0FA900C5D978 /* Models */ = { + isa = PBXGroup; + children = ( + 7A064BE329DE107000C5D978 /* Category.swift */, + ); + path = Models; + sourceTree = ""; + }; + 7A064BE529DE17A800C5D978 /* Views */ = { + isa = PBXGroup; + children = ( + 7A064BE829DE18C700C5D978 /* SignInfoView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 7A72230629DCABE400503F78 = { + isa = PBXGroup; + children = ( + 7A72231129DCABE400503F78 /* reSign */, + 7A72231029DCABE400503F78 /* Products */, + ); + sourceTree = ""; + }; + 7A72231029DCABE400503F78 /* Products */ = { + isa = PBXGroup; + children = ( + 7A72230F29DCABE400503F78 /* reSign.app */, + ); + name = Products; + sourceTree = ""; + }; + 7A72231129DCABE400503F78 /* reSign */ = { + isa = PBXGroup; + children = ( + 7A064BE529DE17A800C5D978 /* Views */, + 7A064BE129DE0FA900C5D978 /* Models */, + 7A72231229DCABE400503F78 /* reSignApp.swift */, + 7A72231429DCABE400503F78 /* ContentView.swift */, + 7A064BEA29DF5BB800C5D978 /* AppPackage.swift */, + 7A064BEC29E2C91D00C5D978 /* Certificate.swift */, + 7A72231629DCABE500503F78 /* Assets.xcassets */, + 7A72231B29DCABE500503F78 /* reSign.entitlements */, + 7A72231829DCABE500503F78 /* Preview Content */, + ); + path = reSign; + sourceTree = ""; + }; + 7A72231829DCABE500503F78 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 7A72231929DCABE500503F78 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7A72230E29DCABE400503F78 /* reSign */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7A72231E29DCABE500503F78 /* Build configuration list for PBXNativeTarget "reSign" */; + buildPhases = ( + 7A72230B29DCABE400503F78 /* Sources */, + 7A72230C29DCABE400503F78 /* Frameworks */, + 7A72230D29DCABE400503F78 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = reSign; + productName = reSign; + productReference = 7A72230F29DCABE400503F78 /* reSign.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7A72230729DCABE400503F78 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 7A72230E29DCABE400503F78 = { + CreatedOnToolsVersion = 14.3; + }; + }; + }; + buildConfigurationList = 7A72230A29DCABE400503F78 /* Build configuration list for PBXProject "reSign" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7A72230629DCABE400503F78; + productRefGroup = 7A72231029DCABE400503F78 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7A72230E29DCABE400503F78 /* reSign */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7A72230D29DCABE400503F78 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A72231A29DCABE500503F78 /* Preview Assets.xcassets in Resources */, + 7A72231729DCABE500503F78 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7A72230B29DCABE400503F78 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A72231529DCABE400503F78 /* ContentView.swift in Sources */, + 7A064BEB29DF5BB800C5D978 /* AppPackage.swift in Sources */, + 7A064BE929DE18C700C5D978 /* SignInfoView.swift in Sources */, + 7A064BED29E2C91D00C5D978 /* Certificate.swift in Sources */, + 7A72231329DCABE400503F78 /* reSignApp.swift in Sources */, + 7A064BE429DE107000C5D978 /* Category.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7A72231C29DCABE500503F78 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7A72231D29DCABE500503F78 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 7A72231F29DCABE500503F78 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = reSign/reSign.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"reSign/Preview Content\""; + DEVELOPMENT_TEAM = 46DTTB8X4S; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.reSign; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 7A72232029DCABE500503F78 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = reSign/reSign.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"reSign/Preview Content\""; + DEVELOPMENT_TEAM = 46DTTB8X4S; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.reSign; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7A72230A29DCABE400503F78 /* Build configuration list for PBXProject "reSign" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7A72231C29DCABE500503F78 /* Debug */, + 7A72231D29DCABE500503F78 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7A72231E29DCABE500503F78 /* Build configuration list for PBXNativeTarget "reSign" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7A72231F29DCABE500503F78 /* Debug */, + 7A72232029DCABE500503F78 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7A72230729DCABE400503F78 /* Project object */; +} diff --git a/reSign.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/reSign.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/reSign.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/reSign.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/reSign.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/reSign.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/reSign.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/reSign.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..9b873ab --- /dev/null +++ b/reSign.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/reSign.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist b/reSign.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..b076f0b --- /dev/null +++ b/reSign.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + reSign.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/reSign/AppPackage.swift b/reSign/AppPackage.swift new file mode 100644 index 0000000..56e7043 --- /dev/null +++ b/reSign/AppPackage.swift @@ -0,0 +1,177 @@ +// +// AppPackage.swift +// reSign +// +// Created by Selim Mustafaev on 06.04.2023. +// + +import Foundation +import Security + +enum SecError: LocalizedError { + + case custom(message: String) + case staticCodeNil + case designatedRequirementsError + case certReadError + + var errorDescription: String? { + switch self { + case .custom(let message): return message + case .staticCodeNil: return "Failed to initialize SecStaticCode" + case .designatedRequirementsError: return "Failed to copy designated requirements" + case .certReadError: return "Error reading certificate" + } + } +} + + +class AppPackage { + + public let url: URL + + // Generic info + + var name: String + var bundleId: String? + var mainExecutable: String? + var format: String? + var source: String? + var runtimeVersion: Int? + var flags: [String]? + var digestAlgorithm: String? + var teamId: String? + var signingDate: Date? + var signingDateActual: Date? + var designatedRequirements: String? + var implicitRequirements: String? + var certificates: [Certificate]? + + init(url: URL) { + + self.url = url + self.name = url.deletingPathExtension().lastPathComponent + + do { + try getAppInfo() + } catch { + print("Error: \(error.localizedDescription)") + } + } + + // https://developer.apple.com/documentation/security/1394686-seccopyerrormessagestring + func checkResult(_ status: OSStatus) throws { + if status != 0 { + if let message = SecCopyErrorMessageString(status, nil) { + throw SecError.custom(message: message as String) + } else { + throw NSError(domain: NSOSStatusErrorDomain, code: Int(status)) + } + } + } + + func getAppInfo() throws { + + let pStaticCode: UnsafeMutablePointer = .allocate(capacity: 1) + defer { pStaticCode.deallocate() } + var status = SecStaticCodeCreateWithPath(url as CFURL, [], pStaticCode) + try checkResult(status) + + guard let staticCode = pStaticCode.pointee else { + throw SecError.staticCodeNil + } + + let pSigningInfo: UnsafeMutablePointer = .allocate(capacity: 1) + defer { pSigningInfo.deallocate() } + let signInfoFlags: SecCSFlags = [ + SecCSFlags(rawValue: kSecCSRequirementInformation), + SecCSFlags(rawValue: kSecCSSigningInformation), + ] + status = SecCodeCopySigningInformation(staticCode, signInfoFlags, pSigningInfo) + try checkResult(status) + + if let signingInfo = pSigningInfo.pointee as? [String: Any] { + + bundleId = signingInfo[kSecCodeInfoIdentifier as String] as? String + mainExecutable = (signingInfo[kSecCodeInfoMainExecutable as String] as? URL)?.path() + format = signingInfo[kSecCodeInfoFormat as String] as? String + source = signingInfo[kSecCodeInfoSource as String] as? String + runtimeVersion = signingInfo[kSecCodeInfoRuntimeVersion as String] as? Int + flags = getFlags(from: signingInfo) + digestAlgorithm = getDigestAlgorithm(from: signingInfo) + teamId = signingInfo[kSecCodeInfoTeamIdentifier as String] as? String + signingDate = signingInfo[kSecCodeInfoTime as String] as? Date + signingDateActual = signingInfo[kSecCodeInfoTimestamp as String] as? Date + designatedRequirements = try getRequirements(from: signingInfo, key: kSecCodeInfoDesignatedRequirement) + implicitRequirements = try getRequirements(from: signingInfo, key: kSecCodeInfoImplicitDesignatedRequirement) + certificates = try getCertificates(from: signingInfo) + + let infoPlist = signingInfo[kSecCodeInfoPList as String] + print(infoPlist) + } + } + + func getDigestAlgorithm(from dict: [String: Any]) -> String? { + guard let code = dict[kSecCodeInfoDigestAlgorithm as String] as? UInt32, + let codeEnum = SecCSDigestAlgorithm(rawValue: code) + else { + return nil + } + + switch codeEnum { + case .codeSignatureHashSHA1: return "SHA1" + case .codeSignatureHashSHA256: return "SHA256" + case .codeSignatureHashSHA256Truncated: return "SHA256 truncated" + case .codeSignatureHashSHA384: return "SHA384" + case .codeSignatureHashSHA512: return "SHA512" + case .codeSignatureNoHash: return "No hash" + default: return nil + } + } + + func getRequirements(from dict: [String: Any], key: CFString) throws -> String? { + guard let reqAny = dict[key as String] else { + return nil + } + + let pReqStr: UnsafeMutablePointer = .allocate(capacity: 1) + defer { pReqStr.deallocate() } + let status = SecRequirementCopyString(reqAny as! SecRequirement, [], pReqStr) + try checkResult(status) + + if let reqStr = pReqStr.pointee as? String { + return reqStr + } + + return nil + } + + func getFlags(from dict: [String: Any]) -> [String]? { + guard let number = dict[kSecCodeInfoFlags as String] as? UInt32 else { + return nil + } + + let flags = SecCodeSignatureFlags(rawValue: number) + var result: [String] = [] + + if flags.contains(.adhoc) { result.append("adhoc") } + if flags.contains(.enforcement) { result.append("enforce code signing") } + if flags.contains(.forceExpiration) { result.append("force expiration") } + if flags.contains(.forceHard) { result.append("force hard") } + if flags.contains(.forceKill) { result.append("force kill") } + if flags.contains(.host) { result.append("host guest code") } + if flags.contains(.libraryValidation) { result.append("require library validation") } + if flags.contains(.restrict) { result.append("restrict dyld loading") } + if flags.contains(.runtime) { result.append("runtime hardening") } + + return result + } + + func getCertificates(from dict: [String: Any]) throws -> [Certificate]? { + guard let certs = dict[kSecCodeInfoCertificates as String] as? [SecCertificate] else { + return nil + } + + return try certs.map(Certificate.init) + } +} diff --git a/reSign/Assets.xcassets/AccentColor.colorset/Contents.json b/reSign/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/reSign/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/reSign/Assets.xcassets/AppIcon.appiconset/Contents.json b/reSign/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/reSign/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/reSign/Assets.xcassets/Contents.json b/reSign/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/reSign/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/reSign/Certificate.swift b/reSign/Certificate.swift new file mode 100644 index 0000000..58c7d3c --- /dev/null +++ b/reSign/Certificate.swift @@ -0,0 +1,81 @@ +// +// Certificate.swift +// reSign +// +// Created by Selim Mustafaev on 09.04.2023. +// + +import Foundation +import Security + +class Certificate { + + typealias CertDict = [CFString: [CFString: Any]] + + private let cert: SecCertificate + + public private(set) var commonName: String + public private(set) var notValidBefore: Date + public private(set) var notValidAfter: Date + + init(_ cert: SecCertificate) throws { + self.cert = cert + + let dict = try Certificate.parse(from: cert) + + self.commonName = try Certificate.readCN(from: cert) + self.notValidBefore = try Certificate.readDate(key: kSecOIDX509V1ValidityNotBefore, from: dict) + self.notValidAfter = try Certificate.readDate(key: kSecOIDX509V1ValidityNotAfter, from: dict) + } + + static func parse(from cert: SecCertificate) throws -> CertDict { + var pError: Unmanaged? = nil + guard let certDict = SecCertificateCopyValues(cert, nil, &pError) as? CertDict else { + throw SecError.certReadError + } + + if let error = pError?.takeUnretainedValue() { + throw error as Error + } + + return certDict + } + + static func read(key: CFString, from dict: CertDict) throws -> T { + guard let node = dict[key], + let value = node[kSecPropertyKeyValue] as? T + else { + throw SecError.certReadError + } + + return value + } + + static func readDate(key: CFString, from dict: CertDict) throws -> Date { + guard let node = dict[key], + let value = node[kSecPropertyKeyValue] as? TimeInterval + else { + throw SecError.certReadError + } + + return Date(timeIntervalSinceReferenceDate: value) + } + + static func readCN(from cert: SecCertificate) throws -> String { + var commonName: CFString? + SecCertificateCopyCommonName(cert, &commonName) + + if let commonName { + return commonName as String + } else { + throw SecError.certReadError + } + } +} + +extension Certificate: Identifiable { + + var id: String { + commonName + } +} diff --git a/reSign/ContentView.swift b/reSign/ContentView.swift new file mode 100644 index 0000000..d762ec2 --- /dev/null +++ b/reSign/ContentView.swift @@ -0,0 +1,85 @@ +// +// ContentView.swift +// reSign +// +// Created by Selim Mustafaev on 04.04.2023. +// + +import SwiftUI + +struct ContentView: View { + + let categories: [Category] = [ + Category(name: "Signing info", icon: "signature", type: .signInfo), + Category(name: "Info.plist", icon: "info", type: .infoPlist), + Category(name: "Entitlements", icon: "list.bullet", type: .entitlements) + ] + + let actionCategories: [Category] = [ + Category(name: "ReSign", icon: "arrow.uturn.forward", type: .resign) + ] + + @State var selection: Category? = nil + @State var appPackage: AppPackage? = nil + + var body: some View { + NavigationSplitView { + List(selection: $selection) { + Section("Info") { + ForEach(categories, id: \.self) { category in + Label(category.name, systemImage: category.icon) + } + } + + Section("Actions") { + ForEach(actionCategories, id: \.self) { category in + Label(category.name, systemImage: category.icon) + } + } + } + .toolbar { + ToolbarItemGroup(placement: .primaryAction) { + Spacer() + Button { + openClicked() + } label: { + Image(systemName: "doc") + } + + } + } + } detail: { + if appPackage == nil { + EmptyView() + } else { + switch selection?.type { + case .signInfo: SignInfoView(appPackage: appPackage!) + case .infoPlist: Text("Info.plist") + case .entitlements: Text("Entitlements") + case .resign: Text("ReSign") + default: EmptyView() + } + } + } + .navigationTitle(appPackage?.name ?? "") + .navigationSubtitle(appPackage?.bundleId ?? "") + } + + func openClicked() { + let panel = NSOpenPanel() + panel.canChooseDirectories = false + panel.allowsMultipleSelection = false + panel.allowedContentTypes = [.application] + + if panel.runModal() == .OK, let url = panel.url { + appPackage = AppPackage(url: url) + selection = categories.first + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/reSign/Models/Category.swift b/reSign/Models/Category.swift new file mode 100644 index 0000000..0733d6d --- /dev/null +++ b/reSign/Models/Category.swift @@ -0,0 +1,22 @@ +// +// Category.swift +// reSign +// +// Created by Selim Mustafaev on 05.04.2023. +// + +import Foundation + +struct Category: Hashable { + + enum CategoryType { + case signInfo + case infoPlist + case entitlements + case resign + } + + let name: String + let icon: String + let type: CategoryType +} diff --git a/reSign/Preview Content/Preview Assets.xcassets/Contents.json b/reSign/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/reSign/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/reSign/Views/SignInfoView.swift b/reSign/Views/SignInfoView.swift new file mode 100644 index 0000000..299e5af --- /dev/null +++ b/reSign/Views/SignInfoView.swift @@ -0,0 +1,88 @@ +// +// SignInfoView.swift +// reSign +// +// Created by Selim Mustafaev on 05.04.2023. +// + +import SwiftUI + +struct FormTextItem: View { + + let name: String + let value: String? + + var body: some View { + HStack { + Text(name) + Spacer() + Text(value ?? "") + .foregroundColor(.secondary) + .textSelection(.enabled) + } + } + + init(name: String, value: String?) { + self.name = name + self.value = value + } + + init(name: String, value: Int?) { + self.name = name + self.value = value != nil ? String(value!) : nil + } + + init(name: String, date: Date?) { + self.name = name + + if let date { + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .medium + self.value = formatter.string(from: date) + } else { + self.value = nil + } + } + + init(name: String, array: [String]?) { + self.name = name + self.value = array?.joined(separator: ", ") + } +} + +struct SignInfoView: View { + + let appPackage: AppPackage + + var body: some View { + Form { + Section(header: Text("General info")) { + FormTextItem(name: "Main executable path", value: appPackage.mainExecutable) + FormTextItem(name: "Format", value: appPackage.format) + FormTextItem(name: "Code signature source", value: appPackage.source) + FormTextItem(name: "Runtime version", value: appPackage.runtimeVersion) + FormTextItem(name: "Flags", array: appPackage.flags) + FormTextItem(name: "Digest algorithm", value: appPackage.digestAlgorithm) + FormTextItem(name: "Bundle ID", value: appPackage.bundleId) + FormTextItem(name: "Team ID", value: appPackage.teamId) + FormTextItem(name: "Signing date", date: appPackage.signingDate) + FormTextItem(name: "Signing date (actual)", date: appPackage.signingDateActual) + } + + Section(header: Text("Requirements")) { + FormTextItem(name: "Designated requirements", value: appPackage.designatedRequirements) + FormTextItem(name: "Implicit requirements", value: appPackage.implicitRequirements) + } + + if let certificates = appPackage.certificates { + Section("Certificates") { + ForEach(certificates) { cert in + FormTextItem(name: cert.commonName, date: cert.notValidAfter) + } + } + } + } + .formStyle(.grouped) + } +} diff --git a/reSign/reSign.entitlements b/reSign/reSign.entitlements new file mode 100644 index 0000000..f2ef3ae --- /dev/null +++ b/reSign/reSign.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/reSign/reSignApp.swift b/reSign/reSignApp.swift new file mode 100644 index 0000000..60366f2 --- /dev/null +++ b/reSign/reSignApp.swift @@ -0,0 +1,17 @@ +// +// reSignApp.swift +// reSign +// +// Created by Selim Mustafaev on 04.04.2023. +// + +import SwiftUI + +@main +struct reSignApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +}