LudovicRousseau / pyscard

pyscard smartcard library for python
http://pyscard.sourceforge.net/
GNU Lesser General Public License v2.1
383 stars 110 forks source link

Cards take too long to disconnect after authentication #60

Closed abkarino closed 5 years ago

abkarino commented 6 years ago

I have a script that authenticates Mifare Classic 1K. However, the reader takes around 2 second to be ready to sense another card. This issue happens if I attempt to authenticate the card using stored key. If I remove the code that authenticates (or throw the error Card not supported always), it takes less than 0.1 sec to sense the card is removed. Any ideas of the reason? P.S. The buzzer and LED control don't affect the bug. If removed, the led is turned off for 2 seconds after the communication. I am using Python27x64 on Windows10.

Here is my code:

class PrintObserver(CardObserver):
    """A simple card observer that is notified
    when cards are inserted/removed from the system and
    prints the list of cards
    """
    MifareClassic = [0x3B, 0x8F, 0x80, 0x01, 0x80, 0x4F, 0x0C, 0xA0, 0x00, 0x00, 0x03, 0x06, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x6A]

    Auth = [*********] #removed key info

    WrongBeeb = [0xFF, 0x00, 0x40, 0x5a, 0x04, 0x01, 0x01, 0x02, 0x01]
    AuthBlock15 = [0xFF, 0xB0, 0x00, 0x3c, 0x10]
    SuccessBeeb = [0xFF, 0x00, 0x40, 0x5a, 0x04, 0x01, 0x00, 0x01, 0x01]

    def update(self, observable, actions):
        (addedcards, removedcards) = actions
        for card in addedcards:
            try:
                card.connection = card.createConnection()
                card.connection.connect()
                if card.atr != self.MifareClassic:
                    raise Exception("Card not supported")
                data, sw1, sw2 = card.connection.transmit(self.Auth)
                if (sw1, sw2) == (0x90, 0x0):
                    data, sw1, sw2 = card.connection.transmit(self.AuthBlock15)
                    print ("block "+ str(15) +":\t"+ toHexString(data) +" | "+''.join(chr(i) for i in data))
                    card.connection.transmit(self.SuccessBeeb)
                elif (sw1, sw2) == (0x63, 0x0):
                    raise Exception("Key not working")
            except Exception as e:
                try:
                    card.connection.transmit(self.WrongBeeb)
                except:
                    pass
                print(e.message)
                pass
abkarino commented 6 years ago

@LudovicRousseau Sorry I overrode your changes. :)

LudovicRousseau commented 6 years ago

It may well be a "limitation" of the reader. The reader has no easy way to detect the card is no more present. So it must use a timeout. I don't know if you can change the reader timeout. I guess not.

abkarino commented 6 years ago

If it is the reader, then the timeout would be consistent. You see I send a buzzer code in case of fail. After it is done, it takes 2 second in case of I authenticated the card. But takes 0.1 if I don't.

abkarino commented 6 years ago

I am using ACR122U. If I don't run the python script, the response is 0.1

for card in addedcards:
            try:
                card.connection = card.createConnection()
                card.connection.connect()
                card.connection.transmit(self.SuccessBeeb)
                raise Exception("Card not supported")
            except Exception as e:
                try:
                    card.connection.transmit(self.WrongBeeb)
                except:
                    pass
                print(e.message)
                pass

This code results in 0.1 sec response time, so I am sure it is not the write process as I can't play the sound if the card is not found. So it is mainly data, sw1, sw2 = card.connection.transmit(self.Auth). Can you propose any approach to get more info about that? Will you have time, to check if you can reproduce it?

LudovicRousseau commented 6 years ago

You can try to get logs at the PC/SC level. But I have no idea how to do that on Windows.

abkarino commented 5 years ago

@LudovicRousseau do you provide a way to unpower card? It seems that my problem is releated to auto power off after 5 sec. I want to unpower card via code.

abkarino commented 5 years ago

I see hresult = SCardDisconnect(hcard, SCARD_UNPOWER_CARD) in get the ATR of a card but don't know how to integrate since I use observer approach.

abkarino commented 5 years ago

Also there is this: https://docs.microsoft.com/en-us/windows/desktop/api/winscard/nf-winscard-scarddisconnect

LudovicRousseau commented 5 years ago

SCardDisconnect() is part of the low level PySCard API. https://pyscard.sourceforge.io/epydoc/smartcard.scard.scard-module.html#SCardDisconnect

abkarino commented 5 years ago

How to integrate with high level API? Do I have to make the whole script based on low level API?

LudovicRousseau commented 5 years ago

The .connect() method has a disposition parameter. https://pyscard.sourceforge.io/epydoc/smartcard.CardConnection.CardConnection-class.html#connect

using disposition=SCARD_UNPOWER_CARD should unpower the card on disconnection.

abkarino commented 5 years ago

I think I tried it but didn't work, I will give it another try and let you know. Meanwhile, can your delete your edit from the post edit history, I cannot delete it and it contains the key. Thanks.