mifare.js
Summary
Script classes to access Mifare cards
Class Summary
|
Mifare |
Class encapsulating access to a Mifare classic 1K/4K card
|
Sector |
Class representing a sector on a Mifare card
|
function Mifare(card) {
this.card = card;
}
Mifare.KEY_A = 0x60;
Mifare.KEY_B = 0x61;
Mifare.PUBLICKEYS = [
new ByteString("FFFFFFFFFFFF", HEX),
new ByteString("A0A1A2A3A4A5", HEX),
new ByteString("B0B1B2B3B4B5", HEX),
new ByteString("D3F7D3F7D3F7", HEX),
];
Mifare.crc8 = function(data) {
var polynom = 0x1d;
var crc = 0xC7;
for (var i = 0; i < data.length; i++) {
crc ^= data.byteAt(i);
for (var b = 0; b < 8; b++) {
var msb = crc & 0x80;
crc = (crc << 1) & 0xFF;
if (msb) {
crc ^= polynom;
}
}
}
return crc;
}
Mifare.prototype.getUID = function() {
return this.card.sendApdu(0xFF, 0xCA, 0x00, 0x00, 4, [0x9000]);
}
Mifare.prototype.loadKey = function(keyid, key) {
assert(typeof(keyid) == "number");
assert(key.length == 6);
if ((keyid == 0x60) || (keyid == 0x61)) {
this.card.sendApdu(0xFF, 0x82, 0x00, keyid, key, [0x9000]);
} else {
this.card.sendApdu(0xFF, 0x82, 0x20, keyid, key, [0x9000]);
}
}
Mifare.prototype.readBlock = function(block) {
return this.card.sendApdu(0xFF, 0xB0, block >> 8, block & 0xFF, 16, [0x9000]);
}
Mifare.prototype.updateBlock = function(block, data) {
assert(data.length == 16);
return this.card.sendApdu(0xFF, 0xD6, block >> 8, block & 0xFF, data, [0x9000]);
}
Mifare.prototype.authenticate = function(block, keytype, keyid) {
var bb = new ByteBuffer();
bb.append(0x01);
bb.append(ByteString.valueOf(block, 2));
bb.append(keytype);
if ((keyid != 0x60) && (keyid != 0x61)) {
bb.append(keyid);
} else {
bb.append(0x01);
}
this.card.sendApdu(0xFF,0x86,0x00,0x00, bb.toByteString());
return this.card.SW == 0x9000;
}
Mifare.prototype.newSector = function(no) {
return new Sector(this, no);
}
function Sector(mifare, no) {
this.mifare = mifare;
this.no = no;
this.blocks = [];
this.keyid = [0, 1];
}
Sector.MASK = [ 0x00E0EE, 0x00D0DD, 0x00B0BB, 0x007077 ];
Sector.AC_TRAILER = [
"000 - Key A: Write Key A | AC: Write Never | Key B: Read Key A / Write Key A",
"001 - Key A: Write Key A | AC: Write Key A | Key B: Read Key A / Write Key A",
"010 - Key A: Write Never | AC: Write Never | Key B: Read Key A / Write Never",
"011 - Key A: Write Key B | AC: Write Key B | Key B: Read Never / Write Key B",
"100 - Key A: Write Key B | AC: Write Never | Key B: Read Never / Write Key B",
"101 - Key A: Write Never | AC: Write Key B | Key B: Read Never / Write Never",
"110 - Key A: Write Never | AC: Write Never | Key B: Read Never / Write Never",
"111 - Key A: Write Never | AC: Write Never | Key B: Read Never / Write Never",
];
Sector.AC_FIXED_AC_NOKEY_B = 0;
Sector.AC_UPDATE_AC_NOKEY_B = 1;
Sector.AC_READONLY_NOKEY_B = 2;
Sector.AC_UPDATE_WITH_KEYB = 3;
Sector.AC_FIXED_AC_UPDATE_WITH_KEYB = 4;
Sector.AC_UPDATE_AC_FIXED_KEYS = 5;
Sector.AC_NEVER2 = 6;
Sector.AC_DATA = [
"000 - Read: Key AB | Write: Key AB | Inc: Key AB | Dec: Key AB",
"001 - Read: Key AB | Write: Never | Inc: Never | Dec: Key AB",
"010 - Read: Key AB | Write: Never | Inc: Never | Dec: Never ",
"011 - Read: Key B | Write: Key B | Inc: Never | Dec: Never ",
"100 - Read: Key AB | Write: Key B | Inc: Never | Dec: Never ",
"101 - Read: Key B | Write: Never | Inc: Never | Dec: Never ",
"110 - Read: Key AB | Write: Key B | Inc: Key B | Dec: Key AB",
"111 - Read: Never | Write: Never | Inc: Never | Dec: Never ",
];
Sector.AC_ALWAYS = 0;
Sector.AC_NONRECHARGEABLE = 1;
Sector.AC_READONLY = 2;
Sector.AC_KEYBONLY = 3;
Sector.AC_UPDATEKEYB = 4;
Sector.AC_KEYBREADONLY = 5;
Sector.AC_RECHARGEABLE = 6;
Sector.AC_NEVER = 7;
Sector.prototype.setKeyId = function(keyid, keytype) {
if (typeof(keytype) == "undefined") {
keytype = Mifare.KEY_A;
}
this.keyid[keytype - Mifare.KEY_A] = keyid;
}
Sector.prototype.read = function(block) {
assert(block >= 0);
assert(block <= 3);
var blockoffs = (this.no << 2) + block;
this.blocks[block] = this.mifare.readBlock(blockoffs);
return this.blocks[block];
}
Sector.prototype.update = function(block, data) {
if (typeof(data) == "undefined") {
data = this.blocks[block];
} else {
this.blocks[block] = data
}
var blockoffs = (this.no << 2) + block;
this.mifare.updateBlock(blockoffs, data);
}
Sector.prototype.authenticate = function(block, keytype) {
return this.mifare.authenticate((this.no << 2) + block, keytype, this.keyid[keytype - Mifare.KEY_A]);
}
Sector.prototype.authenticatePublic = function(block, keytype) {
var i = 0;
var authenticated = false;
for (var i = 0; i < Mifare.PUBLICKEYS.length; i++) {
this.mifare.loadKey(this.keyid[keytype - Mifare.KEY_A], Mifare.PUBLICKEYS[i]);
if (this.authenticate(block, keytype)) {
return i;
}
}
return -1;
}
Sector.prototype.readAll = function(keytype) {
if (typeof(keytype) == "undefined") {
keytype = Mifare.KEY_A;
}
var bb = new ByteBuffer();
this.authenticate(0, keytype);
for (var i = 0; i < 4; i++) {
bb.append(this.read(i));
}
return bb.toByteString();
}
Sector.prototype.getACforBlock = function(block) {
var c = this.blocks[3].bytes(6, 3).toUnsigned();
return ((((c >> (12 + block)) & 0x01) << 2) +
(((c >> ( 0 + block)) & 0x01) << 1) +
((c >> (4 + block)) & 0x01));
}
Sector.prototype.setACforBlock = function(block, ac) {
var c = this.blocks[3].bytes(6, 3).toUnsigned();
c &= Sector.MASK[block];
c |= ((((ac >> 2) & 0x01) << (12 + block)) +
(((ac >> 1) & 0x01) << ( 0 + block)) +
(( ac & 0x01) << ( 4 + block)));
c |= (((~c & 0xF) << 20) +
((~c & 0xF0) << 4) +
((~c & 0xF000) << 4));
var d = this.blocks[3];
this.blocks[3] = d.bytes(0, 6).concat(ByteString.valueOf(c, 3).concat(d.bytes(9)));
}
Sector.prototype.setKeyA = function(key) {
var d = this.blocks[3];
this.blocks[3] = key.concat(d.bytes(6));
}
Sector.prototype.setKeyB = function(key) {
var d = this.blocks[3];
this.blocks[3] = d.bytes(0, 10).concat(key);
}
Sector.prototype.setHeaderDataByte = function(db) {
var d = this.blocks[3];
this.blocks[3] = d.bytes(0, 9).concat(db).concat(d.bytes(10));
}
Sector.toASCII = function(data) {
var str = "";
for (var i = 0; i < data.length; i++) {
var c = data.byteAt(i);
if ((c >= 0x20) && (c < 0x7F)) {
str += String.fromCharCode(c);
} else {
str += ".";
}
}
return str;
}
Sector.prototype.toString = function() {
var str = "";
for (var i = 0; i < 4; i++) {
str += "Sec" + this.no + " Blk" + i + " - ";
var ac = this.getACforBlock(i);
if (i == 3) {
str += Sector.AC_TRAILER[ac];
} else {
str += Sector.AC_DATA[ac];
}
str += "\n";
if (typeof(this.blocks[i]) != "undefined") {
str += " " + this.blocks[i].toString(HEX) + " " + Sector.toASCII(this.blocks[i]) + "\n";
}
}
return str;
}
Documentation generated by
JSDoc on Tue Sep 3 22:29:45 2013