MIFARE DESFire EV1 via pcsc-perl

Der RFID Chip machte und macht mir noch immer einige Sorgen.

Die Kommunikation erfolgt via sogenannter APDU Kommandos. Das sind 8 byte HEX Strings. [2]
Es gibt wohl nach ISO Standard irgendwelche standard Befehle und es gibt zusätzlich einen DESFire EV1 proprietären Befehlssatz.

Die ISO Befehle sind leider nur rudimentär beschrieben. [2] Jedenfalls habe ich nichts adequates gefunden. Schlimmer noch, die DESFire EV1 Befehle sind gar nicht öffentlich zu bekommen.

Es soll eine Dokumentation von NXP geben die sich „MIFARE DESFire – Implementation hints and examples,document number: 094532″ nennt. In diversen Foren ist davon die Rede dass man die nur nach Unterzeichnung einer NDA bekommt.
Um irgendwie weiter zu kommen habe ich nun einen Account bei NXP.com erstellt und am 1.6. eine Anfrage beim Support erstellt (diese ist Stand heute noch nicht beantwortet worden). Wir harren also der Dinge.

Nichts desto trotz ist es mir nach einiger Recherche gelungen die Basisdaten auszulesen aus einer nicht Verschlüsselten blanko RFID Karte. Also welche Grundlegende Konfiguration diese hat, die UID und ein paar Infos zum Betriebssystem und der Hardware. Hier ein Beispiel (der Quellcode ist NICHT vollständig, es sind nur ein paar Ausschnitte des gesammten Programms):

[..]
# PC/SC Handling
use Chipcard::PCSC;                # to connect to card reader
use Chipcard::PCSC::Card;          # to read smart card
[..]
#---------------------------------------------------------------------#
# sendAPDU
# description: send APDU command and receive data
# input: object (hCard), string (APDU comand)
# return: string (APDU response)
#---------------------------------------------------------------------#
sub sendAPDU
  {
  my $card = shift;
  my $APDU_in = shift;

  my $SendData = Chipcard::PCSC::ascii_to_array($APDU_in);
  my $RecvData = $card->Transmit($SendData);
  if ($RecvData)
    {
    my $APDU_out = Chipcard::PCSC::array_to_ascii($RecvData);
    return ($APDU_out);
    }
  else
    {
    return(undef);
    }
  }
#---------------------------------------------------------------------#
# sendAPDUwithSW
# description: send APDU command and receive data and status word
# input: object (hCard), string (APDU comand)
# return: string (APDU response), string (APDU status word)
#---------------------------------------------------------------------#
sub sendAPDUwithSW
  {
  my $card = shift;
  my $APDU_in = shift;

  my $SendData = Chipcard::PCSC::ascii_to_array($APDU_in);
  my $RecvData = $card->Transmit($SendData);
  my $sw_r = pop(@{$RecvData});
  my $sw_l = pop(@{$RecvData});
  my $sw = [$sw_l, $sw_r];

  my $APDU_out = Chipcard::PCSC::array_to_ascii($RecvData);
  my $SW = Chipcard::PCSC::array_to_ascii($sw);

  return ($APDU_out, $SW);
  }
#---------------------------------------------------------------------#
# getPICCData
# description: get the Cards manufacturing data
# input: object (hCard)
# return: hash table reference
#   hardware_vendor_id  => Vendor ID in hex
#   hardware_type       => Hardware Type
#   hardware_subtype    => Hardware Subtype
#   hardware_version    => Hardware Version Number
#   hardware_storage    => Storage size in bytes
#   hardware_protocol   => Hardware protocol
#   software_vendor_id  => Vendor ID in hex
#   software_type       => Software Type
#   software_subtype    => Software Subtype
#   software_version    => Software Version Number
#   software_storage    => Storage size in bytes
#   software_protocol   => Storage protocol
#   batch_number        => Batch Number
#   calendar_week       => Calendar week of production
#   production_year     => Year of production
#---------------------------------------------------------------------#
sub getPICCData
  {
  my $card = shift;

  my $response='';
  my $PICC = {};

  # get Hardware data
  $response = sendAPDU($card, '60');
  if (!$response) { return(undef); }
  # AF 04 01 01 01 00 1A 05
  $PICC->{'hardware_vendor_id'} = substr($response, 3, 2);
  $PICC->{'hardware_type'} = substr($response, 6, 2);
  $PICC->{'hardware_subtype'} = substr($response, 9, 2);
  my $HWVerMaj = substr($response, 12, 2);
  my $HWVerMin = substr($response, 15, 2);
  $PICC->{'hardware_version'} = hex($HWVerMaj).'.'.hex($HWVerMin);
  my $HWStorage = substr($response, 18, 2);
  #$PICC->{'hardware_storage'} = hex($HWStorage);
  $PICC->{'hardware_storage'} = $HWStorage;
  $PICC->{'hardware_protocol'} = substr($response, 21, 2);

  # get Software data
  $response = sendAPDU($card, 'AF');
  if (!$response) { return(undef); }
  $PICC->{'software_vendor_id'} = substr($response, 3, 2);
  $PICC->{'software_type'} = substr($response, 6, 2);
  $PICC->{'software_subtype'} = substr($response, 9, 2);
  my $SWVerMaj = substr($response, 12, 2);
  my $SWVerMin = substr($response, 15, 2);
  $PICC->{'software_version'} = hex($SWVerMaj).'.'.hex($SWVerMin);
  my $SWStorage = substr($response, 18, 2);
  #$PICC->{'software_storage'} = hex($SWStorage);
  $PICC->{'software_storage'} = $SWStorage;
  $PICC->{'software_protocol'} = substr($response, 21, 2);

  # get production data
  $response = sendAPDU($card, 'AF');
  if (!$response) { return(undef); }
  $PICC->{'batch_number'} = substr($response, 24, 14);
  my $ProdCW = substr($response, 39, 2);
  $PICC->{'calendar_week'} = $ProdCW;
  my $ProdYR = substr($response, 42, 2);
  $PICC->{'production_year'} = $ProdYR + 2000;

  return($PICC);
  }
[..]

my $hContext = new Chipcard::PCSC($Chipcard::PCSC::SCARD_SCOPE_SYSTEM, 0);
my @ReadersList = $hContext->ListReaders ();
my $ReadersName = $ReadersList[0];
$readers_states[0]={ 'reader_name' => $ReadersName };
# ignore first event
my @StatusResult = $hContext->GetStatusChange(\@readers_states);
foreach my $reader (@readers_states) { $reader->{'current_state'} = $reader->{'event_state'}; }

[..]

# Initialize  Card Object to read data from card
my $hCard = new Chipcard::PCSC::Card($hContext,$ReadersName,$Chipcard::PCSC::SCARD_SHARE_EXCLUSIVE,$Chipcard::PCSC::SCARD_PROTOCOL_T0);

# get UID from Card
my ($CARD_UID, $sw) = sendAPDUwithSW($hCard, 'FF CA 00 00 00');
print DATETIMESTRING().'   APDU state: '.Chipcard::PCSC::Card::ISO7816Error($sw).' ('.$sw.')'."\n";
print DATETIMESTRING().'   Card UID: '.$CARD_UID."\n";
[..]
my $piccData = getPICCData($hCard);
[..]
my $piccApp = getPICCApp($hCard);
[..]
$hCard->Disconnect($Chipcard::PCSC::SCARD_EJECT_CARD);
$hCard = undef;

Der nächste Schritt ist, eine Authentisierung hinzubekommen. Zum Verfahren findet man einige Beiträge im Netz – allerdings nie so richtig vollständig. [3a][3b][3c][4][5a][5b][6]

Die Desfire unterstützt als Verschlüsselungs Algorithmus DES, 3-DES und AES. Leider konnte ich keines der Beispiele im Netz mit Hilfe der Per Cipher Engins nachprogrammieren. Es scheint hier zu generellen Implementationsproblemen zu kommen. Egal was ich anwende CBC / noPadding via DES, 3-DES oder AES, nichts scheint die selben Ergebnisse zu reproduzieren wie in der Beispielen im Internet.

Ja, ich konnte das Licht im Büro ein und Ausschalten (hat meinen 5 Jährigen schwer beeindruckt) aber es ist mir dann doch zu unsicher nur mit der UID einer Karte etwas freizuschalten. Daher habe ich das Thema mit Perl zu Seite gelegt und angefangen Java zu lernen.

Ein paar Tage später konnte ich immerhin schon 3 der 5 auffindbaren Beispiele reproduzieren.

Die Idee aus Perl heraus ein Java zu triggern hatte ich tatsächlich kurz gehabt aber dann doch wieder verworfen. Wie schon gesagt, ich habe auch null Info von NXP.com bis heute.

Mahl ehrlich, das ist wie wen jemanden einen USB Stick verkauft der nicht vom Betriebsystem erkannt wird und man sagt dem Käufer auch nicht wie er ihn zum laufen bekommt, der der Treiber ist geheim. Meines Erachtens ziemlicher Schwachsinn.

Links:

[1] WikiPedia: Application Protocol Data Unit (APDU)
[2] CardWerk: ISO 7816 Smart Card Standard
[3a] http://stackoverflow.com/questions/14319321/how-can-i-do-native-authentication-in-desfire-ev1
[3b] http://stackoverflow.com/questions/14117025/des-send-and-receive-modes-for-desfire-authentication
[3c] https://n3vrax.wordpress.com/2011/07/23/des-algorithm-java-implementation/
[4] https://ridrix.wordpress.com/2009/09/19/mifare-desfire-communication-example/#comment-87
[5a] http://database.developer-works.com/article/15751843/Mifare+DESfire+card+Authentication!!!!!
[5b] https://community.oracle.com/thread/1751843
[6] http://stackoverflow.com/questions/21257442/mifare-desfire-ev1-authentication-using-aes