NFCPassportReader: Error identifying Active Authentication Hash Algorithm - Australian ePassport

I used the sample application (SPM) to scan an Australian ePassport Issued 2023 and found Passive Authentication succeeds while Active Authentication fails with the following error printed in the session output:

...
2023-11-02 8:29:10.8570 - Reading tag - DG15
2023-11-02 8:29:10.9570 - TagReader - Number of data bytes to read - 294
2023-11-02 8:29:11.0460 - Performing Active Authentication
2023-11-02 8:29:11.8720 - Error identifying Active Authentication RSA message digest hash algorithm
2023-11-02 8:29:11.9670 - Passport passed SOD Verification
2023-11-02 8:29:11.9790 - Parse SOD - Using Algo - SHA256
2023-11-02 8:29:11.9800 -       - Hashes     - [NFCPassportReader.DataGroupId.DG15: "F3FB8A3D6346B37293BBF5939ED7C04369B7F8B642A27B6BAFA979CABEF561B9", NFCPassportReader.DataGroupId.DG1: "2C3BFF5F63EBE095A5F9E16C23C1D3792F7571635A56F2C1AB693E2D051E63DA", NFCPassportReader.DataGroupId.DG2: "73A88BDC89B7215D824174D67C53DF6B86E32AC9D262E67EFB2CA9F99A2F3F29", NFCPassportReader.DataGroupId.DG14: "06CAA3C87D4CF49923140662BC4565E0A578DA2F5717363869D30B9E9C7FB80F", NFCPassportReader.DataGroupId.DG7: "47636EFC50B4AEDC7D74C1DBFDA03F5B860EE107D0A4718BEA248C99AE843D42"]
2023-11-02 8:29:11.9800 - Passport passed Datagroup Tampering check

Any idea what is happening here?

Thanks 🙏

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Comments: 28 (11 by maintainers)

Most upvoted comments

Hi @AndyQ @harry-anderson,

We’re recently run into the same issue and together with @donbobka we’ve made some discoveries that we hope will help in solving this issue 🙂

Australian R series passport:

  • uses a 256 byte RSA key for AA, so the expected AA signature length is also exactly 256 bytes
  • we’re only getting 231 bytes back in AA response after unwrapping from the Secure Messaging envelope
  • the chip doesn’t seem to support response chaining - we’re getting 0x90 0x00 status, which indicates success and no remaining data available - the fix for Finnish passports doesn’t apply here - apparently the Australian chip just silently truncates the response here
  • OpenSSLUtils.decryptRSASignature called on truncated data is effectively random bytes, so we fail to identify the digest algorithm based on decrypted signature trailer
  • we’ve managed to get AA working by forcing extended length APDU for AA request (both nested and Secure Messaging envelope) and response:
diff --git a/Sources/NFCPassportReader/SecureMessaging.swift b/Sources/NFCPassportReader/SecureMessaging.swift
index 575185a..765745b 100644
--- a/Sources/NFCPassportReader/SecureMessaging.swift
+++ b/Sources/NFCPassportReader/SecureMessaging.swift
@@ -88,7 +88,7 @@ public class SecureMessaging {
         // otherwise its a single byte of size
         let size = do87.count + do97.count + do8e.count
         var dataSize: [UInt8]
-        if size > 255 {
+        if size > 255 || apdu.expectedResponseLength > 256 { // possibly should be apdu.expectedResponseLength > 231 to account for SM envelope
             dataSize = [0x00] + intToBin(size, pad: 4)
         } else {
             dataSize = intToBin(size)
@@ -98,7 +98,7 @@ public class SecureMessaging {
 
         // If the data is more that 255, specify the we are using extended length (0x00, 0x00)
         // Thanks to @filom for the fix!
-        if size > 255 {
+        if size > 255 || apdu.expectedResponseLength > 256 { // possibly should be apdu.expectedResponseLength > 231 to account for SM envelope
             protectedAPDU += [0x00,0x00]
         } else {
             protectedAPDU += [0x00]
diff --git a/Sources/NFCPassportReader/TagReader.swift b/Sources/NFCPassportReader/TagReader.swift
index 8791622..ac8366b 100644
--- a/Sources/NFCPassportReader/TagReader.swift
+++ b/Sources/NFCPassportReader/TagReader.swift
@@ -52,7 +52,7 @@ public class TagReader {
     func doInternalAuthentication( challenge: [UInt8] ) async throws -> ResponseAPDU {
         let randNonce = Data(challenge)
 
-        let cmd = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x88, p1Parameter: 0, p2Parameter: 0, data: randNonce, expectedResponseLength: 256)
+        let cmd = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x88, p1Parameter: 0, p2Parameter: 0, data: randNonce, expectedResponseLength: 65536)
 
         return try await send( cmd: cmd )
     }
  • notice that we had to make the change in both places inside protect, despite the fact that we’re not sending more than 256 bytes, but only expecting to receive more than 256 bytes back (our guess is that we must use extended APDU format for both request and response together)
  • our fix might break AA for some older passport that don’t support extended length APDU at all, so we’re not going to submit that PoC as a PR 🙂 (also we don’t know what will happen for Finnish passports - with that fix we would use extended length APDU instead of response chaining, so the previous fix might be unnecessary anymore)

We used to have the same issue on android using jmrtd library and it was just recently fixed in https://sourceforge.net/p/jmrtd/code/1878/ - seems like they try to predict the AA signature size based on the AA public key from DG15.

We might submit a PR with a proper fix based on jmrtd’s approach in the following days/weeks, but we would also be very happy for you to do it, since we don’t have access to too many passport to test against and our understanding of the ICAO standards and NFC tech is also quite limited 😅

I did this for the reading of the data groups and it works fine (Swedish National ID) and much faster. It could be a good idea to have this as the default and then reintroduce the fallback mechanism to drop to 0xA0 (160) on failure.

Thats great news, I’ll test out across my dev passports hopefully today (work has got in the way this week!) and see if thats a compatible change.

OK, so thats out then, looks like it didn’t even get the signature back this time (which is odd esp as no error).

Am a little stumped at the moment.