7 # MIFARE Application Directory (MAD)
8 # http://www.nxp.com/acrobat_download2/other/identification/MAD_overview.pdf
10 use Data::Dump qw(dump);
12 my $debug = $ENV{DEBUG} || 0;
14 my $function_clusters;
20 my ( $code, $function ) = split(/\s+/,$_,2);
22 if ( $code =~ m/^($h{2})-($h{2})$/ ) {
23 foreach my $c ( hex($1) .. hex($2) ) {
24 $function_clusters->{ unpack('HH',$c) } = $function;
26 } elsif ( $code =~ m/^($h{2})$/ ) {
27 $function_clusters->{ lc $code } = $function;
28 } elsif ( $code =~ m/^($h{4})$/ ) {
29 $mad_id->{ lc $1 } = $function;
31 die "can't parse __DATA__ line $.\n$_\n";
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',
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:-/-',
59 warn "# function_clusters ",dump($function_clusters);
60 warn "# mad_id ", dump($mad_id);
66 die "expected 4096 bytes, got ",length($card), " bytes\n"
67 unless length $card == 4096;
69 my ( $ADV, $MA, $DA );
73 foreach my $sector ( 0 .. 39 ) {
75 my $blocks = $sector < 32 ? 4 : 16;
77 next if substr($card,$pos,$blocks * 0x10) eq "\x00" x ($blocks * 0x10);
79 # General purpose byte (GPB)
80 my $GBP = ord(substr($card,0x39,1));
83 printf "manufacturer block\nSerial number: %s\nCB: %s\nmanufacturer data: %s\n"
84 , unpack('H*',substr($card,0,4))
85 , unpack('H*',substr($card,4,1))
86 , unpack('H*',substr($card,5,11))
90 $ADV = $GBP & 0b00000011;
91 $MA = $GBP & 0b01000000;
92 $DA = $GBP & 0b10000000;
93 printf "ADV (MAD version code): %d %s\n", $ADV;
94 printf "MA (multiapplication): %s\n", $MA ? 'yes' : 'monoaplication';
95 printf "DA (MAD available): %s%s\n", $DA ? 'yes' : 'no',
96 substr($card,$pos+0x30,6) eq "\xA0\xA1\xA2\xA3\xA4\xA5" ? ' public' : '';
98 printf "Info byte (publisher sector): %x\n", ord(substr($card,0x11,1));
100 my $mad_offset = 0x10 + ( $sector * 2 );
101 my $v = unpack('v',(substr($card, $mad_offset, 2)));
102 my $cluster_id = unpack('HH', (( $v & 0xff00 ) >> 8) );
103 my $full_id = sprintf "%04x",$v;
104 printf "MAD sector %-2d@%x %04x [%s]\n%s\n", $sector, $mad_offset, $v
105 , $function_clusters->{ $cluster_id }
106 , $mad_id->{$full_id} || "FIXME: add $full_id from MAD_overview.pdf to __DATA__ at end of $0"
109 if ( $v == 0x0004 ) {
110 # RLE encoded card holder information
111 my $data = substr( $card, $pos, 0x30);
115 0b01 => 'given name',
119 while ( substr($data,$o,1) ne "\x00" ) {
120 my $len = ord(substr($data,$o,1));
121 my $type = ( $len & 0b11000000 ) >> 6;
122 $len = $len & 0b00111111;
123 my $dump = substr($data,$o+1,$len-1);
124 $dump = '0x' . unpack('H*', $dump) if $type == 0b11; # any
125 printf "%-10s %2d %s\n", $types->{$type}, $len, $dump;
128 } elsif ( $v == 0x0015 ) {
129 printf "Card number: %s\n", unpack('h*',substr($card,$pos + 0x04,6));
133 printf "# sector %-2d with %d blocks\n", $sector, $blocks;
136 my $trailer_pos = $pos + $blocks * 0x10 - 0x10;
137 my $c1 = ( ord(substr($card,$trailer_pos+7,1)) & 0xf0 ) >> 4;
138 my $c2 = ( ord(substr($card,$trailer_pos+8,1)) & 0x0f );
139 my $c3 = ( ord(substr($card,$trailer_pos+8,1)) & 0xf0 ) >> 4;
141 printf "# trailer @%x c1:%d c2:%d c3:%d [%016b]\n"
142 , $trailer_pos, $c1, $c2, $c3
143 , unpack('n',(substr($card,$trailer_pos+7,2)))
146 foreach my $j ( 0 .. $blocks - 1 ) {
147 my $offset = $pos + $j * 0x10;
148 my $block = substr($card, $offset, 0x10);
151 = ( ( $c1 & $mask ) * 4 )
152 + ( ( $c2 & $mask ) * 2 )
153 + ( ( $c3 & $mask ) * 1 )
157 my $hex = unpack('H*',$block);
158 $hex =~ s/(....)/$1 /g;
160 if ( $ENV{SWAP} && $j < 3 ) {
161 my $hex_sw = unpack('h*',$block);
162 $hex_sw =~ s/(....)/$1 /g;
163 $hex .= " | $hex_sw";
166 printf "%04x %s %03b %s\n", $offset, $hex
168 , $j < 3 ? $access_condition_data->{$cond} : $access_condition_trailer->{$cond}
173 printf "KEY A:%s | %s GDP: %s | B:%s\n"
174 ,unpack('H*',substr($card,$pos+0x30 ,6))
175 ,unpack('H*',substr($card,$pos+0x30+6 ,3))
176 ,unpack('H*',substr($card,$pos+0x30+9 ,1))
177 ,unpack('H*',substr($card,$pos+0x30+10,6))
182 $pos += $blocks * 0x10;
186 00 card administration
187 01-07 miscellaneous applications
195 21 multi modal transit
199 40 city card services
200 47-48 access control & security
202 4A Ministry of Defence, Netherlands
203 4B Bosch Telecom, Germany
204 4A Ministry of Defence, Netherlands
205 4C European Union Institutions
207 51-54 access control & security
215 80 administration services
226 C0 entertainment & sports
235 F8-FF miscellaneous applications
238 0001 sector is defect, e.g. access keys are destroyed or unknown
239 0002 sector is reserved
240 0003 sector contains additional directory info (useful only for future cards)
241 0004 sector contains card holder information in ASCII format.
242 0005 sector not applicable (above memory size)
244 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
245 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope 1 10.10.2007 Issuer information
247 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Customer profile
248 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
249 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
251 1835 - city traffic KORID LK, spol.s.r.o. KORID LK, spol.s.r.o. Europe 2 08.09.2008 Eticket
252 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Prepaid coupon 1S
253 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Prepaid coupon
254 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 1 01.05.2009 Prepaid coupon
255 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 1 01.08.2009 Prepaid coupon
256 183B - city traffic UNICARD S.A. UNICARD S.A. Poland 15 01.01.2010 city traffic services
258 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe 1 01.08.2008 Electronic ticket
259 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
260 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 3 01.05.2009 electronic ticket
262 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 4 01.04.2009 Electronic purse
263 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 4 01.05.2009 electronic purse
264 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse