added life cycle state
[perl-Mifare-MAD.git] / mifare-mad.pl
1 #!/usr/bin/perl
2
3 use warnings;
4 use strict;
5
6 # based on AN10787
7 # MIFARE Application Directory (MAD)
8 # http://www.nxp.com/acrobat_download2/other/identification/MAD_overview.pdf
9
10 use Data::Dump qw(dump);
11
12 my $debug = $ENV{DEBUG} || 0;
13
14 my $function_clusters;
15 my $mad_id;
16
17 while(<DATA>) {
18         chomp;
19         next if m/^#?\s*$/;
20         my ( $code, $function ) = split(/\s+/,$_,2);
21         my $h = '[0-9A-F]';
22         if ( $code =~ m/^($h{2})-($h{2})$/ ) {
23                 foreach my $c ( hex($1) .. hex($2) ) {
24                         $function_clusters->{ unpack('HH',$c) } = $function;
25                 }
26         } elsif ( $code =~ m/^($h{2})$/ ) {
27                 $function_clusters->{ lc $code } = $function;
28         } elsif ( $code =~ m/^($h{4})$/ ) {
29                 $mad_id->{ lc $1 } = $function;
30         } else {
31                 die "can't parse __DATA__ line $.\n$_\n";
32         }
33 }
34
35 my $access_condition_data = {
36 0b000 => 'R:AB W:AB I:AB DTR:AB transport conf',
37 0b010 => 'R:AB W:-- I:-- DTR:-- r/w block',
38 0b100 => 'R:AB W:-B I:-- DTR:-- r/w block', 
39 0b110 => 'R:AB W:-B I:-B DTR:AB r/w block',
40 0b001 => 'R:AB W:-- I:-- DTR:AB value block',
41 0b011 => 'R:-B W:-B I:-- DTR:-- value block',
42 0b101 => 'R:-B W:-- I:-- DTR:-- r/w block',
43 0b111 => 'R:-- W:-- I:-- DTR:-- r/w block',
44 };
45
46 my $access_condition_trailer = {
47 0b000 => 'R/W: KEYA:-/A ACCESS:A-/- DATB:A/A ?',
48 0b010 => 'R/W: KEYA:-/- ACCESS:A-/- DATB:A/- ?'.
49 0b100 => 'R/W: KEYA:-/B ACCESS:AB/- KEYB:-/B',
50 0b110 => 'R/W: KEYA:-/- ACCESS:AB/- KEYB:-/-',
51 0b001 => 'R/W: KEYA:-/A ACCESS:A-/A DATB:A/A ? transport conf',
52 0b011 => 'R/W: KEYA:-/B ACCESS:AB/B KEYB:-/B',
53 0b101 => 'R/W: KEYA:-/- ACCESS:AB/B KEYB:-/-',
54 0b111 => 'R/W: KEYA:-/- ACCESS:AB/- KEYB:-/-',
55 };
56
57 my $life_cycle = {
58 "\x78\x77\x88" => 'MAD INIT,RW',
59 "\x7F\x07\x88" => 'NFC INIT,RW',
60 "\x07\x8F\x0F" => 'READ ONLY',
61 };
62
63
64 if ( $debug ) {
65         warn "# function_clusters ",dump($function_clusters);
66         warn "# mad_id ", dump($mad_id);
67 }
68
69 local $/ = undef;
70 my $card = <>;
71
72 die "expected 4096 bytes, got ",length($card), " bytes\n"
73         unless length $card == 4096;
74
75 my ( $ADV, $MA, $DA );
76
77 my $pos = 0;
78
79 foreach my $sector ( 0 .. 39 ) {
80
81         my $blocks = $sector < 32 ? 4 : 16;
82
83         next if substr($card,$pos,$blocks * 0x10) eq "\x00" x ($blocks * 0x10);
84
85         # General purpose byte (GPB)
86         my $GBP = ord(substr($card,0x39,1));
87
88         if ( $sector == 0 ) {
89                 printf "manufacturer block\nSerial number: %s\nCB: %s\nmanufacturer data: %s\n"
90                         , unpack('H*',substr($card,0,4))
91                         , unpack('H*',substr($card,4,1))
92                         , unpack('H*',substr($card,5,11))
93                         ;
94
95                 # MAD
96                 $ADV = $GBP & 0b00000011;
97                 $MA  = $GBP & 0b01000000;
98                 $DA  = $GBP & 0b10000000;
99                 printf "ADV (MAD version code): %d %s\n", $ADV;
100                 printf "MA (multiapplication): %s\n", $MA ? 'yes' : 'monoaplication';
101                 printf "DA (MAD available): %s%s\n",  $DA ? 'yes' : 'no',
102                         substr($card,$pos+0x30,6) eq "\xA0\xA1\xA2\xA3\xA4\xA5" ? ' public' : '';
103
104                 printf "Info byte (publisher sector): %x\n", ord(substr($card,0x11,1));
105         } elsif ( $DA ) {
106                 my $mad_offset = 0x10 + ( $sector * 2 );
107                 my $v = unpack('v',(substr($card, $mad_offset, 2)));
108                 my $cluster_id = unpack('HH', (( $v & 0xff00 ) >> 8) );
109                 my $full_id = sprintf "%04x",$v;
110                 printf "MAD sector %-2d@%x %04x [%s]\n%s\n", $sector, $mad_offset, $v
111                         , $function_clusters->{ $cluster_id }
112                         , $mad_id->{$full_id} || "FIXME: add $full_id from MAD_overview.pdf to __DATA__ at end of $0"
113                         ;
114
115                 if ( $v == 0x0004 ) {
116                         # RLE encoded card holder information
117                         my $data = substr( $card, $pos, 0x30);
118                         my $o = 0;
119                         my $types = {
120                                 0b00 => 'surname',
121                                 0b01 => 'given name',
122                                 0b10 => 'sex',
123                                 0b11 => 'any',
124                         };
125                         while ( substr($data,$o,1) ne "\x00" ) {
126                                 my $len = ord(substr($data,$o,1));
127                                 my $type = ( $len & 0b11000000 ) >> 6;
128                                 $len     =   $len & 0b00111111;
129                                 my $dump = substr($data,$o+1,$len-1);
130                                 $dump = '0x' . unpack('H*', $dump) if $type == 0b11; # any
131                                 printf "%-10s %2d %s\n", $types->{$type}, $len, $dump;
132                                 $o += $len + 1;
133                         }
134                 } elsif ( $v == 0x0015 ) {
135                         printf "Card number: %s\n", unpack('h*',substr($card,$pos + 0x04,6));
136                 }
137
138         } else {
139                 printf "# sector %-2d with %d blocks\n", $sector, $blocks;
140         }
141
142         my $trailer_pos = $pos + $blocks * 0x10 - 0x10;
143         my $c1 = ( ord(substr($card,$trailer_pos+7,1)) & 0xf0 ) >> 4;
144         my $c2 = ( ord(substr($card,$trailer_pos+8,1)) & 0x0f );
145         my $c3 = ( ord(substr($card,$trailer_pos+8,1)) & 0xf0 ) >> 4;
146
147         printf "# trailer @%x c1:%d c2:%d c3:%d [%016b]\n"
148                 , $trailer_pos, $c1, $c2, $c3
149                 , unpack('n',(substr($card,$trailer_pos+7,2)))
150                 ;
151
152         foreach my $j ( 0 .. $blocks - 1 ) {
153                 my $offset = $pos + $j * 0x10;
154                 my $block = substr($card, $offset, 0x10);
155                 my $mask = 1 << $j;
156                 my $cond
157                         = ( ( $c1 & $mask ) * 4 )
158                         + ( ( $c2 & $mask ) * 2 )
159                         + ( ( $c3 & $mask ) * 1 )
160                         ;
161                 $cond >>= $j;
162
163                 my $hex = unpack('H*',$block);
164                 $hex =~ s/(....)/$1 /g;
165
166                 if ( $ENV{SWAP} ) {
167                         my $hex_sw = unpack('h*',$block);
168                         $hex_sw =~ s/(....)/$1 /g;
169                         $hex .= " | $hex_sw";
170                 }
171
172                 printf "%04x  %s %03b %s\n", $offset, $hex
173                         , $cond
174                         , $j < 3 ? $access_condition_data->{$cond} : $access_condition_trailer->{$cond}
175                         ;
176         }
177
178         printf "KEY A:%s | %s GDP: %s | B:%s %s\n"
179                 ,unpack('H*',substr($card,$trailer_pos   ,6))
180                 ,unpack('H*',substr($card,$trailer_pos+6 ,3))
181                 ,unpack('H*',substr($card,$trailer_pos+9 ,1))
182                 ,unpack('H*',substr($card,$trailer_pos+10,6))
183                 ,$life_cycle->{substr($card,$trailer_pos+6,3)} || ''
184                 ;
185
186         print "\n";
187
188         $pos += $blocks * 0x10;
189 }
190
191 __DATA__
192 00    card administration
193 01-07 miscellaneous applications
194 08    airlines
195 09    ferry trafic
196 10    railway services
197 12    transport
198 18    city traffic
199 19    Czech Railways
200 20    bus services
201 21    multi modal transit
202 28    taxi
203 30    road toll
204 38    company services
205 40    city card services
206 47-48 access control & security
207 49    VIGIK
208 4A    Ministry of Defence, Netherlands
209 4B    Bosch Telecom, Germany
210 4A    Ministry of Defence, Netherlands
211 4C    European Union Institutions
212 50    ski ticketing
213 51-54 access control & security
214 58    academic services
215 60    food
216 68    non food trade
217 70    hotel
218 75    airport services
219 78    car rental
220 79    Dutch government
221 80    administration services
222 88    electronic purse
223 90    television
224 91    cruise ship
225 95    IOPTA
226 97    Metering
227 98    telephone
228 A0    health services
229 A8    warehouse
230 B0    electronic trade
231 B8    banking
232 C0    entertainment & sports
233 C8    car parking
234 C9    Fleet Management
235 D0    fuel, gasoline
236 D8    info services
237 E0    press
238 E1    NFC Forum
239 E8    computer
240 F0    mail
241 F8-FF miscellaneous applications
242
243 0000    sector is free
244 0001    sector is defect, e.g. access keys are destroyed or unknown
245 0002    sector is reserved
246 0003    sector contains additional directory info (useful only for future cards)
247 0004    sector contains card holder information in ASCII format.
248 0005    sector not applicable (above memory size)
249
250 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
251 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope    1 10.10.2007 Issuer information
252
253 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe       1 01.12.2008 Customer profile
254 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
255 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
256
257 1835 - city traffic KORID LK, spol.s.r.o.       KORID LK, spol.s.r.o.        Europe         2 08.09.2008 Eticket
258 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe         1 01.12.2008 Prepaid coupon 1S
259 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o.   EUROPE,Croatia 1 01.04.2009 Prepaid coupon
260 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o.  Europe         1 01.05.2009 Prepaid coupon
261 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o  EUROPE,Czech R 1 01.08.2009 Prepaid coupon
262 183B - city traffic UNICARD S.A.                UNICARD S.A.                 Poland        15 01.01.2010 city traffic services
263
264 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe     1 01.08.2008 Electronic ticket
265 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
266 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe       3 01.05.2009 electronic ticket
267
268 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia  4 01.04.2009 Electronic purse
269 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe         4 01.05.2009 electronic purse
270 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse
271