Skip to content

Commit

Permalink
security: add resources check
Browse files Browse the repository at this point in the history
Signed-off-by: 82Flex <[email protected]>
  • Loading branch information
Lessica committed Jan 14, 2024
1 parent e3e0b5a commit 8ba6919
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 8 deletions.
Binary file added Certificates/Certificates.p12
Binary file not shown.
Binary file added Certificates/Lessica.cer
Binary file not shown.
26 changes: 23 additions & 3 deletions Reveil/DataModels/Presets/SecurityPresets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,21 @@ struct SecurityPresets: Codable {
}

if let bundleProvisioningProfilePath = bundle.path(forResource: "embedded", ofType: "mobileprovision"),
let profileHashValue = IntegrityChecker.getMobileProvisionProfileHashValue(path: bundleProvisioningProfilePath)
let profileHashValue = IntegrityChecker.calculateHashValue(path: bundleProvisioningProfilePath)
{
secureMobileProvisioningProfileHashes.removeAll(keepingCapacity: true)
secureMobileProvisioningProfileHashes.insert(profileHashValue)
}

var updatedHashes: Dictionary<String, String> = secureResourceHashes
for secureResourceName in secureResourceHashes.keys {
let resourcePath = bundle.path(forResource: secureResourceName, ofType: nil)
if let resourcePath, let resourceHashValue = IntegrityChecker.calculateHashValue(path: resourcePath)
{
updatedHashes.updateValue(resourceHashValue, forKey: secureResourceName)
}
}
secureResourceHashes = updatedHashes
}

var secureStandaloneLibraries: Set<String> = [
Expand Down Expand Up @@ -84,11 +94,21 @@ struct SecurityPresets: Codable {
]

var secureMobileProvisioningProfileHashes: Set<String> = [
"0675eb798917a1d44f11424be328c72a58812ba03900f1c58569af867353f438",
"",
]

var secureMainExecutableMachOHashes: Set<String> = [
"502a2f7f57fcd163c4a8ccaa09b1d8831e53d214c1a8e4ef49904c7615324fdc",
"",
]

var secureResourceHashes: Dictionary<String, String> = [
"library_stub.zip": "",
"rsc-001-country-mapping.json": "",
"rsc-002-ios-versions.json": "",
"rsc-003-iphone-models.json": "",
"rsc-004-carriers.json": "",
"rsc-005-ipad-models.json": "",
"rsc-006-ipod-models.json": "",
]

var insecureEnvironmentVariables: Set<String> = [
Expand Down
Binary file modified Reveil/Resources/Settings.bundle/en.lproj/Root.strings
Binary file not shown.
Binary file modified Reveil/Resources/Settings.bundle/zh_Hans.lproj/Root.strings
Binary file not shown.
9 changes: 6 additions & 3 deletions Reveil/SecuritySuite/FileIntegrityCheck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ enum FileIntegrityCheck: Codable {
// Compare current hash value(SHA256 hex string) of `embedded.mobileprovision` with a specified hash value.
// Use command `"shasum -a 256 /path/to/embedded.mobileprovision"` to get SHA256 value on your macOS.
case mobileProvision(String)
case commonResource(String, String)

// Compare current hash value(SHA256 hex string) of executable file with a specified (Image Name, Hash Value).
// Only work on dynamic library and arm64.
Expand All @@ -24,11 +25,13 @@ extension FileIntegrityCheck: Explainable {
var description: String {
switch self {
case let .bundleID(exceptedBundleID):
"The expected bundle identify was \(exceptedBundleID)"
"The expected bundle identify was \(exceptedBundleID)."
case let .mobileProvision(expectedSha256Value):
"The expected hash value of Mobile Provision file was \(expectedSha256Value)"
"The expected hash value of Mobile Provision file was \(expectedSha256Value)."
case let .commonResource(resourceName, expectedSha256Value):
"The expected hash value of the resource named \(resourceName) was \(expectedSha256Value)."
case let .machO(imageName, expectedSha256Value):
"The expected hash value of \"__TEXT.__text\" data of \(imageName) Mach-O file was \(expectedSha256Value)"
"The expected hash value of \"__TEXT.__text\" data of \(imageName) Mach-O file was \(expectedSha256Value)."
}
}
}
10 changes: 8 additions & 2 deletions Reveil/SecuritySuite/IntegrityChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ final class IntegrityChecker {
result = true
hitChecks.append(check)
}
case let .commonResource(resourceName, expectedSha256Value):
let resourcePath = Bundle(for: Self.self).path(forResource: resourceName, ofType: nil)
if let resourcePath, calculateHashValue(path: resourcePath) != expectedSha256Value {
result = true
hitChecks.append(check)
}
case let .machO(imageName, expectedSha256Value):
if !checkMachO(imageName, with: expectedSha256Value.lowercased()) {
result = true
Expand Down Expand Up @@ -58,13 +64,13 @@ final class IntegrityChecker {

static func checkMobileProvision(_ expectedSha256Values: Set<String>) -> Bool {
guard let path = Bundle(for: Self.self).path(forResource: "embedded", ofType: "mobileprovision"),
let hashValue = getMobileProvisionProfileHashValue(path: path)
let hashValue = calculateHashValue(path: path)
else { return false }

return expectedSha256Values.contains(hashValue)
}

static func getMobileProvisionProfileHashValue(path: String) -> String? {
static func calculateHashValue(path: String) -> String? {
guard FileManager.default.fileExists(atPath: path) else {
return nil
}
Expand Down
27 changes: 27 additions & 0 deletions Reveil/ViewModels/Security.swift
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,33 @@ final class Security: ObservableObject, StaticEntryProvider, Explainable {
IntegrityChecker.checkMobileProvision(SecurityPresets.default.secureMobileProvisioningProfileHashes)
}

func checkResourceHashes() -> Bool {
let resHashes = SecurityPresets.default.secureResourceHashes
for resName in resHashes.keys {
if let resHash = resHashes[resName] {
let resTampered = IntegrityChecker.amITampered([.commonResource(resName, resHash)]).result
if resTampered {
return false
}
}
}
return true
}

func getModifiedResourceNames() -> [String] {
var modifiedNames = [String]()
let resHashes = SecurityPresets.default.secureResourceHashes
for resName in resHashes.keys {
if let resHash = resHashes[resName] {
let resTampered = IntegrityChecker.amITampered([.commonResource(resName, resHash)]).result
if resTampered {
modifiedNames.append(resName)
}
}
}
return modifiedNames
}

func checkMachOHash() -> Bool {
#if DEBUG
return IntegrityChecker.checkMachO(currentExecutablePath, with: SecurityPresets.default.secureMainExecutableMachOHashes)
Expand Down
23 changes: 23 additions & 0 deletions Reveil/ViewModels/SecurityCheck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable

case identifiedBundleIdentifier(Status)
case identifiedMobileProvisioningProfile(Status)
case identifiedResources(Status)
case identifiedMachO(Status)
case identifiedEntitlements(Status)

Expand Down Expand Up @@ -100,6 +101,8 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
self = .identifiedBundleIdentifier(status)
case "identifiedMobileProvisioningProfile":
self = .identifiedMobileProvisioningProfile(status)
case "identifiedResources":
self = .identifiedResources(status)
case "identifiedMachO":
self = .identifiedMachO(status)
case "identifiedEntitlements":
Expand Down Expand Up @@ -173,6 +176,8 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
status.prefix + "identifiedBundleIdentifier"
case let .identifiedMobileProvisioningProfile(status):
status.prefix + "identifiedMobileProvisioningProfile"
case let .identifiedResources(status):
status.prefix + "identifiedResources"
case let .identifiedMachO(status):
status.prefix + "identifiedMachO"
case let .identifiedEntitlements(status):
Expand Down Expand Up @@ -232,6 +237,7 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
.noSuspiciousEnvironmentVariables(.unchanged),
.identifiedBundleIdentifier(.unchanged),
.identifiedMobileProvisioningProfile(.unchanged),
.identifiedResources(.unchanged),
.identifiedMachO(.unchanged),
.identifiedEntitlements(.unchanged),
.expectedCodeSigningStatus(.unchanged),
Expand Down Expand Up @@ -269,6 +275,7 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
case let .noSuspiciousEnvironmentVariables(status): fallthrough
case let .identifiedBundleIdentifier(status): fallthrough
case let .identifiedMobileProvisioningProfile(status): fallthrough
case let .identifiedResources(status): fallthrough
case let .identifiedMachO(status): fallthrough
case let .identifiedEntitlements(status): fallthrough
case let .expectedCodeSigningStatus(status): fallthrough
Expand Down Expand Up @@ -308,6 +315,7 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
case let .noSuspiciousEnvironmentVariables(status): fallthrough
case let .identifiedBundleIdentifier(status): fallthrough
case let .identifiedMobileProvisioningProfile(status): fallthrough
case let .identifiedResources(status): fallthrough
case let .identifiedMachO(status): fallthrough
case let .identifiedEntitlements(status): fallthrough
case let .expectedCodeSigningStatus(status): fallthrough
Expand Down Expand Up @@ -347,6 +355,7 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
case let .noSuspiciousEnvironmentVariables(status): fallthrough
case let .identifiedBundleIdentifier(status): fallthrough
case let .identifiedMobileProvisioningProfile(status): fallthrough
case let .identifiedResources(status): fallthrough
case let .identifiedMachO(status): fallthrough
case let .identifiedEntitlements(status): fallthrough
case let .expectedCodeSigningStatus(status): fallthrough
Expand Down Expand Up @@ -419,6 +428,10 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
status == .failed ?
NSLocalizedString("TAMPERED_MOBILE_PROVISIONING_PROFILE", comment: "Mobile provisioning profile was tampered") :
NSLocalizedString("ORIGINAL_MOBILE_PROVISIONING_PROFILE", comment: "Mobile provisioning profile is trusted")
case let .identifiedResources(status):
status == .failed ?
NSLocalizedString("TAMPERED_RESOURCES", comment: "Some of the embedded resources were tampered") :
NSLocalizedString("ORIGINAL_RESOURCES", comment: "Embedded resources are original")
case let .identifiedMachO(status):
status == .failed ?
NSLocalizedString("TAMPERED_MACH_O", comment: "Main executable was tampered") :
Expand Down Expand Up @@ -548,6 +561,8 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
.staticIntegrity
case .identifiedMobileProvisioningProfile:
.staticIntegrity
case .identifiedResources:
.staticIntegrity
case .identifiedMachO:
.staticIntegrity
case .identifiedEntitlements:
Expand Down Expand Up @@ -616,6 +631,8 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
.identifiedBundleIdentifier(Security.shared.checkMainBundleIdentifier() ? .passed : .failed)
case .identifiedMobileProvisioningProfile:
.identifiedMobileProvisioningProfile(Security.shared.checkMobileProvisioningProfileHash() ? .passed : .failed)
case .identifiedResources:
.identifiedResources(Security.shared.checkResourceHashes() ? .passed : .failed)
case .identifiedMachO:
.identifiedMachO(Security.shared.checkMachOHash() ? .passed : .failed)
case .identifiedEntitlements:
Expand Down Expand Up @@ -726,6 +743,12 @@ enum SecurityCheck: CaseIterable, Codable, Equatable, Hashable, RawRepresentable
BasicEntry(customLabel: $0, allowedToCopy: true)
}
}
case let .identifiedResources(status):
if status == .failed {
entryChildren = Security.shared.getModifiedResourceNames().map {
BasicEntry(customLabel: $0, allowedToCopy: true)
}
}
case let .identifiedEntitlements(status):
if status == .failed {
entryChildren = Security.shared.getUnknownEntitlementKeys().map {
Expand Down
6 changes: 6 additions & 0 deletions Reveil/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,9 @@
/* Mobile provisioning profile is trusted */
"ORIGINAL_MOBILE_PROVISIONING_PROFILE" = "Mobile provisioning profile is trusted";

/* Embedded resources are original */
"ORIGINAL_RESOURCES" = "Embedded resources are original";

/* Others */
"OTHERS" = "Others";

Expand Down Expand Up @@ -916,6 +919,9 @@
/* Mobile provisioning profile was tampered */
"TAMPERED_MOBILE_PROVISIONING_PROFILE" = "Mobile provisioning profile was tampered";

/* Some of the embedded resources were tampered */
"TAMPERED_RESOURCES" = "Some of the embedded resources were tampered";

/* TB Frequency */
"TB_FREQ" = "Time-Based Frequency";

Expand Down
6 changes: 6 additions & 0 deletions Reveil/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,9 @@
/* Mobile provisioning profile is trusted */
"ORIGINAL_MOBILE_PROVISIONING_PROFILE" = "已验证应用程序分发配置文件";

/* Embedded resources are original */
"ORIGINAL_RESOURCES" = "已验证内建资源均为初始状态";

/* Others */
"OTHERS" = "其他";

Expand Down Expand Up @@ -916,6 +919,9 @@
/* Mobile provisioning profile was tampered */
"TAMPERED_MOBILE_PROVISIONING_PROFILE" = "应用程序分发配置文件被篡改";

/* Some of the embedded resources were tampered */
"TAMPERED_RESOURCES" = "部分内建资源被篡改";

/* TB Frequency */
"TB_FREQ" = "时基频率";

Expand Down

0 comments on commit 8ba6919

Please sign in to comment.