/* NFCard is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
NFCard is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wget. If not, see <http://www.gnu.org/licenses/>.
Additional permission under GNU GPL version 3 section 7 */
package com.zzx.factorytest.nfc.tech;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import android.nfc.tech.IsoDep;
import com.zzx.factorytest.nfc.Util;
public class Iso7816 {
public static final byte[] EMPTY = { 0 };
protected byte[] data;
protected Iso7816() {
data = Iso7816.EMPTY;
}
protected Iso7816(byte[] bytes) {
data = (bytes == null) ? Iso7816.EMPTY : bytes;
}
public boolean match(byte[] bytes) {
return match(bytes, 0);
}
public boolean match(byte[] bytes, int start) {
final byte[] data = this.data;
if (data.length <= bytes.length - start) {
for (final byte v : data) {
if (v != bytes[start++])
return false;
}
}
return true;
}
public boolean match(byte tag) {
return (data.length == 1 && data[0] == tag);
}
public boolean match(short tag) {
final byte[] data = this.data;
if (data.length == 2) {
final byte d0 = (byte) (0x000000FF & tag);
final byte d1 = (byte) (0x000000FF & (tag >> 8));
return (data[0] == d0 && data[1] == d1);
}
return false;
}
public int size() {
return data.length;
}
public byte[] getBytes() {
return data;
}
@Override
public String toString() {
return Util.toHexString(data, 0, data.length);
}
public final static class ID extends Iso7816 {
public ID(byte[] bytes) {
super(bytes);
}
}
public final static class Response extends Iso7816 {
public static final byte[] EMPTY = {};
public static final byte[] ERROR = { 0x6F, 0x00 }; // SW_UNKNOWN
public Response(byte[] bytes) {
super((bytes == null || bytes.length < 2) ? Response.ERROR : bytes);
}
public byte getSw1() {
return data[data.length - 2];
}
public byte getSw2() {
return data[data.length - 1];
}
public short getSw12() {
final byte[] d = this.data;
int n = d.length;
return (short) ((d[n - 2] << 8) | (0xFF & d[n - 1]));
}
public boolean isOkey() {
return equalsSw12(SW_NO_ERROR);
}
public boolean equalsSw12(short val) {
return getSw12() == val;
}
public int size() {
return data.length - 2;
}
public byte[] getBytes() {
return isOkey() ? Arrays.copyOfRange(data, 0, size())
: Response.EMPTY;
}
}
public final static class BerT extends Iso7816 {
// tag template
public static final byte TMPL_FCP = 0x62; // File Control Parameters
public static final byte TMPL_FMD = 0x64; // File Management Data
public static final byte TMPL_FCI = 0x6F; // FCP and FMD
// proprietary information
public final static BerT CLASS_PRI = new BerT((byte) 0xA5);
// short EF identifier
public final static BerT CLASS_SFI = new BerT((byte) 0x88);
// dedicated file name
public final static BerT CLASS_DFN = new BerT((byte) 0x84);
// application data object
public final static BerT CLASS_ADO = new BerT((byte) 0x61);
// application id
public final static BerT CLASS_AID = new BerT((byte) 0x4F);
public static int test(byte[] bytes, int start) {
int len = 1;
if ((bytes[start] & 0x1F) == 0x1F) {
while ((bytes[start + len] & 0x80) == 0x80)
++len;
++len;
}
return len;
}
public static BerT read(byte[] bytes, int start) {
return new BerT(Arrays.copyOfRange(bytes, start,
start + test(bytes, start)));
}
public BerT(byte tag) {
this(new byte[] { tag });
}
public BerT(short tag) {
this(new byte[] { (byte) (0x000000FF & (tag >> 8)),
(byte) (0x000000FF & tag) });
}
public BerT(byte[] bytes) {
super(bytes);
}
public boolean hasChild() {
return ((data[0] & 0x20) == 0x20);
}
}
public final static class BerL extends Iso7816 {
private final int val;
public static int test(byte[] bytes, int start) {
int len = 1;
if ((bytes[start] & 0x80) == 0x80) {
len += bytes[start] & 0x07;
}
return len;
}
public static int calc(byte[] bytes, int start) {
if ((bytes[start] & 0x80) == 0x80) {
int v = 0;
int e = start + bytes[start] & 0x07;
while (++start <= e) {
v <<= 8;
v |= bytes[start] & 0xFF;
}
return v;
}
return bytes[start];
}
public static BerL read(byte[] bytes, int start) {
return new BerL(Arrays.copyOfRange(bytes, start,
start + test(bytes, start)));
}
public BerL(byte[] bytes) {
super(bytes);
val = calc(bytes, 0);
}
public int toInt() {
return val;
}
}
public final static class BerV extends Iso7816 {
public static BerV read(byte[] bytes, int start, int len) {
return new BerV(Arrays.copyOfRange(bytes, start, start + len));
}
public BerV(byte[] bytes) {
super(bytes);
}
}
public final static class BerTLV extends Iso7816 {
public static int test(byte[] bytes, int start) {
final int lt = BerT.test(bytes, start);
final int ll = BerL.test(bytes, start + lt);
final int lv = BerL.calc(bytes, start + lt);
return lt + ll + lv;
}
public static BerTLV read(Iso7816 obj) {
return read(obj.getBytes(), 0);
}
public static BerTLV read(byte[] bytes, int start) {
int s = start;
final BerT t = BerT.read(bytes, s);
s += t.size();
final BerL l = BerL.read(bytes, s);
s += l.size();
final BerV v = BerV.read(bytes, s, l.toInt());
s += v.size();
final BerTLV tlv = new BerTLV(t, l, v);
tlv.data = Arrays.copyOfRange(bytes, start, s);
return tlv;
}
public static ArrayList<BerTLV> readList(Iso7816 obj) {
return readList(obj.getBytes());
}
public static ArrayList<BerTLV> readList(final byte[] data) {
final ArrayList<BerTLV> ret = new ArrayList<BerTLV>();
int start = 0;
int end = data.length - 3;
while (start < end) {
final BerTLV tlv = read(data, start);
ret.add(tlv);
start += tlv.size();
}
return ret;
}
public final BerT t;
public final BerL l;
public final BerV v;
public BerTLV(BerT t, BerL l, BerV v) {
this.t = t;
this.l = l;
this.v = v;
}
public BerTLV getChildByTag(BerT tag) {
if (t.hasChild()) {
final byte[] raw = v.getBytes();
int start = 0;
int end = raw.length;
while (start < end) {
if (tag.match(raw, start))
return read(raw, start);
start += test(raw, start);
}
}
return null;
}
public BerTLV getChild(int index) {
if (t.hasChild()) {
final byte[] raw = v.getBytes();
int start = 0;
int end = raw.length;
int i = 0;
while (start < end) {
if (i++ == index)
return read(raw, start);
start += test(raw, start);
}
}
return null;
}
}
public final static class Tag {
private final IsoDep nfcTag;
private ID id;
public Tag(IsoDep tag) {
nfcTag = tag;
id = new ID(tag.getTag().getId());
}
public ID getID() {
return id;
}
public Response verify() {
final byte[] cmd = { (byte) 0x00, // CLA Class
(byte) 0x20, // INS Instruction
(byte) 0x00, // P1 Parameter 1
(byte) 0x00, // P2 Parameter 2
(byte) 0x02, // Lc
(byte) 0x12, (byte) 0x34, };
return new Response(transceive(cmd));
}
public Response initPurchase(boolean isEP) {
final byte[] cmd = {
(byte) 0x80, // CLA Class
(byte) 0x50, // INS Instruction
(byte) 0x01, // P1 Parameter 1
(byte) (isEP ? 2 : 1), // P2 Parameter 2
(byte) 0x0B, // Lc
(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x11, (byte) 0x22, (byte) 0x33,
(byte) 0x44, (byte) 0x55, (byte) 0x66, (byte) 0x0F, // Le
};
return new Response(transceive(cmd));
}
public Response getBalance(boolean isEP) {
final byte[] cmd = { (byte) 0x80, // CLA Class
(byte) 0x5C, // INS Instruction
(byte) 0x00, // P1 Parameter 1
(byte) (isEP ? 2 : 1), // P2 Parameter 2
(byte) 0x04, // Le
};
return new Response(transceive(cmd));
}
public Response readRecord(int sfi, int index) {
final byte[] cmd = { (byte) 0x00, // CLA Class
(byte) 0xB2, // INS Instruction
(byte) index, // P1 Parameter 1
(byte) ((sfi << 3) | 0x04), // P2 Parameter 2
(byte) 0x00, // Le
};
return new Response(transceive(cmd));
}
public Response readRecord(int sfi) {
final byte[] cmd = { (byte) 0x00, // CLA Class
(byte) 0xB2, // INS Instruction
(byte) 0x01, // P1 Parameter 1
(byte) ((sfi << 3) | 0x05), // P2 Parameter 2
(byte) 0x00, // Le
};
return new Response(transceive(cmd));
}
public Response readBinary(int sfi) {
final byte[] cmd = { (byte) 0x00, // CLA Class
(byte) 0xB0, // INS Instruction
(byte) (0x00000080 | (sfi & 0x1F)), // P1 Parameter 1
(byte) 0x00, // P2 Parameter 2
(byte) 0x00, // Le
};
return new Response(transceive(cmd));
}
public Response readData(int sfi) {
final byte[] cmd = { (byte) 0x80, // CLA Class
(byte) 0xCA, // INS Instruction
(byte) 0x00, // P1 Parameter 1
(byte) (sfi & 0x1F), // P2 Parameter 2
(byte) 0x00, // Le
};
return new Response(transceive(cmd));
}
public Response selectByID(byte... name) {
ByteBuffer buff = ByteBuffer.allocate(name.length + 6);
buff.put((byte) 0x00) // CLA Class
.put((byte) 0xA4) // INS Instruction
.put((byte) 0x00) // P1 Parameter 1
.put((byte) 0x00) // P2 Parameter 2
.put((byte) name.length) // Lc
.put(name).put((byte) 0x00); // Le
return new Response(transceive(buff.array()));
}
public Response selectByName(byte... name) {
ByteBuffer buff = ByteBuffer.allocate(name.length + 6);
buff.put((byte) 0x00) // CLA Class
.put((byte) 0xA4) // INS Instruction
.put((byte) 0x04) // P1 Parameter 1
.put((byte) 0x00) // P2 Parameter 2
.put((byte) name.length) // Lc
.put(name).put((byte) 0x00); // Le
return new Response(transceive(buff.array()));
}
public void connect() {
try {
nfcTag.connect();
} catch (Exception e) {
}
}
public void close() {
try {
nfcTag.close();
} catch (Exception e) {
}
}
public byte[] transceive(final byte[] cmd) {
try {
return nfcTag.transceive(cmd);
} catch (Exception e) {
return Response.ERROR;
}
}
}
public static final short SW_NO_ERROR = (short) 0x9000;
public static final short SW_BYTES_REMAINING_00 = 0x6100;
public static final short SW_WRONG_LENGTH = 0x6700;
public static final short SW_SECURITY_STATUS_NOT_SATISFIED = 0x6982;
public static final short SW_FILE_INVALID = 0x6983;
public static final short SW_DATA_INVALID = 0x6984;
public static final short SW_CONDITIONS_NOT_SATISFIED = 0x6985;
public static final short SW_COMMAND_NOT_ALLOWED = 0x6986;
public static final short SW_APPLET_SELECT_FAILED = 0x6999;
public static final short SW_WRONG_DATA = 0x6A80;
public static final short SW_FUNC_NOT_SUPPORTED = 0x6A81;
public static final short SW_FILE_NOT_FOUND = 0x6A82;
public static final short SW_RECORD_NOT_FOUND = 0x6A83;
public static final short SW_INCORRECT_P1P2 = 0x6A86;
public static final short SW_WRONG_P1P2 = 0x6B00;
public static final short SW_CORRECT_LENGTH_00 = 0x6C00;
public static final short SW_INS_NOT_SUPPORTED = 0x6D00;
public static final short SW_CLA_NOT_SUPPORTED = 0x6E00;
public static final short SW_UNKNOWN = 0x6F00;
public static final short SW_FILE_FULL = 0x6A84;
}