roelderickx / connective-plugin-linux

A replacement for the Connective Plugin which is used on several websites to log in or sign documents using a card reader and an electronic identity card.
GNU General Public License v3.0
36 stars 2 forks source link

Website still asking to install Connective Browser Package #1

Closed ghpille closed 2 years ago

ghpille commented 2 years ago

I failed to install connective_signing_extension-1.0.4.xpi in Seamonkey, which failed (xpi corrupt), so I tried Firefox ESR from debian, which apparantly succeeded. But trying to register on itsme.be, the page https://bmid-id.connective.eu/readout.html still wants to install its package, which it doesn't have for my operating system. End of story.

When I asked Itsme support when the package for Linux would be available, I got an explanation how to use Virtualbox.

Kind regards, Gerard

BrechtSerckx commented 2 years ago

Same issue on Firefox 93.0 (NixOS 21.05), it doesn't accept the manually installed addon.

roelderickx commented 2 years ago

Virtualbox is not a solution, only a way to shirk their responsability 🙄 They should be ashamed to even propose this.

You'll get the same error message when the backend is incorrectly installed or not running properly, even if the installation of the xpi succeeded.

ghpille commented 2 years ago

It starts without any message, even when no cardreader is connected. Any way to do some testing without a website?

BrechtSerckx commented 2 years ago
* Can you run the backend (connective-backend.py) from the terminal? I assumed tkinter is always included with python, but at least in ubuntu it does not seem to be the case. `apt-get install python3-tk` resolved this. There are other dependencies to resolve first, the backend should start without any message, waiting for input.

Installing tkinter and fixing the shebang for nix solved it for me, thanks!

BrechtSerckx commented 2 years ago

It starts without any message, even when no cardreader is connected. Any way to do some testing without a website?

If the native mechanism works, it should output any errors in the browser console, do you have any?

ghpille commented 2 years ago

It doesn't work and doesn't output errors. I started it before firefox, nothing. I killed it, straced firefox-esr, and as far as I could see, firefox doesn't try to start it. How would it communicate with a running connective-backend.py? In the code I saw "stdio", in which case it would only work if firefox starts connective-backend.py itself.

ghpille commented 2 years ago

oops: ii python3-pyscard 2.0.0-1+b2 amd64 Python3 wrapper above PC/SC API

roelderickx commented 2 years ago

It starts without any message, even when no cardreader is connected. Any way to do some testing without a website?

Yes of course. I added a test directory with an html page to the repository, where you can send messages to the backend. There are some sample messages included on the page for your convenience, but you have to modify the reader property to select the correct smartcard reader. The answer from the backend is not visible, except if you enable the logging on lines 320 and 326 and open the browser console while sending.

ghpille commented 2 years ago

Great, in the mean time I did a pip install pyscard, and ran some tests from pyscard which all worked with my AGP8201. Off to your test page.

ghpille commented 2 years ago

I get "Request sent" and that is it. No answer, no console output.

ghpille commented 2 years ago

After sending a request, shouldn't it try to read an answer?

roelderickx commented 2 years ago

I get "Request sent" and that is it. No answer, no console output.

I am not really sure how to intercept the response in the html page, I didn't bother with it. Make sure you enable the logging as described earlier and open the browser console via ctrl-shift-J. It is somehow different from F12 > Console.

roelderickx commented 2 years ago

After sending a request, shouldn't it try to read an answer?

I'll have a look if I can add that, it would indeed be more useful than opening the browser console and enabling log.

ghpille commented 2 years ago

Didn't know about that console, but all I get is when loading that page: Cc[aContract] is undefined XPCOMUtils.jsm:215

ghpille commented 2 years ago

I didn't enable logging.

roelderickx commented 2 years ago

I have updated the html page, the response is added too. No need to enable logging or open the browser console anymore. The reader property in the READ_FILE and COMPUTE_AUTHENTICATION messages should be copied from the response of the GET_READERS message.

ghpille commented 2 years ago

OK, we have liftoff. Get Info & Get Readers working, but read file: Response received: {"error":{"code":5,"id":5,"message":"Error reading file (Comm 0x6a87) (0xa4080c)"},"resp":"READ_FILE"}

ghpille commented 2 years ago

In the mean time, https://bmid-id.connective.eu/readout.html is no longer asking for the package, but works as well as under Windoze: "An error has occurred".

ghpille commented 2 years ago

FYI: Request sent: {"cmd":"COMPUTE_AUTHENTICATION","reader":"ACS APG8201 USB Reader 00 00","hash":"31CD354EACB4F4B43ACADFBC75D6E178E53CDB09FEE0B8A063932BDEECC56AC4","activationToken":"Z9/qe5+6bzQMwLPdQ4XAcTRdWLaAS0kNFw16ybUw1vW/r4k9l+xQT6CokTeX2Z+lvkn/QYorLTPlZFOjVrOAcOA48Q4nVyRLECigTUIqMw6/UF9wzCrhcRuADNtKXkfNhXKsyIXKsqmGiE0IDqloooIo0+NFwizJkESj+FTUk/rsb5Jm8CIU0yhDsaRJGY5lCpmNcEvhhm6tut/pME+DdezTDZ/1Kjj43Wx/B/4w8lmQsk+E+14/Quksr1RPIu9ikkUDTGyBpkBnN/No1q2+ZHOFb8Cddxzpca5pegk1WZ3WPlUATD+K5/oYn+DW2e3mU9oc4JyzH4c1RPeCmIhXeQ==","isRequest":true} Response received: {"pinRemainingAttempts":-1,"pinValid":true,"valid":false,"resp":"COMPUTE_AUTHENTICATION"}

roelderickx commented 2 years ago

In the mean time, https://bmid-id.connective.eu/readout.html is no longer asking for the package, but works as well as under Windoze: "An error has occurred".

Now it is getting interesting, at least we have consistent behaviour across all operating systems :-) It may be a problem with your card since the READ_FILE request really is answered by your identity card rather than your card reader, but let's not rule out anything for now. From your last message I understand that the authentication succeeds (this is a response from the card reader), but somehow your card is unable to calculate a signature (this is a response from the card). I will create a test script to see the exact response of the card / card reader in stead of the Connective error code, to have a more detailed log of what happens. It may take a few evenings though, I have to make sure your card does not get blocked and that the output does not contain sensitive data. Just for information, does your identity card and card reader work for the websites of the belgian government (for example tax-on-web)? It must be said that the code base of the eid middleware of Fedict is pretty solid, if this works for you it may help to find a solution here too.

ghpille commented 2 years ago

FYI: https://github.com/Lapin-Blanc/pythonbeid/blob/master/beid.py can read my information.

ghpille commented 2 years ago

Beid works, use it regularly.

ghpille commented 2 years ago

FYI: return get_error(5, 'Error reading file (Comm 0x6a87) (0xa4080c)') is returned both for beid_card.select_file(request_file_id) as for read_selected_file. also, request_file_id = request_json['fileId'] if 'reader' in request_json else None should perhaps be "if 'fileId' in request.json?

ghpille commented 2 years ago

OK, this got it working in read_selected_file

            if sw1 == 0x90 and sw2 == 0x00:
                log('data: %s' % data)
                file_contents.extend(data)
                if len(data) != 256:
                    length = -1
                else:
                    offset += 256
            elif sw1 == 0x6C:
                request_data = [0x00,0xB0,int(offset/256),offset%256,sw2]
                data, sw1, sw2 = self.connection.transmit(request_data)
                log('data: %s' % data)
                file_contents.extend(data)
                length = -1
            elif sw1 == 0x6B and sw2 == 0x00:
            ....
ghpille commented 2 years ago

On 22/12/2021 21:21, Roel Derickx wrote:

In the mean time, https://bmid-id.connective.eu/readout.html is no longer asking for the package, but works as well as under
Windoze: "An error has occurred".

Now it is getting interesting, at least we have consistent behaviour across all operating systems :-) It may be a problem with your card since the READ_FILE request really is answered by your identity card rather than your card reader, but let's not rule out anything for now. From your last message I understand that the authentication succeeds (this is a response from the card reader), but somehow your card is unable to calculate a signature (this is a response from the card). I will create a test script to see the exact response of the card / card reader in stead of the Connective error code, to have a more detailed log of what happens. It may take a few evenings though, I have to make sure your card does not get blocked and that the output does not contain sensitive data. Just for information, does your identity card and card reader work for the websites of the belgian government (for example tax-on-web)? It must be said that the code base of the eid middleware of Fedict https://github.com/Fedict/eid-mw is pretty solid, if this works for you it may help to find a solution here too.

—

Are these guys even aware that you're doing their job? itsme.be, connective.eu

roelderickx commented 2 years ago

FYI: return get_error(5, 'Error reading file (Comm 0x6a87) (0xa4080c)') is returned both for beid_card.select_file(request_file_id) as for read_selected_file. also, request_file_id = request_json['fileId'] if 'reader' in request_json else None should perhaps be if 'fileId' in request_json?

Yes idd this is a copy-paste bug. I modified it and pushed it to github.

OK, this got it working in read_selected_file

            if sw1 == 0x90 and sw2 == 0x00:
                log('data: %s' % data)
                file_contents.extend(data)
                if len(data) != 256:
                    length = -1
                else:
                    offset += 256
            elif sw1 == 0x6C:
                request_data = [0x00,0xB0,int(offset/256),offset%256,sw2]
                data, sw1, sw2 = self.connection.transmit(request_data)
                log('data: %s' % data)
                file_contents.extend(data)
                length = -1
            elif sw1 == 0x6B and sw2 == 0x00:
            ....

Thanks for your investigation! It seems not all card readers return the 0x6C code, since it worked for me and for @BrechtSerckx. You implemented it according to the documentation so I committed this change to github as well. There may be an issue for v1.8 identity cards (recent cards with a new layout, not sure if you have seen them already) but I'll implement that later.

Then we're still stuck at the COMPUTE_AUTHENTICATION message. Can you try to find out which are the return codes for the MSE:SET command at line 298 and the PSO:CDS at line 304? I think we have to detect the available algorithms first, before MSE:SET, but that is just a first guess.

Are these guys even aware that you're doing their job? itsme.be, connective.eu

I don't think so. Let's hope they do not change the version too often because that will require changes here too.

ghpille commented 2 years ago

I've got some extra logging in my version, so line numbers are problematic. This is the "sign" procedure?

select algorithm returned 90 0 length 0
signing returned 61 0 length 0
roelderickx commented 2 years ago

I've got some extra logging in my version, so line numbers are problematic. This is the "sign" procedure?

select algorithm returned 90 0 length 0
signing returned 61 0 length 0

Indeed, the sign function. 61 means success as well, just as it does in the __select_applet function, but the difference with 90 is that the response should be fetched using a GET RESPONSE. I'll see in the evening if I can implement it, but it really is not that hard. See the documentation at https://github.com/Fedict/eid-mw/blob/master/doc/sdk/documentation/Applet%201.7%20eID%20Cards/Public_Belpic_Applet_v1%207_Ref_Manual%20-%20A01.pdf

BrechtSerckx commented 2 years ago

Thanks for your investigation! It seems not all card readers return the 0x6C code, since it worked for me and for @BrechtSerckx.

FYI: I also get an error on the next step, only the plugin detection works. I have a Vasco Digipass 905 without numpad though, so my error could be totally unrelated. I'll investigate that a bit further.

ghpille commented 2 years ago

This makes it work

            if sw1 == 0x90 and sw2 == 0x00:
                return smartcard.util.toHexString(data).replace(' ', '')
            elif sw1 == 0x61:
                # get_response needed with sw2 length
                request_data = [ 0x00, 0xC0, 0x00, 0x00, sw2 ]
                data, sw1, sw2 = self.connection.transmit(request_data)
                log('get response returned %x %x length %d' % (sw1,sw2,len(data)))
                return smartcard.util.toHexString(data).replace(' ', '')
            else:
                return None

response:

{"pinRemainingAttempts":-1,"pinValid":true,"valid":true,"signature":"69957AF0CAFC11C53FF793F3A8FCB718ABEC94A02B7F589B8B06D8D8B9197570DFC5ABBA54445C84302500A3877E4B3399F19995ECCF2F6B1276134183AEFC5972C4E4440F60D4FCB6F4A83039ACB0AC7753UUXX989FC5C0FF70975CBFCAB8499C66D07719938EE30C3A3433C033936A07FE391ABDE6801951E5A2236A15EAF67EAFB88EB0ZZ0C0BA97666D9A7BB803734FAC8DE01GHK96CE79C3D0DA363ADA101B6FD48ED1520DB1A4D2983A268C82BB3F0299CC45C9EEDC50B0095E297720405EAF2AC532CC7B5F22D87E1153E40B47D61LL9C55D76F8B86CA138D046956853AB8A71F799683FA9FDFC046B024338B77KLMN749D2EC6E1655F635935D4D96A","resp":"COMPUTE_AUTHENTICATION"}
roelderickx commented 2 years ago

Once more many thanks for your investigation! The response seems correct but the new [ 0x00, 0xC0, 0x00, 0x00, sw2 ] request can once again return 0x61. So we should loop. I had a look at the reference implementation from fedict and implemented a general send_apdu function to deal with both 0x61 and 0x6C.

@ghpille Can you test if it works with the latest version from github? It would confirm that all possible scenarios regarding return values are supported.

@BrechtSerckx I must have misunderstood you when thinking it worked. I opened a new issue to implement the on-screen numpad but I propose to get this issue resolved first before continuing. I will revert to you for testing.

ghpille commented 2 years ago

The tests from the html all succeed, but https://bmid-id.connective.eu/readout.html fails, which it didn't with my version. I'll look for the differences.

ghpille commented 2 years ago
>     def __send_apdu(self, apdu):
>         data, sw1, sw2 = self.connection.transmit(apdu)
>         if len(data) == 0:
>             if sw1 == 0x61:
>                 return self.__send_apdu([ 0x00, 0xC0, 0x00, 0x00, sw2 ])
>             if sw1 == 0x6C:
>                 # TODO v1.8 cards need a delay of 50ms here
>                 return self.__send_apdu(apdu[0:4] + [ sw2 ] + apdu[5:])
>         elif len(data) == 256:
>             while sw1 == 0x61:
>                 extra_bytes, sw1, sw2 = self.__send_apdu([ 0x00, 0xC0, 0x00, 0x00, sw2 ])
>                 data.extend(extra_bytes)
>         return data, sw1, sw2

When reading a file, len(data) == 256 but sw1 == 0x90, so the extra bytes are never fetched, I think. This is the sequence when my version reads a larger file, eg. the photo:

request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 90 0 length 256
request_data returned 6c e6 length 0
request_remainder returned 90 0 length 230

I don't feel comfortable with send_apdu and its recursive calls, difficult to get the logic. Your previous version was more straightforward.

roelderickx commented 2 years ago

I don't feel comfortable with send_apdu and its recursive calls, difficult to get the logic. Your previous version was more straightforward.

Idd. It is how the fedict version works but I don't see the added value of the recursive call either.

ghpille commented 2 years ago

The response seems correct but the new [ 0x00, 0xC0, 0x00, 0x00, sw2 ] request can once again return 0x61.

Are you sure about that? Eid-mw's BeidReadFile in ./cardcomm/minidriver/src/SmartCard.c handles it the way I did. Anything but a x90,x00 after that request is terminal. BTW, Merry Xmas & Zalig Kerstfeest if I don't hear from you today.

roelderickx commented 2 years ago

According to the documentation a GET RESPONSE request returns as many times 0x61 as necessary to send all data in 256-byte chunks. For now it is irrelevant, this message is only required to fetch the signature which is exactly 256 bytes long. But if Connective decides to move forward to a longer signature in the future this will become important. Not to be confused with a 0x6C, which is indeed only sent once at the end of both a GET RESPONSE and a READ BINARY message to indicate there are less than 256 bytes left. A new request with the correct length is expected to return 0x90 then. It seems my cardreader intercepts the 0x6C and automatically retries with the correct length, I always get 0x90 but for the last chunk the length of the data is just smaller. I wonder what happens if the file size is a multiplication of 256. I think I can test with another card reader during the weekend, let's see if I can find a solution which always works. It is essential to get this resolved as correct and general as possible to make sure all cardreaders are supported, and to avoid future bugs if the Connective protocol is changed.

Hoe dan ook, eerst is er Kerstmis. Zalig Kerstfeest 🎄

ghpille commented 2 years ago

Where in the documentation do you find that? The differences between your tests and mine, are they due to the card or the reader? When I read the get response documentation in the Belpic Ref, you'll never get a 61 00, which would mean "there are exactly 256 bytes left. The meaning of "61 xx" is "'xx' remaining bytes to retrieve from the card, through subsequent Get Response command", not "commandS". I would like you to prove me wrong.

roelderickx commented 2 years ago

It is at least how I interprete table 10 on page 19 of https://github.com/Fedict/eid-mw/blob/master/doc/sdk/documentation/Applet%201.7%20eID%20Cards/Public_Belpic_Applet_v1%207_Ref_Manual%20-%20A01.pdf, and how it is implemented in https://github.com/Fedict/eid-mw/blob/master/cardcomm/pkcs11/src/cardlayer/card.cpp#L523

The differences between your tests and mine, are they due to the card or the reader?

This is really a good question. To be honest I don't know, but it is a remarkable difference. I have no 0x6C response, nor a 0x61 and as such I don't need GET RESPONSE to fetch the signature.

ghpille commented 2 years ago

I have another reader, I'll check if it works under Linux. Bus 002 Device 002: ID 1486:2535 SCM PC-Card GmbH USB2.0 Multi-Card Reader pcsc_scan doesn't even see it.

roelderickx commented 2 years ago

When I read the get response documentation in the Belpic Ref, you'll never get a 61 00, which would mean "there are exactly 256 bytes left. The meaning of "61 xx" is "'xx' remaining bytes to retrieve from the card, through subsequent Get Response command", not "commandS". I would like you to prove me wrong.

I don't see a reason why Get Response cannot return "61 xx" again, if more data is available.

The differences between your tests and mine, are they due to the card or the reader?

Yesterday I had the opportunity to test with an Alcor Micro AU 9560 and I can confirm the difference is due to the card reader, not the card.

Alcor Micro AU9560

APDU sent: 00,a4,08,0c,06,3f,00,df,01,40,35 - received 0x90 0x00 len 0
APDU sent: 00,b0,00,00,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,00,fc,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,01,f8,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,02,f4,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,03,f0,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,04,ec,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,05,e8,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,06,e4,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,07,e0,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,08,dc,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,09,d8,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,0a,d4,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,0b,d0,fc - received 0x6c 0x0e len 0
APDU sent: 00,b0,0b,d0,0e - received 0x90 0x00 len 14
APDU sent: 00,2a,9e,9a,20,[REDACTED],00 - received 0x61 0x00 len 0
APDU sent: 00,c0,00,00,00 - received 0x90 0x00 len 256

VASCO DIGIPASS 870

APDU sent: 00,a4,08,0c,06,3f,00,df,01,40,35 - received 0x90 0x00 len 0
APDU sent: 00,b0,00,00,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,00,fc,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,01,f8,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,02,f4,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,03,f0,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,04,ec,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,05,e8,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,06,e4,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,07,e0,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,08,dc,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,09,d8,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,0a,d4,fc - received 0x90 0x00 len 252
APDU sent: 00,b0,0b,d0,fc - received 0x90 0x00 len 14
APDU sent: 00,2a,9e,9a,20,[REDACTED],00 - received 0x90 0x00 len 256

I have removed the recursive calls from send_apdu and tested the same code on both card readers, it worked for me in both cases. The changes are committed to github, please test on your end.

ghpille commented 2 years ago

On 26/12/2021 11:33, Roel Derickx wrote:

I don't see a reason why Get Response cannot return "61 xx" again, if more data is available.

Because it's illogic.

Say you request 256 bytes. Repeatedly.

until you receive, say, a 61 10, and no data.

So you request those 16 bytes and should get 16 bytes and a 90 00 status.

I can't imagine - but here I can be wrong - that the card was mistaken or has changed its mind, in which case there are less or more bytes available. The beid code I saw, both python and C, did not provision for that.

roelderickx commented 2 years ago

Ok I see your point now.

Say you request 256 bytes. Repeatedly.

I may be wrong here, but I think we repeatedly receive a 61 xx in stead of 90 00, to indicate we should continue fetching from get response in stead of the original compute authentication request.

until you receive, say, a 61 10, and no data.

So you request those 16 bytes and should get 16 bytes and a 90 00 status.

I can't imagine - but here I can be wrong - that the card was mistaken or has changed its mind, in which case there are less or more bytes available. The beid code I saw, both python and C, did not provision for that.

If the length is not ok we would receive a 6c xx, that's why the C version implemented it recursively. But I agree this is overdefensive. It shouldn't happen since the previous 61 xx has given us the right length. A return value of 6c xx for get response can only point to a bug in the code, so I'd rather see an error in this case.

ghpille commented 2 years ago

I'll give the new code a try. I saw the new code for the buttons, and in an earlier life I did tinker with Tk and now Tkinter a little: you may be able to reduce the GUI code a lot. Let me give you an example:

import tkinter as tk
from tkinter import ttk

def bHit(what):
  print(what)

root = tk.Tk()
root.geometry('200x600')
root.resizable(False, True)
root.title('Button Demo')

buttons = []
for i in range(10):
  buttons.append(ttk.Button(root,text=i,command=lambda i=i: bHit(i)));

for b in buttons:
  b.pack()

exit_button = ttk.Button(    root,    text='Exit',    command=lambda: root.quit())
exit_button.pack(    ipadx=5,    ipady=5,    expand=True)

root.mainloop()
ghpille commented 2 years ago

All tests succeed, as does https://bmid-id.connective.eu/readout.html? :1st_place_medal:

roelderickx commented 2 years ago

I'll give the new code a try. I saw the new code for the buttons, and in an earlier life I did tinker with Tk and now Tkinter a little: you may be able to reduce the GUI code a lot.

Many thanks for having a look at the code. I am not a front-end developer and was already happy I got an acceptable result, but at the same time I wondered if it could be done more efficient. I created issue #5 for this, it is less urgent but a good excercise in Tkinter.

All tests succeed, as does https://bmid-id.connective.eu/readout.html :1st_place_medal:

Ok, then it probably works for all cardreaders and all current belgian id-cards. Thanks again for your bug report, the testing and the code review, it was really valuable :+1: