SWAP=1 to dump low nybble first
[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                 if ( $ENV{SWAP} && $j < 3 ) {
140                         my $hex_sw = unpack('h*',$block);
141                         $hex_sw =~ s/(....)/$1 /g;
142                         $hex .= " | $hex_sw";
143                 }
144
145                 printf "%04x  %s %03b %s\n", $offset, $hex
146                         , $cond
147                         , $j < 3 ? $access_condition_data->{$cond} : $access_condition_trailer->{$cond}
148                         ;
149         }
150
151
152         printf "KEY A:%s | %s GDP: %s | B:%s\n"
153                 ,unpack('H*',substr($card,$pos+0x30   ,6))
154                 ,unpack('H*',substr($card,$pos+0x30+6 ,3))
155                 ,unpack('H*',substr($card,$pos+0x30+9 ,1))
156                 ,unpack('H*',substr($card,$pos+0x30+10,6))
157                 ;
158
159         print "\n";
160
161 }
162
163 __DATA__
164 00    card administration
165 01-07 miscellaneous applications
166 08    airlines
167 09    ferry trafic
168 10    railway services
169 12    transport
170 18    city traffic
171 19    Czech Railways
172 20    bus services
173 21    multi modal transit
174 28    taxi
175 30    road toll
176 38    company services
177 40    city card services
178 47-48 access control & security
179 49    VIGIK
180 4A    Ministry of Defence, Netherlands
181 4B    Bosch Telecom, Germany
182 4A    Ministry of Defence, Netherlands
183 4C    European Union Institutions
184 50    ski ticketing
185 51-54 access control & security
186 58    academic services
187 60    food
188 68    non food trade
189 70    hotel
190 75    airport services
191 78    car rental
192 79    Dutch government
193 80    administration services
194 88    electronic purse
195 90    television
196 91    cruise ship
197 95    IOPTA
198 97    Metering
199 98    telephone
200 A0    health services
201 A8    warehouse
202 B0    electronic trade
203 B8    banking
204 C0    entertainment & sports
205 C8    car parking
206 C9    Fleet Management
207 D0    fuel, gasoline
208 D8    info services
209 E0    press
210 E1    NFC Forum
211 E8    computer
212 F0    mail
213 F8-FF miscellaneous applications
214
215 0000    sector is free
216 0001    sector is defect, e.g. access keys are destroyed or unknown
217 0002    sector is reserved
218 0003    sector contains additional directory info (useful only for future cards)
219 0004    sector contains card holder information in ASCII format.
220 0005    sector not applicable (above memory size)
221
222 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
223 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope    1 10.10.2007 Issuer information
224
225 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe       1 01.12.2008 Customer profile
226 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
227 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
228
229 1835 - city traffic KORID LK, spol.s.r.o.       KORID LK, spol.s.r.o.        Europe         2 08.09.2008 Eticket
230 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe         1 01.12.2008 Prepaid coupon 1S
231 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o.   EUROPE,Croatia 1 01.04.2009 Prepaid coupon
232 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o.  Europe         1 01.05.2009 Prepaid coupon
233 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o  EUROPE,Czech R 1 01.08.2009 Prepaid coupon
234 183B - city traffic UNICARD S.A.                UNICARD S.A.                 Poland        15 01.01.2010 city traffic services
235
236 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe     1 01.08.2008 Electronic ticket
237 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
238 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe       3 01.05.2009 electronic ticket
239
240 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia  4 01.04.2009 Electronic purse
241 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe         4 01.05.2009 electronic purse
242 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse
243