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:-/-',
58 "\x78\x77\x88" => 'MAD INIT,RW',
59 "\x7F\x07\x88" => 'NFC INIT,RW',
60 "\x07\x8F\x0F" => 'READ ONLY',
65 warn "# function_clusters ",dump($function_clusters);
66 warn "# mad_id ", dump($mad_id);
72 die "expected 4096 or 1024 bytes, got ",length($card), " bytes\n"
73 unless length $card == 4096 || length $card == 1024;
75 my ( $ADV, $MA, $DA );
79 foreach my $sector ( 0 .. 39 ) {
81 my $blocks = $sector < 32 ? 4 : 16;
83 last if $pos >= length($card);
84 next if substr($card,$pos,$blocks * 0x10) eq "\x00" x ($blocks * 0x10);
86 # General purpose byte (GPB)
87 my $GBP = ord(substr($card,0x39,1));
90 printf "manufacturer block\nSerial number: %s\nCB: %s\nmanufacturer data: %s\n"
91 , unpack('H*',substr($card,0,4))
92 , unpack('H*',substr($card,4,1))
93 , unpack('H*',substr($card,5,11))
97 $ADV = $GBP & 0b00000011;
98 $MA = $GBP & 0b01000000;
99 $DA = $GBP & 0b10000000;
100 printf "ADV (MAD version code): %d\n", $ADV;
101 printf "MA (multiapplication): %s\n", $MA ? 'yes' : 'monoaplication';
102 printf "DA (MAD available): %s%s\n", $DA ? 'yes' : 'no',
103 substr($card,$pos+0x30,6) eq "\xA0\xA1\xA2\xA3\xA4\xA5" ? ' public' : '';
105 printf "Info byte (publisher sector): %x\n", ord(substr($card,0x11,1));
107 my $mad_offset = 0x10 + ( $sector * 2 );
108 my $v = unpack('v',(substr($card, $mad_offset, 2)));
109 my $cluster_id = unpack('HH', (( $v & 0xff00 ) >> 8) );
110 my $full_id = sprintf "%04x",$v;
111 printf "# sector %-2d MAD@%x %04x [%s]\n%s\n", $sector, $mad_offset, $v
112 , $function_clusters->{ $cluster_id }
113 , $mad_id->{$full_id} || "FIXME: add $full_id from MAD_overview.pdf to __DATA__ at end of $0"
116 if ( $v == 0x0004 ) {
117 # RLE encoded card holder information
118 my $data = substr( $card, $pos, 0x30);
122 0b01 => 'given name',
126 while ( substr($data,$o,1) ne "\x00" ) {
127 my $len = ord(substr($data,$o,1));
128 my $type = ( $len & 0b11000000 ) >> 6;
129 $len = $len & 0b00111111;
130 my $dump = substr($data,$o+1,$len-1);
131 $dump = '0x' . unpack('H*', $dump) if $type == 0b11; # any
132 printf "%-10s %2d %s\n", $types->{$type}, $len, $dump;
135 } elsif ( $v == 0x0015 ) {
136 printf "Card number: %s\n", unpack('h*',substr($card,$pos + 0x04,6));
140 printf "# sector %-2d with %d blocks\n", $sector, $blocks;
143 my $trailer_pos = $pos + $blocks * 0x10 - 0x10;
144 my $c1 = ( ord(substr($card,$trailer_pos+7,1)) & 0xf0 ) >> 4;
145 my $c2 = ( ord(substr($card,$trailer_pos+8,1)) & 0x0f );
146 my $c3 = ( ord(substr($card,$trailer_pos+8,1)) & 0xf0 ) >> 4;
148 printf "# trailer @%x %016b c1:%08b c2:%08b c3:%08b\n"
149 , unpack('n',(substr($card,$trailer_pos+7,2)))
150 , $trailer_pos, $c1, $c2, $c3
154 foreach my $j ( 0 .. $blocks - 1 ) {
155 my $offset = $pos + $j * 0x10;
156 my $block = substr($card, $offset, 0x10);
160 $j % 5 == 0 ? $j / 5 :
161 undef; # display condition only once for block group
163 if ( defined $acl_block ) {
164 my $trailer = $j == ( $blocks - 1 );
165 my $mask = 1 << $acl_block;
167 = ( ( $c1 & $mask ) * 4 )
168 + ( ( $c2 & $mask ) * 2 )
169 + ( ( $c3 & $mask ) * 1 )
171 $cond >>= $acl_block;
172 $condition = sprintf ' %03b %s'
174 , $trailer ? $access_condition_trailer->{$cond}
175 : $access_condition_data->{$cond}
177 if ( ! $trailer && ( $cond == 0b001 || $cond == 0b011 ) ) {
178 my ( $value_block, $not ) = unpack 'llx8', $block;
179 my $value = $value_block;
180 # my $positive = $value_block & 0x8000_0000;
181 # my $value = $value_block & 0x7fff_ffff;
182 # $value = -$value if ! $positive;
183 #$condition .= sprintf " = %d 0x%x", $value, $value_block;
184 $condition .= sprintf " = %d 0x%x", $value_block, $not;
190 my $hex = unpack('H*',$block);
191 $hex =~ s/(....)/$1 /g;
194 my $hex_sw = unpack('h*',$block);
195 $hex_sw =~ s/(....)/$1 /g;
196 $hex .= " | $hex_sw";
199 printf "%x %03x %s%s\n", $j, $offset, $hex, $condition;
202 printf "KEY A:%s | %s GDP: %s | B:%s %s\n"
203 ,unpack('H*',substr($card,$trailer_pos ,6))
204 ,unpack('H*',substr($card,$trailer_pos+6 ,3))
205 ,unpack('H*',substr($card,$trailer_pos+9 ,1))
206 ,unpack('H*',substr($card,$trailer_pos+10,6))
207 ,$life_cycle->{substr($card,$trailer_pos+6,3)} || ''
212 $pos += $blocks * 0x10;
216 00 card administration
217 01-07 miscellaneous applications
225 21 multi modal transit
229 40 city card services
230 47-48 access control & security
232 4A Ministry of Defence, Netherlands
233 4B Bosch Telecom, Germany
234 4A Ministry of Defence, Netherlands
235 4C European Union Institutions
237 51-54 access control & security
245 80 administration services
256 C0 entertainment & sports
265 F8-FF miscellaneous applications
268 0001 sector is defect, e.g. access keys are destroyed or unknown
269 0002 sector is reserved
270 0003 sector contains additional directory info (useful only for future cards)
271 0004 sector contains card holder information in ASCII format.
272 0005 sector not applicable (above memory size)
274 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
275 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope 1 10.10.2007 Issuer information
277 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Customer profile
278 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
279 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
281 1835 - city traffic KORID LK, spol.s.r.o. KORID LK, spol.s.r.o. Europe 2 08.09.2008 Eticket
282 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Prepaid coupon 1S
283 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Prepaid coupon
284 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 1 01.05.2009 Prepaid coupon
285 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 1 01.08.2009 Prepaid coupon
286 183B - city traffic UNICARD S.A. UNICARD S.A. Poland 15 01.01.2010 city traffic services
288 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe 1 01.08.2008 Electronic ticket
289 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
290 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 3 01.05.2009 electronic ticket
292 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 4 01.04.2009 Electronic purse
293 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 4 01.05.2009 electronic purse
294 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse