Skip to content

Instantly share code, notes, and snippets.

@msmuenchen
Created April 24, 2014 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msmuenchen/11237980 to your computer and use it in GitHub Desktop.
Save msmuenchen/11237980 to your computer and use it in GitHub Desktop.
<?php
date_default_timezone_set("Europe/Berlin");
require("libmifare.php");
$ctx=scard_establish_context();
echo "Established context:\n";
var_dump($ctx);
$readers=scard_list_readers($ctx);
if(sizeof($readers)==0)
die("no readers\n");
while(true) {
$conn=scard_connect($ctx,$readers[0],SCARD_PROTOCOL_T1,$activeProto);
if($conn===false) {
$en=scard_last_errno();
$es=scard_errstr($en);
if($en!=SCARD_W_REMOVED_CARD)
echo "got error ".dechex($en).": $es\n";
sleep(1);
continue;
}
$card=new Mifare1K($conn);
$card->loadKey(0x00,"ffffffffffff");
$card->loadKey(0x01,"ffffffffffff");
echo "Wiping card\n";
$card->wipe();
$data=$card->getUID();
$prk=openssl_pkey_get_private(file_get_contents("priv.key"),"pass");
$puk=openssl_pkey_get_public(file_get_contents("pub.key"));
openssl_sign($data,$sig,$prk,OPENSSL_ALGO_SHA512);
$sig=base64_encode($sig);
echo "Writing UID signature\n";
$wd=serialize(["sig"=>$sig,"ts"=>date("d.m.Y H:i:s"),"cr"=>"marco"]);
$card->writeAll($wd);
echo "Reading UID signature\n";
$sig_c=unserialize($card->readAll());
$sig_c=base64_decode($sig_c["sig"]);
$res=openssl_verify($data,$sig_c,$puk,OPENSSL_ALGO_SHA512);
if($res==1)
echo "OpenSSL OK, insert next card and press RETURN\n";
else
throw new Exception("OpenSSL verify failed, card broken?");
scard_disconnect($conn);
fgets(STDIN);
}
<?
//Ref http://www.nxp.com/documents/data_sheet/MF1S503x.pdf
//Ref API-ACR122U-2.02.pdf
class UnsupportedATRException extends Exception {
}
class MalformedDataException extends Exception {
}
class APDUProtocolError extends Exception {
}
function decpadhex($dec) {
return str_pad(dechex($dec),2,"0",STR_PAD_LEFT);
}
function transmitAndCheck($conn,$cmd,$rc) {
// echo "transmitting to card: '$cmd', expecting rc $rc\n";
$ret=scard_transmit($conn,$cmd);
// echo "returned from card raw: '$ret'\n";
if(strlen($ret)<strlen($rc))
throw new MalformedDataException("Returned length smaller than expected rc length");
$retc=substr($ret,-strlen($rc),strlen($rc));
if(strtoupper($retc)!=strtoupper($rc))
throw new APDUProtocolError("RC $retc, expected $rc");
$data=substr($ret,0,strlen($ret)-strlen($rc));
$ds=hexstr($data);
// echo "got from card: '$data'-'$ds'(".strlen($ds).")\n";
return $data;
}
function hexstr($hex) {
$ds="";
for($i=0;$i<strlen($hex);$i+=2) {
$ch=chr(hexdec($hex[$i].$hex[$i+1]));
$ds.=$ch;
}
return $ds;
}
function strhex($str) {
$hs="";
for($i=0;$i<strlen($str);$i++) {
$hs.=decpadhex(ord($str[$i]));
}
return $hs;
}
class Mifare1K {
private $conn;
public $dataBlocks=[
[0,1],[0,2],
[1,0],[1,1],[1,2],
[2,0],[2,1],[2,2],
[3,0],[3,1],[3,2],
[4,0],[4,1],[4,2],
[5,0],[5,1],[5,2],
[6,0],[6,1],[6,2],
[7,0],[7,1],[7,2],
[8,0],[8,1],[8,2],
[9,0],[9,1],[9,2],
[10,0],[10,1],[10,2],
[11,0],[11,1],[11,2],
[12,0],[12,1],[12,2],
[13,0],[13,1],[13,2],
[14,0],[14,1],[14,2],
[15,0],[15,1],[15,2],
];
static public $acceptedATR=[
"3B8F8001804F0CA000000306030001000000006A",
];
function __construct($conn) {
$this->conn=$conn;
$status=scard_status($conn);
$atr=$status["ATR"];
if(array_search($atr,static::$acceptedATR)===FALSE)
throw new UnsupportedATRException("Unsupported ATR/card $atr");
}
function getUID() {
return transmitAndCheck($this->conn,"ffca000000","9000");
}
function loadKey($id,$key) {
transmitAndCheck($this->conn,"ff8200".decpadhex($id)."06".$key,"9000");
}
function readBlock($sector,$blk) {
return transmitAndCheck($this->conn,"ffb000".decpadhex($sector*4+$blk).decpadhex(16),"9000");
}
function writeBlock($sector,$blk,$data) {
if(strlen($data)!=16)
throw new MalformedDataException("can only update 16-byte blocks!");
$datah=strhex($data);
transmitAndCheck($this->conn,"ffd600".decpadhex($sector*4+$blk).decpadhex(16).strhex($data),"9000");
}
function authenticate($sector,$keytype,$keyid) {
$cmd="ff86000005";
$ad="0100".decpadhex($sector*4).decpadhex($keytype).decpadhex($keyid);
return transmitAndCheck($this->conn,$cmd.$ad,"9000");
}
function dumpData() {
for($i=0;$i<sizeof($this->dataBlocks)-10;$i++) {
list($sector,$blk)=$this->dataBlocks[$i];
$this->authenticate($sector,0x61,0x00);
$data=$this->readBlock($sector,$blk);
$ds=hexstr($data);
echo decpadhex($sector)."/".decpadhex($blk).": $data [$ds]\n";
}
}
function readAll() {
$fullData="";
for($i=0;$i<sizeof($this->dataBlocks);$i++) {
list($sector,$blk)=$this->dataBlocks[$i];
$this->authenticate($sector,0x61,0x00);
$data=$this->readBlock($sector,$blk);
$fullData.=$data;
}
$raw=substr($fullData,0,4);
$dataLen=hexdec(substr($fullData,0,4))*2;
$data=hexstr(substr($fullData,4,$dataLen));
return $data;
}
function writeAll($data) {
if(strlen($data)>((sizeof($this->dataBlocks)*16)+2))
throw new MalformedDataException("data too long");
$data=hexstr(str_pad(dechex(strlen($data)),4,"0",STR_PAD_LEFT)).$data;
$chunks=str_split($data,16);
for($i=0;$i<sizeof($chunks);$i++) {
$chunk=str_pad($chunks[$i],16," ",STR_PAD_RIGHT);
list($sector,$blk)=$this->dataBlocks[$i];
$this->authenticate($sector,0x61,0x00);
$this->writeBlock($sector,$blk,$chunk);
}
}
function wipe() {
for($i=0;$i<sizeof($this->dataBlocks);$i++) {
list($sector,$blk)=$this->dataBlocks[$i];
$this->authenticate($sector,0x61,0x00);
// echo "Blanking s".decpadhex($sector)."/blk".decpadhex($blk).".\n";
$this->writeBlock($sector,$blk,"----------------");
}
}
function dumpACLAndKeys() {
for($i=0;$i<16;$i++) {
// 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6
//000000000000 - 787788C1 - 000000000000
$this->authenticate($i,0x61,0x00);
$tr=$this->readBlock($i,3);
$keyA=substr($tr,0,12);
$acl1=hexdec(substr($tr,12,2));
$acl2=hexdec(substr($tr,14,2));
$acl3=hexdec(substr($tr,16,2));
$userByte=substr($tr,18,2);
$keyB=substr($tr,20,12);
$c2i=($acl1 & 0xF0)>>4;
$c1i=($acl1 & 0x0F);
$c1=($acl2 & 0xF0)>>4;
$c3i=($acl2 & 0x0F);
$c3=($acl3 & 0xF0)>>4;
$c2=($acl3 & 0x0F);
if($c1!=((~$c1i) & 0x0F))
throw new Exception("Sector $i: C1 bits do not match");
if($c2!=((~$c2i) & 0x0F))
throw new Exception("Sector $i: C2 bits do not match");
if($c3!=((~$c3i) & 0x0F))
throw new Exception("Sector $i: C3 bits do not match");
$b0_c1=($c1 & 0x01);
$b0_c2=($c2 & 0x01);
$b0_c3=($c3 & 0x01);
$b1_c1=($c1 & 0x02)>>1;
$b1_c2=($c2 & 0x02)>>1;
$b1_c3=($c3 & 0x02)>>1;
$b2_c1=($c1 & 0x04)>>2;
$b2_c2=($c2 & 0x04)>>2;
$b2_c3=($c3 & 0x04)>>2;
$b3_c1=($c1 & 0x08)>>3;
$b3_c2=($c2 & 0x08)>>3;
$b3_c3=($c3 & 0x08)>>3;
echo decpadhex($sector).": $keyA - 0: $b0_c1/$b0_c2/$b0_c3 - 1: $b1_c1/$b1_c2/$b1_c3 - 2: $b2_c1/$b2_c2/$b2_c3 - t: $b3_c1/$b3_c2/$b3_c3 - $userByte - $keyB\n";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment