Compare commits
9
Commits
bls-support
..
ident
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31f4ab5a90 | ||
|
|
953c84514a | ||
|
|
2a55211d1c | ||
|
|
2926d032c1 | ||
|
|
66be965c6a | ||
|
|
482215a487 | ||
|
|
e76e608795 | ||
|
|
bbcce01742 | ||
|
|
a39924aba3 |
@@ -32,6 +32,18 @@ public class CashCommandSet {
|
||||
return apduChannel.send(selectApplet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an IDENTIFY CARD APDU. The challenge is sent as APDU data as-is. It must be 32 bytes long
|
||||
*
|
||||
* @param challenge the data of the APDU
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse identifyCard(byte[] challenge) throws IOException {
|
||||
APDUCommand identifyCard = new APDUCommand(0x80, KeycardCommandSet.INS_IDENTIFY_CARD, 0, 0, challenge);
|
||||
return apduChannel.send(identifyCard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a SIGN APDU.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package im.status.keycard.applet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
|
||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
||||
import org.bouncycastle.jce.interfaces.ECPublicKey;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.jce.interfaces.ECPrivateKey;
|
||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
|
||||
public class Certificate extends RecoverableSignature {
|
||||
public static final byte TLV_CERT = (byte) 0x8A;
|
||||
|
||||
private byte[] identPriv;
|
||||
private byte[] identPub;
|
||||
|
||||
public Certificate(byte[] publicKey, boolean compressed, byte[] r, byte[] s, int recId) {
|
||||
super(publicKey, compressed,r, s, recId);
|
||||
}
|
||||
|
||||
public static KeyPair generateIdentKeyPair() {
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", "BC");
|
||||
ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
|
||||
keyPairGenerator.initialize(spec, new SecureRandom());
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
} catch(Exception e) {
|
||||
throw new RuntimeException("Is BouncyCastle in the classpath?");
|
||||
}
|
||||
}
|
||||
|
||||
public static Certificate createCertificate(KeyPair caPair, KeyPair identKeys) {
|
||||
try {
|
||||
byte[] pub = ((ECPublicKey) identKeys.getPublic()).getQ().getEncoded(true);
|
||||
|
||||
MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
|
||||
byte[] hash = md.digest(pub);
|
||||
|
||||
Signature signer = Signature.getInstance("NONEwithECDSA", "BC");
|
||||
signer.initSign(caPair.getPrivate());
|
||||
signer.update(hash);
|
||||
byte[] sig = signer.sign();
|
||||
|
||||
TinyBERTLV tlv = new TinyBERTLV(sig);
|
||||
tlv.enterConstructed(TLV_ECDSA_TEMPLATE);
|
||||
byte[] r = toUInt(tlv.readPrimitive(TinyBERTLV.TLV_INT));
|
||||
byte[] s = toUInt(tlv.readPrimitive(TinyBERTLV.TLV_INT));
|
||||
Certificate cert = new Certificate(((ECPublicKey)caPair.getPublic()).getQ().getEncoded(true), true, r, s, -1);
|
||||
cert.calculateRecID(hash);
|
||||
cert.identPriv = toUInt(((ECPrivateKey) identKeys.getPrivate()).getD().toByteArray());
|
||||
cert.identPub = pub;
|
||||
|
||||
return cert;
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch(Exception e) {
|
||||
throw new RuntimeException("Is BouncyCastle in the classpath?");
|
||||
}
|
||||
}
|
||||
|
||||
public static Certificate generateNewCertificate(KeyPair caPair) {
|
||||
return createCertificate(caPair, generateIdentKeyPair());
|
||||
}
|
||||
|
||||
public static Certificate fromTLV(byte[] certData) {
|
||||
try {
|
||||
byte[] pub = Arrays.copyOfRange(certData, 0, 33);
|
||||
byte[] r = Arrays.copyOfRange(certData, 33, 65);
|
||||
byte[] s = Arrays.copyOfRange(certData, 65, 97);
|
||||
int recId = certData[97];
|
||||
|
||||
MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
|
||||
byte[] hash = md.digest(pub);
|
||||
byte[] caPub = recoverFromSignature(recId, hash, r, s, true);
|
||||
|
||||
Certificate cert = new Certificate(caPub, true, r, s, recId);
|
||||
cert.identPub = pub;
|
||||
|
||||
return cert;
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch(Exception e) {
|
||||
throw new RuntimeException("Is BouncyCastle in the classpath?");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] verifyIdentity(byte[] hash, byte[] tlvData) {
|
||||
try {
|
||||
TinyBERTLV tlv = new TinyBERTLV(tlvData);
|
||||
tlv.enterConstructed(TLV_SIGNATURE_TEMPLATE);
|
||||
byte[] certData = tlv.readPrimitive(TLV_CERT);
|
||||
Certificate cert = fromTLV(certData);
|
||||
byte[] signature = tlv.peekUnread();
|
||||
Signature verifier = Signature.getInstance("NONEWithECDSA", "BC");
|
||||
|
||||
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
ECPublicKeySpec cardKeySpec = new ECPublicKeySpec(ecSpec.getCurve().decodePoint(cert.identPub), ecSpec);
|
||||
ECPublicKey cardKey = (ECPublicKey) KeyFactory.getInstance("ECDSA", "BC").generatePublic(cardKeySpec);
|
||||
|
||||
verifier.initVerify(cardKey);
|
||||
verifier.update(hash);
|
||||
|
||||
if (!verifier.verify(signature)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cert.getPublicKey();
|
||||
} catch(Exception e) {
|
||||
throw new RuntimeException("Is BouncyCastle in the classpath?");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toStoreData() {
|
||||
if (identPriv == null) {
|
||||
throw new IllegalStateException("The private key must be set.");
|
||||
}
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
os.write(this.identPub);
|
||||
os.write(this.getR());
|
||||
os.write(this.getS());
|
||||
os.write(this.getRecId());
|
||||
os.write(this.identPriv);
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return os.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package im.status.keycard.applet;
|
||||
|
||||
import im.status.keycard.io.APDUCommand;
|
||||
import im.status.keycard.io.APDUResponse;
|
||||
import im.status.keycard.io.CardChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Command set for the Ident applet.
|
||||
*/
|
||||
public class IdentCommandSet {
|
||||
private final CardChannel apduChannel;
|
||||
|
||||
/**
|
||||
* Creates a IdentCommandSet using the given APDU Channel
|
||||
* @param apduChannel APDU channel
|
||||
*/
|
||||
public IdentCommandSet(CardChannel apduChannel) {
|
||||
this.apduChannel = apduChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a Cash instance. The applet is assumed to have been installed with its default AID. The returned data is
|
||||
* a public key which must be used to initialize the secure channel.
|
||||
*
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse select() throws IOException {
|
||||
APDUCommand selectApplet = new APDUCommand(0x00, 0xA4, 4, 0, Identifiers.IDENT_INSTANCE_AID);
|
||||
return apduChannel.send(selectApplet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a STORE DATA APDU.
|
||||
*
|
||||
* @param data the data to sign
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse storeData(byte[] data) throws IOException {
|
||||
APDUCommand sign = new APDUCommand(0x80, KeycardCommandSet.INS_STORE_DATA, 0x00, 0x00, data);
|
||||
return apduChannel.send(sign);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@ public class Identifiers {
|
||||
public static final byte[] CASH_AID = Hex.decode("A000000804000103");
|
||||
public static final byte[] CASH_INSTANCE_AID = Hex.decode("A00000080400010301");
|
||||
|
||||
public static final byte[] IDENT_AID = Hex.decode("A000000804000104");
|
||||
public static final byte[] IDENT_INSTANCE_AID = Hex.decode("A00000080400010401");
|
||||
|
||||
/**
|
||||
* Gets the instance AID of the default instance of the Keycard applet.
|
||||
*
|
||||
|
||||
@@ -20,6 +20,7 @@ public class KeycardCommandSet {
|
||||
static final byte INS_INIT = (byte) 0xFE;
|
||||
static final byte INS_GET_STATUS = (byte) 0xF2;
|
||||
static final byte INS_SET_NDEF = (byte) 0xF3;
|
||||
static final byte INS_IDENTIFY_CARD = (byte) 0x14;
|
||||
static final byte INS_VERIFY_PIN = (byte) 0x20;
|
||||
static final byte INS_CHANGE_PIN = (byte) 0x21;
|
||||
static final byte INS_UNBLOCK_PIN = (byte) 0x22;
|
||||
@@ -269,6 +270,18 @@ public class KeycardCommandSet {
|
||||
secureChannel.unpairOthers(apduChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an IDENTIFY CARD APDU. The challenge is sent as APDU data as-is. It must be 32 bytes long
|
||||
*
|
||||
* @param challenge the data of the APDU
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse identifyCard(byte[] challenge) throws IOException {
|
||||
APDUCommand identifyCard = secureChannel.protectedCommand(0x80, INS_IDENTIFY_CARD, 0, 0, challenge);
|
||||
return secureChannel.transmit(apduChannel, identifyCard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a GET STATUS APDU. The info byte is the P1 parameter of the command, valid constants are defined in the applet
|
||||
* class itself.
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package im.status.keycard.applet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class Metadata {
|
||||
private String cardName;
|
||||
private SortedSet<Long> wallets;
|
||||
|
||||
public static Metadata fromData(byte[] data) {
|
||||
int version = (data[0] & 0xe0) >> 5;
|
||||
|
||||
if (version != 1) {
|
||||
throw new RuntimeException("Invalid version");
|
||||
}
|
||||
|
||||
int namelen = (data[0] & 0x1f);
|
||||
int off = 1;
|
||||
|
||||
String cardName = new String(data, off, namelen, Charset.forName("US-ASCII"));
|
||||
off += namelen;
|
||||
|
||||
SortedSet<Long> set = new TreeSet<>();
|
||||
|
||||
while(off < data.length) {
|
||||
int[] start = TinyBERTLV.readNum(data, off);
|
||||
int[] count = TinyBERTLV.readNum(data, start[1]);
|
||||
off = count[1];
|
||||
long s = start[0] & 0xffffffffl;
|
||||
buildRange(set, s, (s + count[0]));
|
||||
}
|
||||
|
||||
return new Metadata(cardName, set);
|
||||
}
|
||||
|
||||
private static void buildRange(SortedSet<Long> set, long start, long end) {
|
||||
for (long i = start; i <= end; i++) {
|
||||
set.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
Metadata(String cardName, SortedSet<Long> wallets) {
|
||||
this.cardName = cardName;
|
||||
this.wallets = wallets;
|
||||
}
|
||||
|
||||
public Metadata(String cardName) {
|
||||
this(cardName, new TreeSet<>());
|
||||
}
|
||||
|
||||
public String getCardName() {
|
||||
return cardName;
|
||||
}
|
||||
|
||||
public void setCardName(String cardName) {
|
||||
if (cardName.length() > 20) {
|
||||
throw new IllegalArgumentException("card name too long");
|
||||
}
|
||||
|
||||
this.cardName = cardName;
|
||||
}
|
||||
|
||||
public SortedSet<Long> getWallets() {
|
||||
return wallets;
|
||||
}
|
||||
|
||||
public void addWallet(long w) {
|
||||
this.wallets.add(w);
|
||||
}
|
||||
|
||||
public void removeWallet(long w) {
|
||||
this.wallets.remove(w);
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
byte[] name = this.cardName.getBytes(Charset.forName("US-ASCII"));
|
||||
os.write(0x20 | name.length);
|
||||
os.write(name, 0, name.length);
|
||||
|
||||
if (wallets.isEmpty()) {
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
long start = wallets.first();
|
||||
int len = 0;
|
||||
|
||||
for (Long w : wallets.tailSet(start + 1)) {
|
||||
if (w == (start + len + 1)) {
|
||||
len++;
|
||||
} else {
|
||||
TinyBERTLV.writeNum(os, (int) start);
|
||||
TinyBERTLV.writeNum(os, len);
|
||||
len = 0;
|
||||
start = w;
|
||||
}
|
||||
}
|
||||
|
||||
TinyBERTLV.writeNum(os, (int) start);
|
||||
TinyBERTLV.writeNum(os, len);
|
||||
|
||||
return os.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public class RecoverableSignature {
|
||||
private int recId;
|
||||
private byte[] r;
|
||||
private byte[] s;
|
||||
private boolean compressed;
|
||||
|
||||
public static final byte TLV_SIGNATURE_TEMPLATE = (byte) 0xA0;
|
||||
public static final byte TLV_ECDSA_TEMPLATE = (byte) 0x30;
|
||||
@@ -41,15 +42,28 @@ public class RecoverableSignature {
|
||||
public RecoverableSignature(byte[] hash, byte[] tlvData) {
|
||||
TinyBERTLV tlv = new TinyBERTLV(tlvData);
|
||||
tlv.enterConstructed(TLV_SIGNATURE_TEMPLATE);
|
||||
publicKey = tlv.readPrimitive(ApplicationInfo.TLV_PUB_KEY);
|
||||
this.publicKey = tlv.readPrimitive(ApplicationInfo.TLV_PUB_KEY);
|
||||
tlv.enterConstructed(TLV_ECDSA_TEMPLATE);
|
||||
r = toUInt(tlv.readPrimitive(TinyBERTLV.TLV_INT));
|
||||
s = toUInt(tlv.readPrimitive(TinyBERTLV.TLV_INT));
|
||||
this.r = toUInt(tlv.readPrimitive(TinyBERTLV.TLV_INT));
|
||||
this.s = toUInt(tlv.readPrimitive(TinyBERTLV.TLV_INT));
|
||||
this.compressed = false;
|
||||
|
||||
calculateRecID(hash);
|
||||
}
|
||||
|
||||
public RecoverableSignature(byte[] publicKey, boolean compressed, byte[] r, byte[] s, int recId) {
|
||||
this.publicKey = publicKey;
|
||||
this.r = r;
|
||||
this.s = s;
|
||||
this.compressed = compressed;
|
||||
this.recId = recId;
|
||||
}
|
||||
|
||||
void calculateRecID(byte[] hash) {
|
||||
recId = -1;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
byte[] candidate = recoverFromSignature(i, new BigInteger(1, hash), new BigInteger(1, r), new BigInteger(1, s));
|
||||
byte[] candidate = recoverFromSignature(i, hash, r, s, compressed);
|
||||
|
||||
if (Arrays.equals(candidate, publicKey)) {
|
||||
recId = i;
|
||||
@@ -62,7 +76,7 @@ public class RecoverableSignature {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] toUInt(byte[] signedInt) {
|
||||
static byte[] toUInt(byte[] signedInt) {
|
||||
if (signedInt[0] == 0) {
|
||||
return Arrays.copyOfRange(signedInt, 1, signedInt.length);
|
||||
} else {
|
||||
@@ -114,7 +128,15 @@ public class RecoverableSignature {
|
||||
return Ethereum.toEthereumAddress(publicKey);
|
||||
}
|
||||
|
||||
private static byte[] recoverFromSignature(int recId, BigInteger e, BigInteger r, BigInteger s) {
|
||||
static byte[] recoverFromSignature(int recId, byte[] hash, byte[] r, byte[] s, boolean compressed) {
|
||||
BigInteger h = new BigInteger(1, hash);
|
||||
BigInteger br = new BigInteger(1, r);
|
||||
BigInteger bs = new BigInteger(1, s);
|
||||
|
||||
return recoverFromSignature(recId, h, br, bs, compressed);
|
||||
}
|
||||
|
||||
static byte[] recoverFromSignature(int recId, BigInteger e, BigInteger r, BigInteger s, boolean compressed) {
|
||||
BigInteger n = CURVE.getN();
|
||||
BigInteger i = BigInteger.valueOf((long) recId / 2);
|
||||
BigInteger x = r.add(i.multiply(n));
|
||||
@@ -135,7 +157,7 @@ public class RecoverableSignature {
|
||||
BigInteger srInv = rInv.multiply(s).mod(n);
|
||||
BigInteger eInvrInv = rInv.multiply(eInv).mod(n);
|
||||
ECPoint q = ECAlgorithms.sumOfTwoMultiplies(CURVE.getG(), eInvrInv, R, srInv);
|
||||
return q.getEncoded(false);
|
||||
return q.getEncoded(compressed);
|
||||
}
|
||||
|
||||
private static ECPoint decompressKey(BigInteger xBN, boolean yBit) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package im.status.keycard.applet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,57 @@ public class TinyBERTLV {
|
||||
private byte[] buffer;
|
||||
private int pos;
|
||||
|
||||
public static int[] readNum(byte[] buf, int off) {
|
||||
int len = buf[off++] & 0xff;
|
||||
int lenlen = 0;
|
||||
|
||||
if ((len & 0x80) == 0x80) {
|
||||
lenlen = len & 0x7f;
|
||||
len = readVal(buf, off, lenlen);
|
||||
}
|
||||
|
||||
return new int[] {len, off + lenlen};
|
||||
}
|
||||
|
||||
public static int readVal(byte[] val, int off, int len) {
|
||||
switch (len) {
|
||||
case 1:
|
||||
return val[off] & 0xff;
|
||||
case 2:
|
||||
return ((val[off] & 0xff) << 8) | (val[off+1] & 0xff);
|
||||
case 3:
|
||||
return ((val[off] & 0xff) << 16) | ((val[off+1] & 0xff) << 8) | (val[off+2] & 0xff);
|
||||
case 4:
|
||||
return ((val[off] & 0xff) << 24) | ((val[off+1] & 0xff) << 16) | ((val[off+2] & 0xff) << 8) | (val[off+3] & 0xff);
|
||||
default:
|
||||
throw new IllegalArgumentException("Integers of length " + len + " are unsupported");
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeNum(ByteArrayOutputStream os, int len) {
|
||||
if ((len & 0xff000000) != 0) {
|
||||
os.write(0x84);
|
||||
os.write((len & 0xff000000) >> 24);
|
||||
os.write((len & 0x00ff0000) >> 16);
|
||||
os.write((len & 0x0000ff00) >> 8);
|
||||
os.write(len & 0x000000ff);
|
||||
} else if ((len & 0x00ff0000) != 0) {
|
||||
os.write(0x83);
|
||||
os.write((len & 0x00ff0000) >> 16);
|
||||
os.write((len & 0x0000ff00) >> 8);
|
||||
os.write(len & 0x000000ff);
|
||||
} else if ((len & 0x0000ff00) != 0) {
|
||||
os.write(0x82);
|
||||
os.write((len & 0x0000ff00) >> 8);
|
||||
os.write(len & 0x000000ff);
|
||||
} else if ((len & 0x00000080) != 0) {
|
||||
os.write(0x81);
|
||||
os.write(len & 0x000000ff);
|
||||
} else {
|
||||
os.write(len);
|
||||
}
|
||||
}
|
||||
|
||||
public TinyBERTLV(byte[] buffer) {
|
||||
this.buffer = buffer;
|
||||
this.pos = 0;
|
||||
@@ -64,19 +116,16 @@ public class TinyBERTLV {
|
||||
*/
|
||||
public int readInt() throws IllegalArgumentException {
|
||||
byte[] val = readPrimitive(TLV_INT);
|
||||
return TinyBERTLV.readVal(val, 0, val.length);
|
||||
}
|
||||
|
||||
switch (val.length) {
|
||||
case 1:
|
||||
return val[0] & 0xff;
|
||||
case 2:
|
||||
return ((val[0] & 0xff) << 8) | (val[1] & 0xff);
|
||||
case 3:
|
||||
return ((val[0] & 0xff) << 16) | ((val[1] & 0xff) << 8) | (val[2] & 0xff);
|
||||
case 4:
|
||||
return ((val[0] & 0xff) << 24) | ((val[1] & 0xff) << 16) | ((val[2] & 0xff) << 8) | (val[3] & 0xff);
|
||||
default:
|
||||
throw new IllegalArgumentException("Integers of length " + val.length + " are unsupported");
|
||||
}
|
||||
/**
|
||||
* Returns all unread bytes in the TLV.
|
||||
*
|
||||
* @return all unread bytes
|
||||
*/
|
||||
byte[] peekUnread() {
|
||||
return Arrays.copyOfRange(buffer, pos, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,13 +153,9 @@ public class TinyBERTLV {
|
||||
* @return the tag
|
||||
*/
|
||||
public int readLength() {
|
||||
int len = buffer[pos++] & 0xff;
|
||||
|
||||
if (len == 0x81) {
|
||||
len = buffer[pos++] & 0xff;
|
||||
}
|
||||
|
||||
return len;
|
||||
int[] len = TinyBERTLV.readNum(buffer, pos);
|
||||
pos = len[1];
|
||||
return len[0];
|
||||
}
|
||||
|
||||
private void checkTag(int expected, int actual) throws IllegalArgumentException {
|
||||
|
||||
@@ -250,6 +250,16 @@ public class GlobalPlatformCommandSet {
|
||||
return delete(Identifiers.NDEF_INSTANCE_AID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the Ident applet instance.
|
||||
*
|
||||
* @return the card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse deleteIdentInstance() throws IOException {
|
||||
return delete(Identifiers.IDENT_INSTANCE_AID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the Keycard package.
|
||||
*
|
||||
@@ -271,6 +281,7 @@ public class GlobalPlatformCommandSet {
|
||||
deleteNDEFInstance().checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
|
||||
deleteKeycardInstance().checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
|
||||
deleteCashInstance().checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
|
||||
deleteIdentInstance().checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
|
||||
deleteKeycardPackage().checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -448,4 +459,14 @@ public class GlobalPlatformCommandSet {
|
||||
public APDUResponse installCashApplet() throws IOException {
|
||||
return installCashApplet(new byte[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the Ident applet.
|
||||
*
|
||||
* @return the card response
|
||||
* @throws IOException communication error.
|
||||
*/
|
||||
public APDUResponse installIdentApplet() throws IOException {
|
||||
return installForInstall(Identifiers.PACKAGE_AID, Identifiers.IDENT_AID, Identifiers.IDENT_INSTANCE_AID, new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user