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 # General purpose byte (GPB)
78 my $GBP = ord(substr($card,0x39,1));
81 printf "manufacturer block\nSerial number: %s\nCB: %s\nmanufacturer data: %s\n"
82 , unpack('H*',substr($card,0,4))
83 , unpack('H*',substr($card,4,1))
84 , unpack('H*',substr($card,5,11))
88 $ADV = $GBP & 0b00000011;
89 $MA = $GBP & 0b01000000;
90 $DA = $GBP & 0b10000000;
91 printf "ADV (MAD version code): %d %s\n", $ADV,
92 printf "MA (multiapplication): %s\n", $MA ? 'yes' : 'monoaplication';
93 printf "DA (MAD available): %s%s\n", $DA ? 'yes' : 'no',
94 substr($card,$pos+0x30,6) eq "\xA0\xA1\xA2\xA3\xA4\xA5" ? ' public' : '';
96 printf "Info byte (publisher sector): %x\n", ord(substr($card,0x11,1));
98 my $mad_offset = 0x10 + ( $sector * 2 );
99 my $v = unpack('v',(substr($card, $mad_offset, 2)));
100 my $cluster_id = unpack('HH', (( $v & 0xff00 ) >> 8) );
101 my $full_id = sprintf "%04x",$v;
102 printf "MAD sector %-2d@%x %04x [%s]\n%s\n", $sector, $mad_offset, $v
103 , $function_clusters->{ $cluster_id }
104 , $mad_id->{$full_id} || "FIXME: add $full_id from MAD_overview.pdf to __DATA__ at end of $0"
107 if ( $v == 0x0004 ) {
108 # RLE encoded card holder information
109 my $data = substr( $card, $pos, 0x30);
113 0b01 => 'given name',
117 while ( substr($data,$o,1) ne "\x00" ) {
118 my $len = ord(substr($data,$o,1));
119 my $type = ( $len & 0b11000000 ) >> 6;
120 $len = $len & 0b00111111;
121 my $dump = substr($data,$o+1,$len-1);
122 $dump = '0x' . unpack('H*', $dump) if $type == 0b11; # any
123 printf "%-10s %2d %s\n", $types->{$type}, $len, $dump;
126 } elsif ( $v == 0x0015 ) {
127 printf "Card number: %s\n", unpack('h*',substr($card,$pos + 0x04,6));
131 printf "# sector %-2d with %d blocks\n", $sector, $blocks;
134 my $trailer_pos = $pos + $blocks * 0x10 - 0x10;
135 my $c1 = ( ord(substr($card,$trailer_pos+7,1)) & 0xf0 ) >> 4;
136 my $c2 = ( ord(substr($card,$trailer_pos+8,1)) & 0x0f );
137 my $c3 = ( ord(substr($card,$trailer_pos+8,1)) & 0xf0 ) >> 4;
139 printf "# trailer @%x c1:%d c2:%d c3:%d [%16b]\n"
140 , $trailer_pos, $c1, $c2, $c3
141 , unpack('n',(substr($card,$trailer_pos+7,2)))
144 foreach my $j ( 0 .. $blocks - 1 ) {
145 my $offset = $pos + $j * 0x10;
146 my $block = substr($card, $offset, 0x10);
149 = ( ( $c1 & $mask ) * 4 )
150 + ( ( $c2 & $mask ) * 2 )
151 + ( ( $c3 & $mask ) * 1 )
155 my $hex = unpack('H*',$block);
156 $hex =~ s/(....)/$1 /g;
158 if ( $ENV{SWAP} && $j < 3 ) {
159 my $hex_sw = unpack('h*',$block);
160 $hex_sw =~ s/(....)/$1 /g;
161 $hex .= " | $hex_sw";
164 printf "%04x %s %03b %s\n", $offset, $hex
166 , $j < 3 ? $access_condition_data->{$cond} : $access_condition_trailer->{$cond}
171 printf "KEY A:%s | %s GDP: %s | B:%s\n"
172 ,unpack('H*',substr($card,$pos+0x30 ,6))
173 ,unpack('H*',substr($card,$pos+0x30+6 ,3))
174 ,unpack('H*',substr($card,$pos+0x30+9 ,1))
175 ,unpack('H*',substr($card,$pos+0x30+10,6))
180 $pos += $blocks * 0x10;
184 00 card administration
185 01-07 miscellaneous applications
193 21 multi modal transit
197 40 city card services
198 47-48 access control & security
200 4A Ministry of Defence, Netherlands
201 4B Bosch Telecom, Germany
202 4A Ministry of Defence, Netherlands
203 4C European Union Institutions
205 51-54 access control & security
213 80 administration services
224 C0 entertainment & sports
233 F8-FF miscellaneous applications
236 0001 sector is defect, e.g. access keys are destroyed or unknown
237 0002 sector is reserved
238 0003 sector contains additional directory info (useful only for future cards)
239 0004 sector contains card holder information in ASCII format.
240 0005 sector not applicable (above memory size)
242 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
243 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope 1 10.10.2007 Issuer information
245 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Customer profile
246 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
247 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
249 1835 - city traffic KORID LK, spol.s.r.o. KORID LK, spol.s.r.o. Europe 2 08.09.2008 Eticket
250 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Prepaid coupon 1S
251 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Prepaid coupon
252 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 1 01.05.2009 Prepaid coupon
253 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 1 01.08.2009 Prepaid coupon
254 183B - city traffic UNICARD S.A. UNICARD S.A. Poland 15 01.01.2010 city traffic services
256 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe 1 01.08.2008 Electronic ticket
257 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
258 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 3 01.05.2009 electronic ticket
260 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 4 01.04.2009 Electronic purse
261 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 4 01.05.2009 electronic purse
262 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse