unique dump names uid.md5.(mfd|mfoc.txt)
[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',
37 0b010 => 'R:AB W:-- I:-- DTR:--',
38 0b100 => 'R:AB W:-B I:-- DTR:--', 
39 0b110 => 'R:AB W:-B I:-B DTR:AB',
40 0b001 => 'R:AB W:-- I:-- DTR:AB',
41 0b011 => 'R:-B W:-B I:-- DTR:--',
42 0b101 => 'R:-B W:-- I:-- DTR:--',
43 0b111 => 'R:-- W:-- I:-- DTR:--',
44 };
45
46 my $access_condition_trailer = {
47 0b000 => 'R/W: KEYSECXA:-/A ACCESS COND:A-/- KEYSECXB:A/A',
48 0b010 => 'R/W: KEYSECXA:-/- ACCESS COND:A-/- KEYSECXB:A/-',
49 0b100 => 'R/W: KEYSECXA:-/B ACCESS COND:AB/- KEYSECXB:-/B',
50 0b110 => 'R/W: KEYSECXA:-/- ACCESS COND:AB/- KEYSECXB:-/-',
51 0b001 => 'R/W: KEYSECXA:-/A ACCESS COND:A-/A KEYSECXB:A/A',
52 0b011 => 'R/W: KEYSECXA:-/B ACCESS COND:AB/B KEYSECXB:-/B',
53 0b101 => 'R/W: KEYSECXA:-/- ACCESS COND:AB/B KEYSECXB:-/-',
54 0b111 => 'R/W: KEYSECXA:-/- ACCESS COND:AB/- KEYSECXB:-/-',
55 };
56
57
58 if ( $debug ) {
59         warn "# function_clusters ",dump($function_clusters);
60         warn "# mad_id ", dump($mad_id);
61 }
62
63 local $/ = undef;
64 my $card = <>;
65
66 die "expected 4096 bytes, got ",length($card), " bytes\n"
67         unless length $card == 4096;
68
69 foreach my $i ( 0 .. 15 ) {
70
71         my $pos = 0x40 * $i;
72
73         if ( $i == 0 ) {
74                 printf "manufacturer block\nSerial number: %s\nCB: %s\nmanufacturer data: %s\n"
75                         , unpack('H*',substr($card,0,4))
76                         , unpack('H*',substr($card,4,1))
77                         , unpack('H*',substr($card,5,11))
78                         ;
79                 # General purpose byte (GPB)
80                 my $gdp = ord(substr($card,0x39,1));
81                 printf "ADV (MAD version code): %d\n", $gdp & 0b00000011;
82                 printf "MA (multiapplication): %s\n",  $gdp & 0b01000000 ? 'yes' : 'monoaplication';
83                 printf "DA (MAD available): %s\n",     $gdp & 0b10000000 ? 'yes' : 'no';
84
85                 printf "Info byte (publisher sector): %x\n", ord(substr($card,0x11,1));
86         } else {
87                 my $mad_offset = 0x10 + ( $i * 2 );
88                 my $v = unpack('v',(substr($card, $mad_offset, 2)));
89                 my $cluster_id = unpack('HH', (( $v & 0xff00 ) >> 8) );
90                 my $full_id = sprintf "%04x",$v;
91                 printf "MAD sector %-2d@%x %04x [%s]\n%s\n", $i, $mad_offset, $v
92                         , $function_clusters->{ $cluster_id }
93                         , $mad_id->{$full_id} || "FIXME: add $full_id from MAD_overview.pdf to __DATA__ at end of $0"
94                         ;
95
96                 if ( $v == 0x0004 ) {
97                         # RLE encoded card holder information
98                         my $data = substr( $card, $pos, 0x30);
99                         my $o = 0;
100                         my $types = {
101                                 0b00 => 'surname',
102                                 0b01 => 'given name',
103                                 0b10 => 'sex',
104                                 0b11 => 'any',
105                         };
106                         while ( substr($data,$o,1) ne "\x00" ) {
107                                 my $len = ord(substr($data,$o,1));
108                                 my $type = ( $len & 0b11000000 ) >> 6;
109                                 $len     =   $len & 0b00111111;
110                                 my $dump = substr($data,$o+1,$len-1);
111                                 $dump = '0x' . unpack('H*', $dump) if $type == 0b11; # any
112                                 printf "%-10s %2d %s\n", $types->{$type}, $len, $dump;
113                                 $o += $len + 1;
114                         }
115                 } elsif ( $v == 0x0015 ) {
116                         printf "Card number: %s\n", unpack('h*',substr($card,$pos + 0x04,6));
117                 }
118
119         }
120
121         my $c1 = ( ord(substr($card,$pos+0x37,1)) & 0xf0 ) >> 4;
122         my $c2 = ( ord(substr($card,$pos+0x38,1)) & 0x0f );
123         my $c3 = ( ord(substr($card,$pos+0x38,1)) & 0xf0 ) >> 4;
124
125         foreach my $j ( 0 .. 3 ) {
126                 my $offset = $pos + $j * 0x10;
127                 my $block = substr($card, $offset, 0x10);
128                 my $mask = 1 << $j;
129                 my $cond
130                         = ( ( $c1 & $mask ) * 4 )
131                         + ( ( $c2 & $mask ) * 2 )
132                         + ( ( $c3 & $mask ) * 1 )
133                         ;
134                 $cond >>= $j;
135
136                 my $hex = unpack('H*',$block);
137                 $hex =~ s/(....)/$1 /g;
138
139                 printf "%04x  %s %03b %s\n", $offset, $hex
140                         , $cond
141                         , $j < 3 ? $access_condition_data->{$cond} : $access_condition_trailer->{$cond}
142                         ;
143         }
144
145
146         printf "KEY A:%s | %s GDP: %s | B:%s\n"
147                 ,unpack('H*',substr($card,$pos+0x30   ,6))
148                 ,unpack('H*',substr($card,$pos+0x30+6 ,3))
149                 ,unpack('H*',substr($card,$pos+0x30+9 ,1))
150                 ,unpack('H*',substr($card,$pos+0x30+10,6))
151                 ;
152
153         print "\n";
154
155 }
156
157 __DATA__
158 00    card administration
159 01-07 miscellaneous applications
160 08    airlines
161 09    ferry trafic
162 10    railway services
163 12    transport
164 18    city traffic
165 19    Czech Railways
166 20    bus services
167 21    multi modal transit
168 28    taxi
169 30    road toll
170 38    company services
171 40    city card services
172 47-48 access control & security
173 49    VIGIK
174 4A    Ministry of Defence, Netherlands
175 4B    Bosch Telecom, Germany
176 4A    Ministry of Defence, Netherlands
177 4C    European Union Institutions
178 50    ski ticketing
179 51-54 access control & security
180 58    academic services
181 60    food
182 68    non food trade
183 70    hotel
184 75    airport services
185 78    car rental
186 79    Dutch government
187 80    administration services
188 88    electronic purse
189 90    television
190 91    cruise ship
191 95    IOPTA
192 97    Metering
193 98    telephone
194 A0    health services
195 A8    warehouse
196 B0    electronic trade
197 B8    banking
198 C0    entertainment & sports
199 C8    car parking
200 C9    Fleet Management
201 D0    fuel, gasoline
202 D8    info services
203 E0    press
204 E1    NFC Forum
205 E8    computer
206 F0    mail
207 F8-FF miscellaneous applications
208
209 0000    sector is free
210 0001    sector is defect, e.g. access keys are destroyed or unknown
211 0002    sector is reserved
212 0003    sector contains additional directory info (useful only for future cards)
213 0004    sector contains card holder information in ASCII format.
214 0005    sector not applicable (above memory size)
215
216 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
217 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope    1 10.10.2007 Issuer information
218
219 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe       1 01.12.2008 Customer profile
220 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
221 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
222
223 1835 - city traffic KORID LK, spol.s.r.o.       KORID LK, spol.s.r.o.        Europe         2 08.09.2008 Eticket
224 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe         1 01.12.2008 Prepaid coupon 1S
225 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o.   EUROPE,Croatia 1 01.04.2009 Prepaid coupon
226 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o.  Europe         1 01.05.2009 Prepaid coupon
227 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o  EUROPE,Czech R 1 01.08.2009 Prepaid coupon
228 183B - city traffic UNICARD S.A.                UNICARD S.A.                 Poland        15 01.01.2010 city traffic services
229
230 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe     1 01.08.2008 Electronic ticket
231 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
232 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe       3 01.05.2009 electronic ticket
233
234 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia  4 01.04.2009 Electronic purse
235 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe         4 01.05.2009 electronic purse
236 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse
237