0

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
Dennis Stritzke
  • 5,198
  • 1
  • 19
  • 28
  • Key seems to be invalid. – Sulthan Feb 01 '21 at 15:11
  • Nope. Key is valid and works just fine on other devices, when using the exact same JWT on another device having the same install. Having an invalid key runs into the catch clause and everything would be fine. Do I miss anything there? – Dennis Stritzke Feb 01 '21 at 15:32

3 Answers3

2

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.

Nicholas Rees
  • 723
  • 2
  • 9
  • Your remark lead to some more testing. Yes, the issue occurs on all iOS 12 devices (at least the two I have access to). Didn't know that before. Also on these devices, the app is just working fine when installed via Xcode. – Dennis Stritzke Feb 02 '21 at 13:24
  • Also, I would love to drop support for iOS 12. Unfortunately, we are targeting the health care market in Germany, where many doctors still use old(er) iPad minis... – Dennis Stritzke Feb 02 '21 at 13:25
  • That's a bummer. I think the answer below about keys larger than 256 being too big is likely on the right track, though I can't understand logically how it would work when attached to Xcode if that were the case. – Nicholas Rees Feb 02 '21 at 13:29
  • Think so, too. I am currently investigating that before commenting on that promising solution. Preview: Also doesn't seem to work with 256 bit keys. – Dennis Stritzke Feb 02 '21 at 13:31
1

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.

EmilioPelaez
  • 18,758
  • 6
  • 46
  • 50
  • Thank you for the analysis. Unfortunately, EC 256bit keys also do not work using the distributed binary. The processing of 256 and 512bit keys DOES work when running a dev build. Do you have any idea what's going on there? – Dennis Stritzke Feb 03 '21 at 20:05
  • I wish I could come up with a theory of why that is happening, I'm baffled. – EmilioPelaez Feb 03 '21 at 22:40
0

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.

Dennis Stritzke
  • 5,198
  • 1
  • 19
  • 28