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

How I can Desfire authenticate in two steps? #87

Open
crossmax opened this issue May 16, 2018 · 12 comments
Open

How I can Desfire authenticate in two steps? #87

crossmax opened this issue May 16, 2018 · 12 comments

Comments

@crossmax
Copy link

Hi.
I'm trying authenticate on mifare desfire EV1 with a key generating for a external device (SAM module).
For that, in my old code where I used native commands, I send 0x0A command to PICC, and I get a random number within response. With this number, the SAM generates the key, and I send 0xAF command and 16 bytes for the key to resume authentication with the PICC.
With libfreefare functions, I don't know how to authenticate in two steps.

The output debug using libfreefare is as follows but I need to have control of the two steps of authentication:

*** mifare_desfire_select_application ***
===> 0000   90 5a 00 00 03 00 00 01 00 
<=== 0000   91 00
PICC replied OPERATION_OK
*** authenticate ***
===> 0000   90 0a 00 00 01 02 00
<=== 0000   6c 62 96 21 59 07 b6 4e 91 af

### I need stop here and do something ###

*** authenticate ***
===> 0000   90 af 00 00 10 a3 54 d9 35 6f e3 3e 0d 68 a5 31
===> 0010   bb 81 40 4b f6 00
<=== 0000   91 ae
PICC replied AUTHENTICATION_ERROR

How I can do it? Thanks

@SloCompTech
Copy link
Contributor

Could you please provide source code for this section ?

@crossmax
Copy link
Author

crossmax commented May 21, 2018

Hi @SloCompTech
I'm testing with this:

MifareDESFireAID aid = mifare_desfire_aid_new(0x00010000);
res = mifare_desfire_select_application(tag, aid); cut_assert_success("mifare_desfire_select_application()");

uint8_t s, c;
res = mifare_desfire_get_key_settings(tag, &s, &c);
cut_assert_success("mifare_desfire_get__key_settings");

uint8_t v;
mifare_desfire_get_key_version(tag, 2, &v);
cut_assert_success("mifare_desfire_get_key_version");

//mifare_desfire_auto_authenticate(tag, 2);
MifareDESFireKey key1 = mifare_desfire_des_key_new_with_version(key_data_null);
res = mifare_desfire_authenticate(tag, 2, key1);
cut_assert_success("mifare_desfire_authenticate()");`

I think is not possible use libfreefare authenticate function with a external generate key. I'm going to modify the function to divide it into two phases. The only question I have is when should I make calls to mifare_cypher_blocks_chained function, I'm not sure if I call it every time I call to DESFIRE_TRANSCEIVE function.
Any suggestion is very welcome!

@SloCompTech
Copy link
Contributor

As far as I understand authentication with Mifare Desfire you need to authenticate with application key after you select app, additionaly you authenticate with PICC key before you create, delete app, so I assume authentication needs to be preformed once, but I'm not 100% positive.

@smortex
Copy link
Contributor

smortex commented May 22, 2018

@crossmax I think this is currently not supported but would be an interesting contribution (i have never worked with a SAM, all devices I had had no SAM)!

The authenticate() function does the full handshake as a single step:

static int
authenticate(FreefareTag tag, uint8_t cmd, uint8_t key_no, MifareDESFireKey key)
{
ASSERT_ACTIVE(tag);
memset(MIFARE_DESFIRE(tag)->ivect, 0, MAX_CRYPTO_BLOCK_SIZE);
MIFARE_DESFIRE(tag)->authenticated_key_no = NOT_YET_AUTHENTICATED;
free(MIFARE_DESFIRE(tag)->session_key);
MIFARE_DESFIRE(tag)->session_key = NULL;
MIFARE_DESFIRE(tag)->authentication_scheme = (AUTHENTICATE_LEGACY == cmd) ? AS_LEGACY : AS_NEW;
BUFFER_INIT(cmd1, 2);
BUFFER_INIT(res, 17);
BUFFER_APPEND(cmd1, cmd);
BUFFER_APPEND(cmd1, key_no);
DESFIRE_TRANSCEIVE(tag, cmd1, res);
size_t key_length = __res_n - 1;
uint8_t PICC_E_RndB[16];
memcpy(PICC_E_RndB, res, key_length);
uint8_t PICC_RndB[16];
memcpy(PICC_RndB, PICC_E_RndB, key_length);
mifare_cypher_blocks_chained(tag, key, MIFARE_DESFIRE(tag)->ivect, PICC_RndB, key_length, MCD_RECEIVE, MCO_DECYPHER);
uint8_t PCD_RndA[16];
RAND_bytes(PCD_RndA, 16);
uint8_t PCD_r_RndB[16];
memcpy(PCD_r_RndB, PICC_RndB, key_length);
rol(PCD_r_RndB, key_length);
uint8_t token[32];
memcpy(token, PCD_RndA, key_length);
memcpy(token + key_length, PCD_r_RndB, key_length);
mifare_cypher_blocks_chained(tag, key, MIFARE_DESFIRE(tag)->ivect, token, 2 * key_length, MCD_SEND, (AUTHENTICATE_LEGACY == cmd) ? MCO_DECYPHER : MCO_ENCYPHER);
BUFFER_INIT(cmd2, 33);
BUFFER_APPEND(cmd2, 0xAF);
BUFFER_APPEND_BYTES(cmd2, token, 2 * key_length);
DESFIRE_TRANSCEIVE(tag, cmd2, res);
uint8_t PICC_E_RndA_s[16];
memcpy(PICC_E_RndA_s, res, key_length);
uint8_t PICC_RndA_s[16];
memcpy(PICC_RndA_s, PICC_E_RndA_s, key_length);
mifare_cypher_blocks_chained(tag, key, MIFARE_DESFIRE(tag)->ivect, PICC_RndA_s, key_length, MCD_RECEIVE, MCO_DECYPHER);
uint8_t PCD_RndA_s[key_length];
memcpy(PCD_RndA_s, PCD_RndA, key_length);
rol(PCD_RndA_s, key_length);
if (0 != memcmp(PCD_RndA_s, PICC_RndA_s, key_length)) {
#ifdef WITH_DEBUG
hexdump(PCD_RndA_s, key_length, "PCD ", 0);
hexdump(PICC_RndA_s, key_length, "PICC ", 0);
#endif
errno = EACCES;
return -1;
}
MIFARE_DESFIRE(tag)->authenticated_key_no = key_no;
MIFARE_DESFIRE(tag)->session_key = mifare_desfire_session_key_new(PCD_RndA, PICC_RndB, key);
memset(MIFARE_DESFIRE(tag)->ivect, 0, MAX_CRYPTO_BLOCK_SIZE);
switch (MIFARE_DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
break;
case AS_NEW:
cmac_generate_subkeys(MIFARE_DESFIRE(tag)->session_key);
break;
}
return 0;
}

I think that if you split this function into smaller pieces, a lot of code could be shared between authenticating with a MifrareDESFireKey and against a SAM module. Before starting this, you may want to be sure that if the communication is fully maced / encrypted, not every command has to pass through the SAM… If it is the case, I guess that updating the ugly DESFIRE_TRANSCEIVE* macro will be required, and last time I tried to convert them to functions, I failed to do so (that leads to a lot more changes than what I expected).

@crossmax
Copy link
Author

@smortex I was doing what you said and two steps authentication works nice.
Wherever, I got involved in this modification because the Desfire reading times are too slow and i don't know how I to improve it.
For read a 144 bytes Desfire file my hardware takes about 100ms, that is 11kbps and the time is same if I use NBR_106 or NBR_847 baud rate.

Are this times normal? With other hardware and the same Desfire cards but using NXP native commands I was getting times almost twice as fast.
Can it be due to apdu encapsulation? Or be due to poor quality reader??
Any suggestion to improve reading times is welcome!!!

@SloCompTech
Copy link
Contributor

@crossmax which bus are you using ? I2C ? SPI ? UART ?

@crossmax
Copy link
Author

@SloCompTech I'm using SPI

@SloCompTech
Copy link
Contributor

In connection string you can set speed, pn532_spi:/dev/spidev0.0:1000000 is working for me, speed is set after : in bps.

@crossmax
Copy link
Author

Yes. I checked the settings first of all. Up to 3,5MHz works fine but reading speed is still slow

@smortex
Copy link
Contributor

smortex commented May 29, 2018

Hi!

For debugging timing problems, I usually end-up using DTrace (I mainly run FreeBSD). If you are running on GNU/Linux, I guess that looking into this (super-complete, by some NetFlix staff) or this (same tools, in a TL;DR fashion) can help?

This should help you to measure which step is slow… processing data, sending it to the device, reading, etc.

@crossmax
Copy link
Author

Hi,
I've never used DTrace, but I'm testing with libfreefare's debug mode and linux timestamp (timeval). Will I get more information if I use DTrace?
These are the traces where you can see the time it takes to read the file:

*** mifare_desfire_select_application ***
===> 0000   90 5a 00 00 03 00 00 01 00
<=== 0000   91 00
PICC replied OPERATION_OK
Time elapsed after the SelectApp 0.054548s

*** authenticate_step1 ***
===> 0000   90 0a 00 00 01 02 00
<=== 0000   84 79 5f 8f cc b5 d9 f7 91 af

*** negotiation with sam ***

*** authenticate_step2 ***
===> 0000   90 af 00 00 10 10 be de d5 e6 7f 45 91 19 1e 33
===> 0010   da 5f 91 94 0a 00
<=== 0000   74 22 f0 69 33 19 12 67 91 00
PCD  0000   bd ce 76 e8 05 00 00 50
PICC 0000   b5 1e f1 c8 0b 93 71 e1
PICC replied OPERATION_OK
Time elapsed after authentication 0.137789s

*** mifare_desfire_get_file_settings ***
===> 0000   90 f5 00 00 01 00 00
<=== 0000   01 03 40 24 8d 00 00 91 00
*** read_data ***
===> 0000   90 bd 00 00 07 00 00 00 00 00 00 00 00
<=== 0000   09 15 22 e7 55 59 43 5a 0a 89 67 8c fb 93 cb c8
<=== 0010   fe a0 f5 7e d0 e8 6d 75 c2 c3 a9 0d c7 fe 4d 96
<=== 0020   6f 2d 61 2c 7e 49 7b 4e 53 06 98 19 13 16 af cc
<=== 0030   a5 af b7 2c 33 4f 74 7c 91 af
*** read_data ***
===> 0000   90 af 00 00 00
<=== 0000   bf 71 d7 64 d7 24 a7 ce a4 1a ff 94 69 21 68 58
<=== 0010   51 5e 45 3e ff ab ee 3e f7 f5 c9 22 00 d3 6c 2e
<=== 0020   cc b6 89 bc f3 4b 45 e3 c9 55 43 86 ec af 8c 96
<=== 0030   61 fd fb 45 a4 67 8f a4 91 af
*** read_data ***
===> 0000   90 af 00 00 00
<=== 0000   aa 7f b1 50 a4 b8 1f 61 20 35 77 0d 39 7b 0c 14
<=== 0010   9a 10 43 fc 92 d6 70 e1 11 57 97 fd 0a bc 6a 31
<=== 0020   91 00
PICC replied `OPERATION_OK
Time elapsed after read file (144bytes) 0.102637s

According to these traces everything is fine.
Could someone tell me how long it takes to read about 144 bytes of a Desfire EV1?

===> Version information for tag 047XXXXXXXXXXX:
UID:                      0x047XXXXXXXXXXX
Batch number:             0xba4450e390
Production date:          week 11, 2013
Hardware Information:
    Vendor ID:            0x04
    Type:                 0x01
    Subtype:              0x01
    Version:              1.0
    Storage size:         0x18 (=4096 bytes)
    Protocol:             0x05
Software Information:
    Vendor ID:            0x04
    Type:                 0x01
    Subtype:              0x01
    Version:              1.4
    Storage size:         0x18 (=4096 bytes)
    Protocol:             0x05

@smortex
Copy link
Contributor

smortex commented Jun 7, 2018

DTrace will not be available on Linux, but with gperf you should be able to gather information about where time is spent (e.g. your application; libfreefare; libnfc; or the kernel itself).

Once you have measured which part is slow, you will know where to see if it's possible to improve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants