AndyQ / NFCPassportReader

NFCPassportReader for iOS 13
MIT License
733 stars 235 forks source link

[German ID] Error reading DG3 tag. Reason: Security status not satisfied. #22

Closed maxxx777 closed 4 years ago

maxxx777 commented 4 years ago
Reading tag - COM
Reading tag - SOD
Reading tag - DG1

are successful.

when reading DG3 tag, getting error:

Reading tag - DG3
Mask class byte and pad command header
    CmdHeader: <value>
Pad data
    Data: <value><value>
Encrypt data with KSenc
    EncryptedData: <value>
Build DO'87
    DO87: <value>
Concatenate CmdHeader and DO87
    M: <value>
        SSC: <value>
Compute MAC of M
    Increment SSC with 1
        SSC: <value>
    Concatenate SSC and M and add padding
        N: <value>
x0: <value>
y0: <value>
x1: <value>
y1: <value>
x2: <value>
y2: <value>
x3: <value>
y3: <value>
y: <value>
bkey: <value>
akey: <value>
b: <value>
a: <value>
    Compute MAC over N with KSmac
        CC: <value>
Build DO'8E
    DO8E: <value>
Construct and send protected APDU
    ProtectedAPDU: <value>
[SM] <NFCISO7816APDU: 0x283c67570>
Error reading tag: sw1 - 69, sw2 - 82 - reason: Security status not satisfied
2019-11-09 19:17:56.458315+0100 NFCPassportReaderApp[669:36407] [CoreNFC] 00000002 81c70900 -[NFCTagReaderSession setAlertMessage:]:92  (null)
ERROR - Security status not satisfied
Calculate the SHA-1 hash of MRZ_information
    Hsha1(MRZ_information): <value>
Take the most significant 16 bytes to form the Kseed
    Kseed: <value>
Calculate the Basic Acces Keys (Kenc and Kmac) using Appendix 5.1
Compute Encryption key (c: 00000001
    Concatenate Kseed and c
        D: <value>
    Calculate the SHA-1 hash of D
        Hsha1(D): <value>
    Form keys Ka and Kb
        Ka: <value>
        Kb: <value>
    Adjust parity bits
        Ka: <value>
        Kb: <value>
Compute MAC Computation key (c: 00000002
    Concatenate Kseed and c
        D: <value>
    Calculate the SHA-1 hash of D
        Hsha1(D): <value>
    Form keys Ka and Kb
        Ka: <value>
        Kb: <value>
    Adjust parity bits
        Ka: <value>
        Kb: <value>
DATA - [135, 254, 118, 14, 193, 128, 176, 231]
Request an 8 byte random number from the MRTD's chip
    RND.ICC: <value>
Generate an 8 byte random and a 16 byte random
    RND.IFD: <value>
    RND.Kifd: <value>
Concatenate RND.IFD, RND.ICC and Kifd
    S: <value>
Encrypt S with TDES key Kenc as calculated in Appendix 5.2
    Eifd: <value>
x0: <value>
y0: <value>
x1: <value>
y1: <value>
x2: <value>
y2: <value>
x3: <value>
y3: <value>
x4: <value>
y4: <value>
y: <value>
bkey: <value>
akey: <value>
b: <value>
a: <value>
Compute MAC over eifd with TDES key Kmac as calculated in-Appendix 5.2
    Mifd: <value>
Construct command data for MUTUAL AUTHENTICATE
    cmd_data: <value>
Error reading tag: sw1 - 6A, sw2 - 88 - reason: Referenced data not found
ERROR - The operation couldn’t be completed. (NFCPassportReader.TagError error 0.)
tagReaderSession:didInvalidateWithError - Error Domain=NFCError Code=200 "Session invalidated by user" UserInfo={NSLocalizedDescription=Session invalidated by user}

<value> replaces real values just to hide them in public.

Affected version: 1.0.0 ID country: Germany Running on Example app

Version 0.0.8 reads the same document with the same MRZ Key successfully.

AndyQ commented 4 years ago

Its very unlikely that you'll be able to read DG3 as that contains biometric info (fingerprints) and you need an additional private certificate/key to be able to read it.

However it should be skipped over and the rest of the passport read OK - is this not happening?

The last version made some changes so that if we got a security status failed then it ignored that DG and moved on to the next one. If this isn't happening please let me know!

maxxx777 commented 4 years ago

Its very unlikely that you'll be able to read DG3 as that contains biometric info (fingerprints) and you need an additional private certificate/key to be able to read it.

Thanks! Good to know!

However it should be skipped over and the rest of the passport read OK - is this not happening?

The last version made some changes so that if we got a security status failed then it ignored that DG and moved on to the next one. If this isn't happening please let me know!

looks like it doesn't. only if I add skipping DG3 explicitly

for dg in foundDGs {
   let id = DataGroupId.getIDFromName(name:dg)
   if dg != "DG3" {
      self.dataGroupsToRead.append(id)
   }
}

then it is skiped and finishes the reading successfully.

AndyQ commented 4 years ago

Thanks - looks like I'm not ha doing that as well as I thought. I'll get a fix for that hopefully tomorrow.

AndyQ commented 4 years ago

What Datagroups are contained in your passport? (there is a debug lin just after Reading tag - COM that should say: DG Found - ["DG1, .....]

Could you post that line?

maxxx777 commented 4 years ago

What Datagroups are contained in your passport? (there is a debug lin just after Reading tag - COM that should say: DG Found - ["DG1, .....]

Could you post that line?

DG Found - ["DG1", "DG3", "DG14", "DG2"]

AndyQ commented 4 years ago

I think I've found the problem. If DG3 is read last for some reason, then the passport read fails.

Could you please check 1.0.1 and let me know if this fixes your issue?

maxxx777 commented 4 years ago

Thanks, 1.0.1 fixes the issue. But know it doesn't display the photo from the id (It did with 1.0.0).

guard let dg2 = dataGroupsRead[.DG2] as? DataGroup2 else { return nil }  
return dg2.getImage()

returns nil because dataGroupsRead doesn't contain DG2 (contains only [DG1, COM, SOD]).

should I create a new issue for that?

AndyQ commented 4 years ago

Ahhh - GAH! I see what I've done! Did a quick version update and 1.0.2 should REALLY fix this!

maxxx777 commented 4 years ago

Looks like 1.0.2 reverts the initial issue again (Reading tag - DG3) https://github.com/AndyQ/NFCPassportReader/issues/22#issue-520495892 https://github.com/AndyQ/NFCPassportReader/issues/22#issuecomment-552235839

AndyQ commented 4 years ago

Hmm this is tricky! Could you send your whole log? I really can't replicate this at all!

No matter what order I read the DGs in, I'm getting everything I can read read in.

maxxx777 commented 4 years ago
Reading tag - DG3
Mask class byte and pad command header
    CmdHeader: <value>
Pad data
    Data: <value>
Encrypt data with KSenc
    EncryptedData: <value>
Build DO'87
    DO87: <value>
Concatenate CmdHeader and DO87
    M: <value>
        SSC: <value>
Compute MAC of M
    Increment SSC with 1
        SSC: <value>
    Concatenate SSC and M and add padding
        N: <value>
x0: <value>
y0: <value>
x1: <value>
y1: <value>
x2: <value>
y2: <value>
x3: <value>
y3: <value>
y: <value>
bkey: <value>
akey: <value>
b: <value>
a: <value>
    Compute MAC over N with KSmac
        CC: <value>
Build DO'8E
    DO8E: <value>
Construct and send protected APDU
    ProtectedAPDU: <value>
[SM] <NFCISO7816APDU: <value>>
Error reading tag: sw1 - 69, sw2 - 82 - reason: Security status not satisfied
ERROR - Security status not satisfied
Starting Basic Access Control (BAC)
Calculate the SHA-1 hash of MRZ_information
    Hsha1(MRZ_information): <value>
Take the most significant 16 bytes to form the Kseed
    Kseed: <value>
Calculate the Basic Acces Keys (Kenc and Kmac) using Appendix 5.1
Compute Encryption key (c: 00000001
    Concatenate Kseed and c
        D: <value>
    Calculate the SHA-1 hash of D
        Hsha1(D): <value>
    Form keys Ka and Kb
        Ka: <value>
        Kb: <value>
    Adjust parity bits
        Ka: <value>
        Kb: <value>
Compute MAC Computation key (c: 00000002
    Concatenate Kseed and c
        D: <value>
    Calculate the SHA-1 hash of D
        Hsha1(D): <value>
    Form keys Ka and Kb
        Ka: <value>
        Kb: <value>
    Adjust parity bits
        Ka: <value>
        Kb: <value>
DATA - [119, 34, 117, 73, 106, 167, 154, 249]
Request an 8 byte random number from the MRTD's chip
    RND.ICC: <value>
Generate an 8 byte random and a 16 byte random
    RND.IFD: <value>
    RND.Kifd: <value>
Concatenate RND.IFD, RND.ICC and Kifd
    S: <value>
Encrypt S with TDES key Kenc as calculated in Appendix 5.2
    Eifd: <value>
x0: <value>
y0: <value>
x1: <value>
y1: <value>
x2: <value>
y2: <value>
x3: <value>
y3: <value>
x4: <value>
y4: <value>
y: <value>
bkey: <value>
akey: <value>
b: <value>
a: <value>
Compute MAC over eifd with TDES key Kmac as calculated in-Appendix 5.2
    Mifd: <value>
Construct command data for MUTUAL AUTHENTICATE
    cmd_data: <value>
Error reading tag: sw1 - 6A, sw2 - 88 - reason: Referenced data not found
ERROR - The operation couldn’t be completed. (NFCPassportReader.TagError error 0.)
BAC Failed
tagReaderSession:didInvalidateWithError - Error Domain=NFCError Code=200 "Session invalidated by user" UserInfo={NSLocalizedDescription=Session invalidated by user}
AndyQ commented 4 years ago

Sorry, could you include the whole steps - that looks like you are only trying to read the DG3 element (which you can't read due to security). Could you also set the LogLevel to .info (in Logging.swift) as I don't need to see all the chip details.

For info, you can read the COM (directory index), SOD (security object), DG1 (MRZ info), DG2 (image), DG7 (signature if present), DG11 (additional personal info if present), DG12 (additional issuer info if present), DG14 (chip authentication signatures if present), DG15 (active authentication key if present).

You can't read DG3 (biometric info) as those certificates are restricted to Government use Other DGs aren't yet implemented.

If you explicitly only try to read DG3 then you won't get anything back.

maxxx777 commented 4 years ago

Yep, I read not only DG3, sorry didn't add the logs for other tags.

The whole log with info log level:

Starting Basic Access Control (BAC)
BAC Successful
Reading tag - COM
DG Found - ["DG1", "DG3", "DG14", "DG2"]
Reading tag - SOD
Reading tag - DG1
Reading tag - DG3
Error reading tag: sw1 - 69, sw2 - 82 - reason: Security status not satisfied
ERROR - Security status not satisfied
Starting Basic Access Control (BAC)
Error reading tag: sw1 - 6A, sw2 - 88 - reason: Referenced data not found
BAC Failed
AndyQ commented 4 years ago

OK, that looks like the re-doing of BAC fails for some reason. (we need to do this after we fail because the original BAC gets invalidated). This could be a feature of the passport (it doesn't do it with my test passports so I've not seen this).

Could you please try adding slight delay before re-doing BAC? E.g. the below would add a 1 second delay if you change line 256-268 in PassportReader from: } else if errMsg == "Security status not satisfied" { // Can't read this element as we aren't allowed - remove it and return out so we re-do BAC self.dataGroupsToRead.removeFirst() completed(nil)

to: } else if errMsg == "Security status not satisfied" { // Can't read this element as we aren't allowed - remove it and return out so we re-do BAC self.dataGroupsToRead.removeFirst() DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { completed(nil) })

And let me know if that fixes it.

maxxx777 commented 4 years ago

Could you please try adding slight delay before re-doing BAC? E.g. the below would add a 1 second delay if you change line 256-268 in PassportReader from: } else if errMsg == "Security status not satisfied" { // Can't read this element as we aren't allowed - remove it and return out so we re-do BAC self.dataGroupsToRead.removeFirst() completed(nil)

to: } else if errMsg == "Security status not satisfied" { // Can't read this element as we aren't allowed - remove it and return out so we re-do BAC self.dataGroupsToRead.removeFirst() DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { completed(nil) })

And let me know if that fixes it.

It doesn't help, even with the delay more than 1 second.

AndyQ commented 4 years ago

Hmm, I'm fairly stumped as to why re-doing BAC is failing. Not something I've come across on any of my test passports.

So for now, a workaround is to just not read DG3.

e.g. let dataGroups : [DataGroupId] = [.COM, .SOD, .DG1, .DG2] passportReader.readPassport(mrzKey: mrzKey, tags:dataGroups, completed: .....)

I'll look at adding an option to not even attempt to read secured elements in the next version (maybe will make that the default).

maxxx777 commented 4 years ago

Ok, thanks for your help! I'll find some time later to investigate and share my findings if I have something.

AndyQ commented 4 years ago

1.0.3 no longer reads the DG3 (or DG4) by default - hopefully that fixes this issue!

maxxx777 commented 4 years ago

Thanks, that fixes the issue!