Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[remote-reader] Fix ATR for 14443 Type B cards #251

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix 14443 Type B generation of wrapped ATR
  • Loading branch information
mildsunrise committed Mar 13, 2023
commit 1b5476940c30b58632241a09e510c77250e3c751
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcB;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
Expand Down Expand Up @@ -109,14 +111,14 @@ public void reset() throws IOException {
selectMF();
}

/* calculation based on https://code.google.com/p/ifdnfc/source/browse/src/atr.c */
/* generate the mapped ATR from 14443 data according to PC/SC part 3 section 3.1.3.2.3 */
@Override
public byte[] getATR() {
// get historical bytes for 14443-A
// for 14443 Type A, use the historical bytes returned as part of the ATS
byte[] historicalBytes = card.getHistoricalBytes();
if (historicalBytes == null) {
// get historical bytes for 14443-B
historicalBytes = card.getHiLayerResponse();
// for 14443 Type B, use Application Data + Protocol Info + MBLI
historicalBytes = getTypeBHistoricalBytes();
}
if (historicalBytes == null) {
historicalBytes = new byte[0];
Expand Down Expand Up @@ -177,4 +179,46 @@ public static NFCReader get(Tag tag, Activity activity) {
}
return nfcReader;
}
}

public byte[] getTypeBHistoricalBytes() {
NfcB nfcB = NfcB.get(card.getTag());
if (nfcB == null)
return null;

byte[] appData = nfcB.getApplicationData();
byte[] protocolInfo = nfcB.getProtocolInfo();
if (!(appData.length == 4 && protocolInfo.length == 3))
return null;

Byte mbli = translateToMbli(protocolInfo, nfcB.getMaxTransceiveLength());
if (mbli == null)
return null;

byte[] historicalBytes = new byte[8];
System.arraycopy(appData, 0, historicalBytes, 0, 4);
System.arraycopy(protocolInfo, 0, historicalBytes, 4, 3);
historicalBytes[7] = (byte)(mbli << 4);
return historicalBytes;
}

public static Byte translateToMbli(byte[] protocolInfo, int maxUnit) {
// retrieve maximum frame size from protocol info
int maxFrameSize = (protocolInfo[1] >> (byte)4) & 0xF;
if (maxFrameSize == 0)
return null;

// there's 3 to 5 bytes of overhead in a buffer.
int predictedMbl = maxUnit + 5; // (can be up to 2 bytes larger)

// buffer length must be maxFrameSize * {power of 2}.
int mbl = Integer.highestOneBit(predictedMbl / maxFrameSize) * maxFrameSize;
if (predictedMbl - mbl > 2)
return null;

// now that we know we have a valid MBL, calculate MBLI from it
int mbli = Integer.numberOfTrailingZeros(mbl / maxFrameSize) + 1;
if (mbli > 15)
return null;
return (byte)mbli;
}
}