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 = unpack 'x4Lx8', $block;
179 my $positive = $value_block & 0x8000_0000;
180 my $value = $value_block & 0x7fff_ffff;
181 $value = -$value if ! $positive;
182 #$condition .= sprintf " = %d 0x%x", $value, $value_block;
183 $condition .= " = " . $value;
189 my $hex = unpack('H*',$block);
190 $hex =~ s/(....)/$1 /g;
193 my $hex_sw = unpack('h*',$block);
194 $hex_sw =~ s/(....)/$1 /g;
195 $hex .= " | $hex_sw";
198 printf "%x %03x %s%s\n", $j, $offset, $hex, $condition;
201 printf "KEY A:%s | %s GDP: %s | B:%s %s\n"
202 ,unpack('H*',substr($card,$trailer_pos ,6))
203 ,unpack('H*',substr($card,$trailer_pos+6 ,3))
204 ,unpack('H*',substr($card,$trailer_pos+9 ,1))
205 ,unpack('H*',substr($card,$trailer_pos+10,6))
206 ,$life_cycle->{substr($card,$trailer_pos+6,3)} || ''
211 $pos += $blocks * 0x10;
215 00 card administration
216 01-07 miscellaneous applications
224 21 multi modal transit
228 40 city card services
229 47-48 access control & security
231 4A Ministry of Defence, Netherlands
232 4B Bosch Telecom, Germany
233 4A Ministry of Defence, Netherlands
234 4C European Union Institutions
236 51-54 access control & security
244 80 administration services
255 C0 entertainment & sports
264 F8-FF miscellaneous applications
267 0001 sector is defect, e.g. access keys are destroyed or unknown
268 0002 sector is reserved
269 0003 sector contains additional directory info (useful only for future cards)
270 0004 sector contains card holder information in ASCII format.
271 0005 sector not applicable (above memory size)
273 0015 - card administration MIKROELEKTRONIKA spol.s.v.MIKROELEKTRONIKA spol.s.v.o. worldwide 1 01.02.2007 Card publisher info
274 0016 - card administration Mikroelektronika spol.s.r.o., Kpt.Mikroelektronika spol.s.r.o., Kpt. PoEurope 1 10.10.2007 Issuer information
276 071C - miscellaneous applications MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Customer profile
277 071D - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Customer profile
278 071E - miscellaneous applications ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Bonus counter
280 1835 - city traffic KORID LK, spol.s.r.o. KORID LK, spol.s.r.o. Europe 2 08.09.2008 Eticket
281 1836 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o., Europe 1 01.12.2008 Prepaid coupon 1S
282 1837 - city traffic ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 1 01.04.2009 Prepaid coupon
283 1838 - city traffic MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 1 01.05.2009 Prepaid coupon
284 1839 - city traffic Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 1 01.08.2009 Prepaid coupon
285 183B - city traffic UNICARD S.A. UNICARD S.A. Poland 15 01.01.2010 city traffic services
287 2061 - bus services Mikroelektronika spol.s r.o. Mikroelektronika spol.s r.o. Europe 1 01.08.2008 Electronic ticket
288 2062 - bus services ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o EUROPE,Croatia 1 01.04.2009 Electronic tiicket
289 2063 - bus services MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 3 01.05.2009 electronic ticket
291 887B - electronic purse ZAGREBACKI Holding d.o.o. MIKROELEKTRONIKA spol.s.r.o. EUROPE,Croatia 4 01.04.2009 Electronic purse
292 887C - electronic purse MIKROELEKTRONIKA spol.s.r. MIKROELEKTRONIKA spol.s.r.o. Europe 4 01.05.2009 electronic purse
293 887D - electronic purse Mikroelektronika spol.s r.o Mikroelektronika spol.s r.o EUROPE,Czech R 4 01.08.2009 Electronic purse