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