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