0

I have pieced together the following code example to detect Mifare smartcards using the javax.smartcardio library. This code works great for the listed Mifare types.

public class DetectCardTester {
    private static final String PROTOCOL = "T=1";

    static Map<String,String> KNOWN_CARD_TYPES = ImmutableMap.<String, String>builder()
        .put("00-01", "Mifare 1K")
        .put("00-02", "Mifare 4K")
        .put("00-03", "Mifare Ultralight")
        .put("00-26", "Mifare Mini")
        .build();

    public static void main(String... args) {
        try {
            final TerminalFactory terminalFactory = SmartcardTerminalFactory.create();
            System.out.println("Place card on the reader");
            final Card card = awaitCard(terminalFactory, 3, SECONDS);
            final ATR atr = card.getATR();
            final byte[] bytes = atr.getBytes();

            System.out.println("ATR=" + String.valueOf(Hex.encodeHex(bytes, false)));

            if (bytes != null && bytes.length > 13) {
                String typeCode = String.format("%02X-%02X", bytes[13], bytes[14]);
                if (KNOWN_CARD_TYPES.containsKey(typeCode)) {
                    System.out.println("Known Type:" + KNOWN_CARD_TYPES.get(typeCode));
                } else {
                    System.out.println("Unknown Type:" + typeCode);
                }
            }
            // TODO: Detect Desfire Card

        } catch (Throwable t) {
            t.printStackTrace();
        }
    }


    public static Card awaitCard(final TerminalFactory terminalFactory, final int qty, final TemporalUnit unit) throws CardException {

        LocalDateTime timeout = now().plus(qty, unit);
        while (now().isBefore(timeout)) {
            Optional<CardTerminal> cardTerminal = getCardPresentTerminal(terminalFactory);
            if (cardTerminal.isPresent()) {
                return cardTerminal.get().connect(PROTOCOL);
            }
        }
        throw new CardNotPresentException("Timed out waiting for card");
    }

    private static Optional<CardTerminal> getCardPresentTerminal(final TerminalFactory terminalFactory) throws CardException {
        List<CardTerminal> terminals = terminalFactory.terminals().list();

        for (CardTerminal cardTerminal : terminals) {

            if (cardTerminal.isCardPresent()) {
                return Optional.of(cardTerminal);
            }
        }

        for (CardTerminal cardTerminal : terminals) {

            // waitForCardPresent / Absent doesn't work with some Jacax.smartcard.io implementations
            // i.e. OSX http://bugs.java.com/view_bug.do?bug_id=7195480
            // This is why we have the imediate check above
            if (cardTerminal.waitForCardPresent(250)) {
                return Optional.of(cardTerminal);
            }
        }

        return Optional.empty();
    }
}

I used the following resources to put this code together:

I would like to implement the TODO comment to detect Desfire cards. If I place a Desfire card on the read this code just outputs:

Place card on the reader
ATR=3B8180018080

I found this question Determine card type from ATR which helps a bit but I am missing something. The HistoricalByte for the card in question is 0x80 for which I am unable to find any information.

If possible I would greatly appreciate the above code example extended such that it can detect the Desfire card type.

Community
  • 1
  • 1
bludginozzie
  • 119
  • 2
  • 10
  • What about a custom ATS for your cards? Or sending the GetVersion? Or sending some command in native DESFire instead of wrapped. I don't come with another option as it is a ISO14443-4A. Even using SAK (not available in Java) you might find collisions. – jlanza Jan 18 '17 at 10:10
  • Thanks for you suggestion. I am unsure how to send an ATS using Java and also not sure how to use it to ensure the card is a Desfire card. I did think of using GetVersion however it feels like a hack! – bludginozzie Jan 20 '17 at 00:37
  • The ATS will be specifically for your card not a DESFire or at least I think so. Then, it is something you configure on your card. Concerning the GetVersion as a hack, I think considering it is a 4A type card it would be otherwise difficult. Actually, somebody can implement on JavaCard a similar command and fake it is a DESFire card :( – jlanza Jan 20 '17 at 08:34
  • http://smartcard-atr.appspot.com/ by Ludovic Rousseau is quite nice to map ATRs to card; Still there are very different cards, having the same ATR, so that does not suffice to identify your cards. – Daniel Heldt Jan 31 '17 at 13:43

1 Answers1

0

The Answer To Reset (ATR) is probably a good indication of what card you have. The SPEC that you are after is called "7816-3" section 8. Its actually a paid for spec but if you google well...

Anyhow, SIM specifications are complex to understand, they are well-ish defined but you need to go down to the bit level to fully understand it.

An ATR is decoded in bytes, lets decode your specific ATR:

Byte1 Called "TS", it is defined as either being 3B or 3F where 3F means "inverse convention" used and 3B "direct convention"

Byte2 Called "T0" , it is defined as the "format character". the first nibble of this byte "8" refers to a Y1 parameter and the second nibble refers to a "K" parameter. Your "T0" first nibble Y1 = 8 and second nibble K = 1 The Y1 parameter 8 = (in binary) 1000 meaning only a TA1 parameter is to follow. if it was 9 = (in binary) 1001 meaning a TA1 is on(1) TB1 is off (0) TC1 is off (0) and TD1 is on (1). K defines the amount of historical bytes i.e. 1 historical byte in your case.

To cut a long story short, yes "80" is your historical byte (defined in 7816-4 spec). 80 is the most basic historical byte you can have. 80 is the "category/status indicator" and all 80 is defined to mean:

A status indicator (one, two or three bytes) may be present in an optional COMPACT-TLV data object

the keyword is "may be present" i.e. in your case they have decided not to include any further information.

Back to your question, Checking for an ATR matching the exact value "3B8180018080" should be sufficient to identify the DESfire card. However in my application I have also used either the existence of a specific file (i.e. only found on a desfire card), contents of a file, or availability of a card specific application by AID to more accurately classify a card.

QuickPrototype
  • 833
  • 7
  • 18