Replace deprecated `SecTrustGetCertificateAtIndex` in iOS 15? - ios

I'm getting a deprecation warning in iOS 15 SDK, but the suggested replacement is not a one-to-one replacement. This is what I have for evaluating the SSL trust chain:
func valid(_ trust: SecTrust, forHost host: String) -> Bool {
guard valid(trust, for: [SecPolicyCreateSSL(true, nil)]),
valid(trust, for: [SecPolicyCreateSSL(true, host as CFString)]) else {
return false
}
let serverCertificatesData = Set(
(0..<SecTrustGetCertificateCount(trust))
.compactMap { SecTrustGetCertificateAtIndex(trust, $0) }
.map { SecCertificateCopyData($0) as Data }
)
let pinnedCertificatesData = Set(
certificates.map { SecCertificateCopyData($0) as Data }
)
return !serverCertificatesData.isDisjoint(with: pinnedCertificatesData)
}
The warning I get in Xcode 13 beta is:
'SecTrustGetCertificateAtIndex' was deprecated in iOS 15.0: renamed to 'SecTrustCopyCertificateChain(_:)'.
Use 'SecTrustCopyCertificateChain(_:)' instead.
However, SecTrustGetCertificateAtIndex (docs) returns SecCertificate where SecTrustCopyCertificateChain (docs) returns a CFArray. How can this properly be updated in the usage I provided?

iOS 14.5 => iOS 15 SDK Diff indicates that the only additions are these (as of Xcode 13 Beta 1)
SecBase.h
Added errSecInvalidCRLAuthority
Added errSecInvalidTupleCredentials
Added errSecCertificateDuplicateExtension
SecTrust.h
Added SecTrustCopyCertificateChain()
They didn't add any new sibling type to SecCertificate. As you already noted that it returns a CFArray.
func SecTrustCopyCertificateChain(_ trust: SecTrust) -> CFArray?
So for this part of your code -
let serverCertificatesData = Set(
(0..<SecTrustGetCertificateCount(trust))
.compactMap { SecTrustGetCertificateAtIndex(trust, $0) }
.map { SecCertificateCopyData($0) as Data }
)
It seems worth a try that SecTrustCopyCertificateChain might return a CFArray of SecCertificate instances? Unfortunately I can't debug this right now.
Maybe try something like this -
if let certificates = SecTrustCopyCertificateChain(trust) as? [SecCertificate] {
let serverCertificatesData = Set(
certificates.map { SecCertificateCopyData($0) as Data }
)
}

Related

CoreNFC - Empty NDEF after upgrade to iOS16

I've just updated to iOS 16, and all of a sudden the same code I used in iOS 15 is now reading ndefMessage as nil in the didDetect callback. I can't find anything online regarding what in iOS 16 would cause this, has anyone seen anything similar?
When I scan the same tag on Android, or use the NFC Tools app on iOS, I can read the tag NDEF fine. It seems that just my code seems to have been affected by the update...
UPDATE 1: I have put the same code onto my iOS 15.6 device, and it works perfectly. It seems to me that this is an iOS 16 bug.
Here's what I have:
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
print("did detect")
let str: String = nfcWriteContent
if (tags.count > 1) {
let retryInterval = DispatchTimeInterval.milliseconds(500)
session.alertMessage = "too_many_nfc_detected".localized()
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
session.restartPolling()
})
return
}
let tag = tags.first!
print("reading...")
tag.readNDEF(completionHandler: {(ndefMessage: NFCNDEFMessage?, error: Error?) in
var res = ""
if (ndefMessage == nil) {
// EVERY NFC SCAN ALWAYS FALLS IN HERE NOW
// WHEN SCANNING THE SAME TAG ON ANDROID, NDEF CONTENT IS PROPERLY RETURNED
print("empty tag")
} else {
print (ndefMessage!.records.count)
for payload in ndefMessage!.records {
if (payload.payload.count == 0) {
continue
}
res += (String.init(data: payload.payload.advanced(by: 1), encoding: .utf8) ?? "Format not supported")
}
}
session.alertMessage = "tag_successfully_read".localized()
session.invalidate()
print("read \(res)")
})
}
I got feedback from an Apple engineer. The sample project runs fine on iOS 16. You are missing out that you are not connected to the tag:
try await ndefReaderSession?.connect(to: tag)
let message = try await tag.readNDEF()
or
ndefReaderSession?.connect(to: tag) { error in
//call tag.readNDEF here
}

How to capture print statements from iOS app installed on iOS device?

I'm reading about some good practices for developing iOS apps and looking at the possibility of monitoring logs of an iOS app installed from App Store using Console.app. So, I was testing here, but I noticed that print statements didn't show up in Console.app, only NSLog does. My question is: is there any way that is possible to see logs that are made with print commands within iOS apps installed on a device? With Frida, Console.app or any other means?
If there is no other method, does it mean that print commands are more secure than NSLog? This seems very counterintuitive to me 🤔
print statement in iOS apps are not logged to one the [persistent] logging systems on iOS, therefore you can not access the output of an app via print statements if they had occur in the past.
By default you can only seem the output of print commands in XCode output panel. However the print commands themselves are always included in the debug and release builds and are therefore executed. Just the output of the print statements is discarded if no XCode is connected to retrieve it.
I tested this by building the following SwiftUI test app (see the end of this answer), made sure the Archive profile is set to RELEASE and the archived the project, to build an IPA file.
The IPA file was then analyzed in IdaPro to see the actual ARM assembler code.
And in all tests using different options (e.g. "Rebuild from Bitcode" (de)activated) the code was always there.
Therefore if you attach Frida to an app you can e.g. hook the print method print(_:separator:terminator:) to retrieve all output that would otherwise be discarded.
struct ContentView: View {
#State var number : Int = 1
var body: some View {
VStack {
Button(" Print ") {
print("print test abcdefgh number %d", number)
}.padding()
Button(" os_log ") {
os_log("os_log test abcdefgh number %d", number)
}.padding()
Button("randomize") {
self.number = Int.random(in: 1...11111)
}.padding()
}
}
}
If, and only if, you want to use print and printf in your app to go to a file or whatever file descriptor:
import SwiftUI
import Darwin
import os.log
extension OSLog {
private static var subsystem = Bundle.main.bundleIdentifier!
static let `default` = OSLog(subsystem: subsystem, category: "default")
}
extension TestApp {
func subscribeFileToStderrAndStdoutIfNotAttachedToDebugger() {
if isatty(STDERR_FILENO) != 1 {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let logfileUrl = documentsUrl.appendingPathComponent("out.log")
logfileUrl.withUnsafeFileSystemRepresentation { path in
guard let path = path else {
return
}
print("redirect stdout and stderr to: \(String(cString: path))")
let file = fopen(path, "a")
assert(file != nil, String(cString: strerror(errno)))
let fd = fileno(file)
assert(fd >= 0, String(cString: strerror(errno)))
let result1 = dup2(fd, STDERR_FILENO)
assert(result1 >= 0, String(cString: strerror(errno)))
let result2 = dup2(fd, STDOUT_FILENO)
assert(result2 >= 0, String(cString: strerror(errno)))
}
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
subscribeFileToStderrAndStdoutIfNotAttachedToDebugger()
return true
}
}

iOS HttpUrlResponse Header Field Case Sensitivity

When attempting to get the headers from an HttpUrlResponse, I am finding the iOS simulator is case-insensitive and a real device is case-sensitive.
The web service returns an HTTP header "Grandmas-Cookies: XXXX"
When the header key has uppercase letters:
urlResponse.response.allHeaderFields["Grandmas-Cookies"] as? String
The simulator does NOT find the key.
A real device sees the key.
When the header key has all lowercase letters:
urlResponse.response.allHeaderFields["grandmas-cookies"] as? String
The simulator does find the key.
A real device does NOT see the key.
Is there a setting I can make to the simulator to behave similarly to the real device? Changing the HTTP headers in the web service to lowercase is not desirable at this point but it is strange this started occurring only recently (yeah it's one of those fun times).
Edit:
#Adam I found a better way to ensure that this isn't an issue.
I created this function that makes the check case insensitive.
func find(header: String) -> String? {
let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }
if let headerValue = keyValues.filter({ $0.0 == header.lowercased() }).first {
return headerValue.1
}
return nil
}
The below may still be useful for some people.
To solve this issue I created a struct. Inside the struct I created a static variable grandmasCookies that can now be referenced from anywhere within your app. This returns the upper case
Grandmas-Cookies
when you are running on a phone device.
This returns lowercase
grandmas-cookies
when you are running in a simulator on a device such as a MacBook Pro.
struct Platform {
static let grandmasCookies: String = {
var xtoken = "Grandmas-Cookies"
#if arch(i386) || arch(x86_64)
xtoken = "grandmas-cookies"
#endif
return xtoken
}()
static let isSimulator: Bool = {
var isSim = false
#if arch(i386) || arch(x86_64)
isSim = true
#endif
return isSim
}()
}
I created a second convenience variable isSimulator which returns true when running from a simulator and false when running on a phone device.
I adapted code from this StackOverflow post to make a solution that works for your scenario and one that I faced as well.

I'm trying to archive a meteor-built ios app, but Xcode but I keep getting 'Ambiguous use of […]'

I built this project from a meteor app (with meteor build ios...).
I've set it to use the legacy version of Swift (otherwise I get a lot of errors). But when I try to archive it, i get this error in different locations.
func cancel() { // Error: Ambiguous use of 'dispatch_sync(_:block)'
dispatch_sync(queue) {
self._cancel()
}
}
func dispatch_sync(queue: dispatch_queue_t, block: () throws -> ())throws { // 1. Found this candidate
var caughtError: ErrorType?
dispatch_sync(queue) {
do {
try block()
} catch {
caughtError = error
}
}
if let caughtError = caughtError {
throw caughtError
}
}
func cancel() { // 2. Found this candidate
dispatch_sync(queue) {
self._cancel()
}
}
I'm not sure how to solve it, could you help me?
PS: I'm using the latest version of Xcode with MacOS Sierra. And Meteor 1.4.1.2
Rename your function dispach_sync into, for instance, dispatch_sync_mine

Segmentation fault when using toRaw()

Working on swift programming language. Using XCode 6.1 GM with iOS 8.1 Beta.
In 2 places inside code getting error message:
Method 'fromRaw' has been replaced with a property 'rawValue'. When I replace .toRaw() with .rawValue getting unknown compiler error.
if self._attached != nil && self._attached!.toRaw() == i {
continue
}
...
self._segmentPoints[direction.clockwise().toRaw()].0, self._segmentPoints[direction.counterclockwise().toRaw()].1)
What am I doing wrong? any suggestions on how to use .rawValue?
toRaw and fromRaw are cancel by new version of xcode
replace by "rawValue"
eg:
enum Rank: Int{
case Ace = 1
case Two,Three,Four
case Jack,Queen,King
func simpleDesc() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Jack
let aceRawValue = ace.rawValue
println(aceRawValue)
let b = Rank(rawValue: 5)
println(b!.simpleDesc())

Resources