Created
April 24, 2014 01:02
-
-
Save msmuenchen/11237980 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<? | |
//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