Proxmark developers community

Research, development and trades concerning the powerful Proxmark3 device.

Remember; sharing is caring. Bring something back to the community.


"Learn the tools of the trade the hard way." +Fravia

You are not logged in.

#1 2018-01-12 20:12:19

altamic
Contributor
Registered: 2018-01-12
Posts: 7

Italian city public transportation system (MF0ICU1)

In the city where I live, the local public transport company sells NFC carnets of 5 or 15 rides in the form of 14443-A NFC Forum Type 2 tags.
In the past they were Mifare Ultralight (MF0ICU1) but recently they use Infineon my-move lean (SLE66R01L).

I have collected something like 850 dumps of sequential obliterations and I have gathered most of the carnet structure/semantic [1].

The missing piece is how the last 2 bytes are computed [2]. Despite the relatively abundant data, I still have not found a correlation/mapping between the ticket data and these 16 bits –that I have improperly called "checksum".

Would you like to help me in my research?

P.S.: the corpus contains a Ruby script used to convert a structured YAML file into a sequence of ASCII hex dump files named conveniently[3].


[1]: carnet structure

0KXXYYZZ    0x88 ⊕ 0x0K ⊕ 0xXX ⊕ 0xYY = 0xZZ
GGHHIIJJ    UID: 0x0KXXYYGGHHIIJJ (represented in decimal in the receipt)
LL          0xLL = 0xGG ⊕ 0xHH ⊕ 0xII ⊕ 0xJJ
  KK        internal: 0xKK related to chip vendor identifier
    F2      Lock byte #1:
                         0xF:         1            1               1              1
                       meaning:   Page7locked, Page6locked,   Page5locked, Page4locked

                         0x2:         0            0               1              0
                       meaning:  OTPlocked, Pages10to15locked, Pages4to9locked, OTPlocked

      03    Lock byte #2:
                         0x0:         0            0               0              0
                       meaning:  Page15locked, Page14locked, Page13locked, Page12locked

                         0x3:         0            0               1              1
                       meaning:  Page11locked, Page10locked,  Page9locked,  Page8locked

MMNNOOPP    OTP: remaining rides (zero bits population in OOPP)
01LL0000    Layout -> Carnet rides
QQ01RRRR    Mask, Tariff (commercial info)
TTTTTT00    Purchase date in minutes since epoch (2005-01-01)
00######
##00%%%%    Purchase serial number (found in the receipt, represented as ########-%%%% where %%%% in decimal)
CCCCCCCC    Locked constant (maybe random)
TTTTTT00    First obliteration timestamp in minutes since epoch
04F80000    Zone
TTTTTT00    Last obliteration timestamp within 90' of validity period
PPPPPPPP    Bus line / Metro stop
F8AEHHHH    Zone, Obliterator Id part 1
HH12????    Obliterator Id part 2, constant, "checksum"

Lock bytes are F2:03 then only the last 6 pages are effectively changeable after purchase.

[2] Are they a function of UID, Locked constant, Last timestamp and/or who knows what?
I have also found some collisions: 2869, 4F11, D7EE, E4BB.

[3] dump file name structure:

              +-- rides subtracted from total
              |
              v
c15_03_minus_12M_+44.txt
  ^  ^         ^  ^
  |  |         |  +-- next obliteration within 90' of validity period (44' in this case)
  |  |         + -- M stands for a Metro entrance (it's a Bus line when not present)
  |  +-- sequential hex id for carnet
  +-- total rides

Last edited by altamic (2018-01-14 02:27:44)

Offline

#2 2018-01-13 12:39:48

iceman
Administrator
Registered: 2013-04-25
Posts: 4,880
Website

Re: Italian city public transportation system (MF0ICU1)

Someone has done his homework well. I'll take a look at it later.


冰人

modhex(hkhehghthbhudcfcdchkigiehgduiehg)

Offline

#3 2018-01-13 14:30:07

altamic
Contributor
Registered: 2018-01-12
Posts: 7

Re: Italian city public transportation system (MF0ICU1)

iceman wrote:

Someone has done his homework well. I'll take a look at it later.

Wow!! iceman!! I am honoured that you stop and look at my puzzle smile

Offline

#4 2018-01-13 15:56:57

altamic
Contributor
Registered: 2018-01-12
Posts: 7

Re: Italian city public transportation system (MF0ICU1)

I noticed that the YAML source has annoying quotes for some values.
You can remove them with:

$ sed -i "s/'//g" carnets.yaml # Linux

or

$ sed -i '' -e "s/'//g" carnets.yaml # MacOS/BSD

Last edited by altamic (2018-01-13 16:31:48)

Offline

#5 2018-01-13 18:14:25

altamic
Contributor
Registered: 2018-01-12
Posts: 7

Re: Italian city public transportation system (MF0ICU1)

The YAML file contains the carnet structure that I have represented in the first post.

In particular, I have used the following names for various fields:

uid
cb0
cb1
internal
lock
otp
layout
mask
tariff
purchase_date
serial_number
unknown
first_validation
constant_1
zone_1
last_validation
line
zone_2
constant_2
obliterator
constant_3
checksum

Such structure can be manipulated easily with unix commands.

For example, to have a list of distinct sorted UIDs you just fire up:

$ grep uid carnets.yaml | cut -d ' ' -f 4 | sort -u
0416E1CA004980
041CBB32882881
04208E5A753381
042C5002323680
043DA2CA004981
0462015A753384
0468252A3C3C84
...

or to find collisions in "checksum", you can run the longer:

$ grep checksum carnets.yaml | cut -d ' ' -f 4 | sort | uniq -c | grep ' 2 ' | cut -d ' ' -f 5
2869
4F11
BACC
D7EE
E4BB

Suppose you would like to find a conventional date for a given date/timestamp found in a ticket, say 63A3B0.
You can find it as follows:

$ date -d @$((16#63A3B0 * 60 + 1104534000)) # Linux
Thu Jun  1 17:48:00 CEST 2017

or

$ date -r $((16#63A3B0 * 60 + 1104534000)) # MacOs/BSD
Thu Jun  1 17:48:00 CEST 2017

Last edited by altamic (2018-01-14 12:57:28)

Offline

#6 2018-01-21 12:33:54

altamic
Contributor
Registered: 2018-01-12
Posts: 7

Re: Italian city public transportation system (MF0ICU1)

The problem with my puzzle is that we do not know the algorithm nor the inputs of what produces what I called "checksum" as output.

If the algorithm depends on some non trivial secret salt shared by all obliterators the problem would be infeasible.

We can only hope that the algorithm is a function of some fields of the carnet stucture, exclusively.

For a given a carnet c_0 obliterated at the minute t resulting in the carnet c_1 we have that the possible inputs are:

fields(c_0) ∪ fields(c_1) / {checksum_c1} =
((fields(c_0) ∩ fields(c_1)) ∪ (fields(c_0) / (fields(c_0) ∩ fields(c_1)) ∪ (fields(c_1) / (fields(c_0) ∩ fields(c_1)) / {checksum_c1}
= { uid, cb0, cb1, internal, lock, layout, mask, tariff, purchase_date, serial_number, unknown, constant_3 } ∪ { otp_c0, first_validation_c0, constant_1_c0, zone_1_c0, line_c0, last_validation_c0, zone_2_c0, constant_2_c0, obliterator_c0, checksum_c0 } ∪ { otp_c1, first_validation_c1, constant_1_c1, zone_1_c1, line_c1, last_validation_c1, zone_2_c1, constant_2_c1, obliterator_c1 }

totaling in 31 possible inputs.

In order to maximize identical inputs we should minimize differences between carnets c0 and c1. Hence we will consider only those that have been obliterated in the same line and obliterator within 90' (a ticket ride maximum duration) so that for c0 and c1 we basically restrict inputs to the set:

{ last_validation_c0, last_validation_c1, checksum_c0 }

Such tickets are:

c15_01_minus_13.txt
c15_01_minus_13_+5.txt
c15_01_minus_13_+51.txt

048A828462753380A448F2031FFFFFFC01050000020102BD59C2200000AE10A6B80044F3705BE1355A02EA0004F80000 5A02EA 00003C0004F8AE10795C12 9EB3
048A828462753380A448F2031FFFFFFC01050000020102BD59C2200000AE10A6B80044F3705BE1355A02EA0004F80000 5A02EF 00003C0004F8AE10795C12 25F9
048A828462753380A448F2031FFFFFFC01050000020102BD59C2200000AE10A6B80044F3705BE1355A02EA0004F80000 5A031D 00003C0004F8AE10795C12 CEDC

c15_17_minus_01.txt
c15_17_minus_01_+8.txt

0570F60B515754E9BB15F2030001C00001050000020102BD5EF1A00000AE10FE2000F0C981CE10825F06B80004F80000 5F06B8 00003C0004F8AE1078C912 CB5F
0570F60B515754E9BB15F2030001C00001050000020102BD5EF1A00000AE10FE2000F0C981CE10825F06B80004F80000 5F06C0 00003C0004F8AE1078C912 624E

With CRC RevEng I explore a possible CRC solution.

For f(last_validation_c0, checksum_c0, last_validation_c1) = checksum_c1

$ reveng -s -w 16 5A02EA9EB35A02EF25F9 5F06B8CB5F5F06C0624E
reveng: no models found

trying with f(last_validation_c0, checksum_c0) = checksum_c1

$ reveng -s -w 16 5A02EA9EB325F9 5F06B8CB5F624E 5A02EF25F9CEDC
reveng: no models found

we try also with f(last_validation_c0, last_validation_c1) = checksum_c1

$ reveng -s -w 16 5A02EA5A02EF25F9 5A02EF5A031DCEDC 5F06B85F06C0624E
reveng: no models found

and finally with f(last_validation_c0) = checksum_c1

$ reveng -s -w 16 5A02EA25F9 5A02EFCEDC 5F06B8624E
reveng: no models found

I would conclude that the "checksum" is not a CRC. Any idea/observation?

Last edited by altamic (2018-01-21 12:38:25)

Offline

#7 2018-02-15 19:02:35

altamic
Contributor
Registered: 2018-01-12
Posts: 7

Re: Italian city public transportation system (MF0ICU1)

It seems that this thread is somewhat stale and it does not cause any interest.

I will continue the research on my own with some Machine Learning techniques. First with some features engineering and then modeling the problem, while collecting more data.

Thank you anyway.

Offline

#8 2018-03-13 13:37:43

tristanik
Contributor
Registered: 2014-11-25
Posts: 82

Re: Italian city public transportation system (MF0ICU1)

Altamic,could you give me your email? I would like to share some information

Offline

Board footer

Powered by FluxBB