SwiftJWT crashes iOS App on JWT.init() call

245 Views Asked by At

The following code crashes on iPad mini 2 / 3 only when executed from the AppStore distributed executable.

let jwtVerifier = JWTVerifier.es512(publicKey: publicKey.data(using: .utf8)!)
do {
  let parsedJwt = try JWT<LicenseJWTClaims>(jwtString: jwt, verifier: jwtVerifier) // <== crashes here
  return License(jwt: parsedJwt, raw_jwt_string: jwt)
} catch {
}

// LicenseJWTClaims for reference
internal struct LicenseJWTClaims: Claims {
    let exp: Date?
    let nbf: Date?
    let iat: Date?
    let licenseKey: String?
}

The code works just fine on every device when installed via XCode. The App also works on 'normal' (not mini) iPads when installed from the App Store.

How would you go on analysing this issue? Do you maybe even have a fix available?

Find the relevant part of the crash log below:

Thread 0 name:  Dispatch queue: com.apple.avfoundation.metadataoutput.objectqueue
Thread 0 Crashed:
0   libswiftCore.dylib              0x00000001b1b33b00 specialized _fatalErrorMessage+ 2112256 (_:_:file:line:flags:) + 296
1   libswiftCore.dylib              0x00000001b1b33b00 specialized _fatalErrorMessage+ 2112256 (_:_:file:line:flags:) + 296
2   libswiftCore.dylib              0x00000001b1b65300 specialized _NativeDictionary.bridged+ 2315008 () + 288
3   libswiftCore.dylib              0x00000001b19b1f70 _NativeDictionary.bridged+ 532336 () + 20
4   CryptorECC                      0x0000000100564958 specialized ECPublicKey.init(der:) + 51544 (ECPublicKey.swift:144)
5   CryptorECC                      0x0000000100564240 ECPublicKey.__allocating_init(key:) + 49728 (ECPublicKey.swift:93)
6   SwiftJWT                        0x00000001008a36d0 BlueECVerifier.verify(signature:for:) + 30416 (BlueECDSA.swift:99)
7   SwiftJWT                        0x00000001008a34a8 BlueECVerifier.verify(jwt:) + 29864 (BlueECDSA.swift:84)
8   SwiftJWT                        0x00000001008a3884 protocol witness for VerifierAlgorithm.verify(jwt:) in conformance BlueECVerifier + 30852 (<compiler-generated>:0)
9   SwiftJWT                        0x00000001008b3200 JWT.init(jwtString:verifier:) + 94720 (JWT.swift:73)
10  Eliah                           0x00000001004d25e4 LicenseValidator.constructLicense(with:) + 255460 (License.swift:64)
11  Eliah                           0x00000001004d3eb4 LicenseViewController.licenseScanned(_:) + 261812 (LicenseViewController.swift:35)
12  Eliah                           0x00000001004d518c partial apply for implicit closure #2 in implicit closure #1 in LicenseViewController.loadView() + 266636 (<compiler-generated>:0)
13  Eliah                           0x00000001004d6c9c specialized LicenseScanView.metadataOutput(_:didOutput:from:) + 273564 (LicenseScanView.swift:0)
14  Eliah                           0x00000001004d616c @objc LicenseScanView.metadataOutput(_:didOutput:from:) + 270700 (<compiler-generated>:0)
15  AVFoundation                    0x000000018a40ac2c -[AVCaptureMetadataOutput _processSampleBuffer:] + 1284
16  AVFoundation                    0x000000018a40a520 __46-[AVCaptureMetadataOutput _updateRemoteQueue:]_block_invoke + 100
17  CoreMedia                       0x00000001878c9118 __FigRemoteOperationReceiverCreateMessageReceiver_block_invoke + 280
18  CoreMedia                       0x00000001878e6718 __FigRemoteQueueReceiverSetHandler_block_invoke.2 + 224
19  libdispatch.dylib               0x0000000183d9c7d4 _dispatch_client_callout + 16
20  libdispatch.dylib               0x0000000183d4101c _dispatch_continuation_pop$VARIANT$mp + 412
21  libdispatch.dylib               0x0000000183d50fa8 _dispatch_source_invoke$VARIANT$mp + 1308
22  libdispatch.dylib               0x0000000183d451f0 _dispatch_lane_serial_drain$VARIANT$mp + 284
23  libdispatch.dylib               0x0000000183d45e74 _dispatch_lane_invoke$VARIANT$mp + 480
24  libdispatch.dylib               0x0000000183d49eec _dispatch_main_queue_callback_4CF$VARIANT$mp + 784
25  CoreFoundation                  0x00000001842efb20 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
26  CoreFoundation                  0x00000001842eaa58 __CFRunLoopRun + 1924
27  CoreFoundation                  0x00000001842e9fb4 CFRunLoopRunSpecific + 436
28  GraphicsServices                0x00000001864eb79c GSEventRunModal + 104
29  UIKitCore                       0x00000001b07efc38 UIApplicationMain + 212
30  Eliah                           0x0000000100499b88 main + 23432 (AppDelegate.swift:5)
31  libdyld.dylib                   0x0000000183dad8e0 start + 4
3

There are 3 best solutions below

2
On BEST ANSWER

CryptorEEC is open source. The last call to the stack that isn't a system framework is ECPublicKey.swift:144.

As you can see from that link, it's creating a dictionary with this value kSecAttrKeyTypeECSECPrimeRandom. According to the next call in the stack, this is causing the crash.

I searched for kSecAttrKeyTypeECSECPrimeRandom on Google and I found this question on SO.

This answer suggests that a key larger than 256 is too big for ECSEC.

This is an old question with old answers, which leads me to believe it's crashing on older devices with older iOS versions because support for larger keys was introduced in iOS 13 or later, or in devices with Secure Enclave (I'm not sure).

Since I don't know how you're using this encrypted token, I can't make a suggestion on how to fix this, but I hope it's at least enough information for you to make a decision.

4
On

Those are both old devices limited to iOS 12. It's not clear if you're able to get it to work on other iOS 12 devices, but I'd look at the common threads of OS version and architecture to see if you can spot a pattern.

Logically speaking, since it's only happening via distributions on the app store, either it's something on Apple's end and unlikely to be fixed since those devices are so old, or its something with the way you're publishing the binary to the app store. Logically, fiddling with the archive options in xcode would be something to try.

I think if it were me I would consider dropping support for those devices and moving on now that 81% of devices are running iOS 14. (But I can't speak for your situation).

In any case, good luck. That's a tough problem.

0
On

Although none of the answers really solved the issue, I still want to share the debugging approach and the final solution.

Analysing this issue

The development build weren't producing the bug. JWT tokens could be parsed just fine. To analyse the issue, I was able to create an Ad Hoc distribution and distribute it via a static web server.

Installing this way, the same issue were happening like with the App Store build.

By the way: it was irrelevant 'Rebuild from Bitcode' or not.

My Solution

...was to just switch to RSA signed tokens. Not my favourite, but somewhat practical to also support these devices.