add data in cash applet
This commit is contained in:
parent
1b716790f0
commit
4cc3f1576c
|
@ -9,7 +9,7 @@ buildscript {
|
|||
|
||||
dependencies {
|
||||
classpath 'com.fidesmo:gradle-javacard:0.2.7'
|
||||
classpath 'com.github.status-im.status-keycard-java:desktop:4a69788'
|
||||
classpath 'com.github.status-im.status-keycard-java:desktop:8cb43e6'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ javacard {
|
|||
aid = '0xA0:0x00:0x00:0x08:0x04:0x00:0x01:0x03'
|
||||
className = 'CashApplet'
|
||||
}
|
||||
version = '2.3'
|
||||
version = '3.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ dependencies {
|
|||
testCompile(files("../jcardsim/jcardsim-3.0.5-SNAPSHOT.jar"))
|
||||
testCompile('org.web3j:core:2.3.1')
|
||||
testCompile('org.bitcoinj:bitcoinj-core:0.14.5')
|
||||
testCompile('com.github.status-im.status-keycard-java:desktop:4a69788')
|
||||
testCompile('com.github.status-im.status-keycard-java:desktop:8cb43e6')
|
||||
testCompile('org.bouncycastle:bcprov-jdk15on:1.60')
|
||||
testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
|
||||
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
|
||||
|
|
|
@ -4,5 +4,5 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.github.status-im.status-keycard-java:desktop:4a69788'
|
||||
compile 'com.github.status-im.status-keycard-java:desktop:8cb43e6'
|
||||
}
|
|
@ -5,6 +5,7 @@ import javacard.security.*;
|
|||
|
||||
public class CashApplet extends Applet {
|
||||
private static final short SIGN_OUT_OFF = ISO7816.OFFSET_CDATA + MessageDigest.LENGTH_SHA_256;
|
||||
private static final byte TLV_PUB_DATA = (byte) 0x82;
|
||||
|
||||
private KeyPair keypair;
|
||||
private ECPublicKey publicKey;
|
||||
|
@ -51,6 +52,14 @@ public class CashApplet extends Applet {
|
|||
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
|
||||
signature.init(privateKey, Signature.MODE_SIGN);
|
||||
|
||||
short c9Off = (short)(bOffset + bArray[bOffset] + 1); // Skip AID
|
||||
c9Off += (short)(bArray[c9Off] + 1); // Skip Privileges and parameter length
|
||||
|
||||
short dataLen = Util.makeShort((byte) 0x00, bArray[c9Off]);
|
||||
if (dataLen > 0) {
|
||||
Util.arrayCopyNonAtomic(bArray, c9Off, SharedMemory.cashDataFile, (short) 0, (short)(dataLen + 1));
|
||||
}
|
||||
|
||||
register(bArray, (short) (bOffset + 1), bArray[bOffset]);
|
||||
}
|
||||
|
||||
|
@ -99,6 +108,11 @@ public class CashApplet extends Applet {
|
|||
Util.setShort(apduBuffer, off, KeycardApplet.APPLICATION_VERSION);
|
||||
off += 2;
|
||||
|
||||
apduBuffer[off++] = TLV_PUB_DATA;
|
||||
apduBuffer[off++] = SharedMemory.cashDataFile[0];
|
||||
Util.arrayCopyNonAtomic(SharedMemory.cashDataFile, (short) 1, apduBuffer, off, SharedMemory.cashDataFile[0]);
|
||||
off += SharedMemory.cashDataFile[0];
|
||||
|
||||
apduBuffer[lenoff] = (byte)(off - lenoff - 1);
|
||||
apdu.setOutgoingAndSend((short) 0, off);
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@ import javacard.framework.*;
|
|||
import javacard.security.*;
|
||||
import javacardx.crypto.Cipher;
|
||||
|
||||
import static javacard.framework.ISO7816.OFFSET_P1;
|
||||
|
||||
/**
|
||||
* The applet's main class. All incoming commands a processed by this class.
|
||||
*/
|
||||
public class KeycardApplet extends Applet {
|
||||
static final short APPLICATION_VERSION = (short) 0x0203;
|
||||
static final short APPLICATION_VERSION = (short) 0x0300;
|
||||
|
||||
static final byte INS_GET_STATUS = (byte) 0xF2;
|
||||
static final byte INS_SET_NDEF = (byte) 0xF3;
|
||||
static final byte INS_INIT = (byte) 0xFE;
|
||||
static final byte INS_VERIFY_PIN = (byte) 0x20;
|
||||
static final byte INS_CHANGE_PIN = (byte) 0x21;
|
||||
|
@ -74,6 +75,10 @@ public class KeycardApplet extends Applet {
|
|||
static final byte EXPORT_KEY_P2_PRIVATE_AND_PUBLIC = 0x00;
|
||||
static final byte EXPORT_KEY_P2_PUBLIC_ONLY = 0x01;
|
||||
|
||||
static final byte STORE_DATA_P1_PUBLIC = 0x00;
|
||||
static final byte STORE_DATA_P1_NDEF = 0x01;
|
||||
static final byte STORE_DATA_P1_CASH = 0x02;
|
||||
|
||||
static final byte TLV_SIGNATURE_TEMPLATE = (byte) 0xA0;
|
||||
|
||||
static final byte TLV_KEY_TEMPLATE = (byte) 0xA1;
|
||||
|
@ -239,9 +244,6 @@ public class KeycardApplet extends Applet {
|
|||
case INS_GET_STATUS:
|
||||
getStatus(apdu);
|
||||
break;
|
||||
case INS_SET_NDEF:
|
||||
setNDEF(apdu);
|
||||
break;
|
||||
case INS_VERIFY_PIN:
|
||||
verifyPIN(apdu);
|
||||
break;
|
||||
|
@ -434,9 +436,9 @@ public class KeycardApplet extends Applet {
|
|||
|
||||
short len;
|
||||
|
||||
if (apduBuffer[ISO7816.OFFSET_P1] == GET_STATUS_P1_APPLICATION) {
|
||||
if (apduBuffer[OFFSET_P1] == GET_STATUS_P1_APPLICATION) {
|
||||
len = getApplicationStatus(apduBuffer, SecureChannel.SC_OUT_OFFSET);
|
||||
} else if (apduBuffer[ISO7816.OFFSET_P1] == GET_STATUS_P1_KEY_PATH) {
|
||||
} else if (apduBuffer[OFFSET_P1] == GET_STATUS_P1_KEY_PATH) {
|
||||
len = getKeyStatus(apduBuffer, SecureChannel.SC_OUT_OFFSET);
|
||||
} else {
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
|
@ -446,36 +448,6 @@ public class KeycardApplet extends Applet {
|
|||
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content of the NDEF data file returned by the NDEF applet. Requires a secure channel to be already open
|
||||
* and the PIN to be verified.
|
||||
*
|
||||
* @param apdu the JCRE-owned APDU object.
|
||||
*/
|
||||
private void setNDEF(APDU apdu) {
|
||||
byte[] apduBuffer = apdu.getBuffer();
|
||||
secureChannel.preprocessAPDU(apduBuffer);
|
||||
|
||||
if (!pin.isValidated()) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
short dataLen = Util.makeShort((byte) 0x00, apduBuffer[ISO7816.OFFSET_LC]);
|
||||
short offset;
|
||||
|
||||
if (Util.makeShort(apduBuffer[ISO7816.OFFSET_CDATA], apduBuffer[(short)(ISO7816.OFFSET_CDATA + 1)]) != (short)(dataLen - 2)) {
|
||||
offset = ISO7816.OFFSET_P2;
|
||||
apduBuffer[ISO7816.OFFSET_P2] = 0;
|
||||
dataLen += 2;
|
||||
} else {
|
||||
offset = ISO7816.OFFSET_CDATA;
|
||||
}
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
Util.arrayCopy(apduBuffer, offset, SharedMemory.ndefDataFile, (short) 0, dataLen);
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the Application Status Template to the APDU buffer. Invoked internally by the getStatus method. This
|
||||
* template is useful to understand if the card is blocked, if it has valid keys and if public key derivation is
|
||||
|
@ -550,7 +522,7 @@ public class KeycardApplet extends Applet {
|
|||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
switch(apduBuffer[ISO7816.OFFSET_P1]) {
|
||||
switch(apduBuffer[OFFSET_P1]) {
|
||||
case CHANGE_PIN_P1_USER_PIN:
|
||||
changeUserPIN(apduBuffer, len);
|
||||
break;
|
||||
|
@ -653,7 +625,7 @@ public class KeycardApplet extends Applet {
|
|||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
switch (apduBuffer[ISO7816.OFFSET_P1]) {
|
||||
switch (apduBuffer[OFFSET_P1]) {
|
||||
case LOAD_KEY_P1_EC:
|
||||
case LOAD_KEY_P1_EXT_EC:
|
||||
loadKeyPair(apduBuffer);
|
||||
|
@ -814,7 +786,7 @@ public class KeycardApplet extends Applet {
|
|||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
doDerive(apduBuffer, (short) 0, len, apduBuffer[ISO7816.OFFSET_P1], true);
|
||||
doDerive(apduBuffer, (short) 0, len, apduBuffer[OFFSET_P1], true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -968,7 +940,7 @@ public class KeycardApplet extends Applet {
|
|||
byte[] apduBuffer = apdu.getBuffer();
|
||||
secureChannel.preprocessAPDU(apduBuffer);
|
||||
|
||||
short csLen = apduBuffer[ISO7816.OFFSET_P1];
|
||||
short csLen = apduBuffer[OFFSET_P1];
|
||||
|
||||
if (csLen < GENERATE_MNEMONIC_P1_CS_MIN || csLen > GENERATE_MNEMONIC_P1_CS_MAX) {
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
|
@ -1105,7 +1077,7 @@ public class KeycardApplet extends Applet {
|
|||
ECPrivateKey signingKey;
|
||||
ECPublicKey outputKey;
|
||||
|
||||
switch((byte) (apduBuffer[ISO7816.OFFSET_P1] & ~DERIVE_P1_SOURCE_MASK)) {
|
||||
switch((byte) (apduBuffer[OFFSET_P1] & ~DERIVE_P1_SOURCE_MASK)) {
|
||||
case SIGN_P1_CURRENT_KEY:
|
||||
signingKey = privateKey;
|
||||
outputKey = publicKey;
|
||||
|
@ -1154,7 +1126,7 @@ public class KeycardApplet extends Applet {
|
|||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
byte derivationSource = (byte) (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_SOURCE_MASK);
|
||||
byte derivationSource = (byte) (apduBuffer[OFFSET_P1] & DERIVE_P1_SOURCE_MASK);
|
||||
doDerive(apduBuffer, MessageDigest.LENGTH_SHA_256, pathLen, derivationSource, makeCurrent);
|
||||
} else {
|
||||
if (len != MessageDigest.LENGTH_SHA_256) {
|
||||
|
@ -1257,9 +1229,9 @@ public class KeycardApplet extends Applet {
|
|||
|
||||
boolean derive = false;
|
||||
boolean makeCurrent = false;
|
||||
byte derivationSource = (byte) (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_SOURCE_MASK);
|
||||
byte derivationSource = (byte) (apduBuffer[OFFSET_P1] & DERIVE_P1_SOURCE_MASK);
|
||||
|
||||
switch ((byte) (apduBuffer[ISO7816.OFFSET_P1] & ~DERIVE_P1_SOURCE_MASK)) {
|
||||
switch ((byte) (apduBuffer[OFFSET_P1] & ~DERIVE_P1_SOURCE_MASK)) {
|
||||
case EXPORT_KEY_P1_CURRENT:
|
||||
break;
|
||||
case EXPORT_KEY_P1_DERIVE_AND_MAKE_CURRENT:
|
||||
|
@ -1339,8 +1311,25 @@ public class KeycardApplet extends Applet {
|
|||
secureChannel.preprocessAPDU(apduBuffer);
|
||||
}
|
||||
|
||||
short outLen = Util.makeShort((byte) 0x00, data[0]);
|
||||
Util.arrayCopyNonAtomic(data, (short) 1, apduBuffer, SecureChannel.SC_OUT_OFFSET, outLen);
|
||||
byte[] dst;
|
||||
|
||||
switch (apduBuffer[OFFSET_P1]) {
|
||||
case STORE_DATA_P1_PUBLIC:
|
||||
dst = data;
|
||||
break;
|
||||
case STORE_DATA_P1_NDEF:
|
||||
dst = SharedMemory.ndefDataFile;
|
||||
break;
|
||||
case STORE_DATA_P1_CASH:
|
||||
dst = SharedMemory.cashDataFile;
|
||||
break;
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
return;
|
||||
}
|
||||
|
||||
short outLen = Util.makeShort((byte) 0x00, dst[0]);
|
||||
Util.arrayCopyNonAtomic(dst, (short) 1, apduBuffer, SecureChannel.SC_OUT_OFFSET, outLen);
|
||||
|
||||
if (secureChannel.isOpen()) {
|
||||
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR);
|
||||
|
@ -1362,14 +1351,31 @@ public class KeycardApplet extends Applet {
|
|||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
byte[] dst;
|
||||
|
||||
switch (apduBuffer[OFFSET_P1]) {
|
||||
case STORE_DATA_P1_PUBLIC:
|
||||
dst = data;
|
||||
break;
|
||||
case STORE_DATA_P1_NDEF:
|
||||
dst = SharedMemory.ndefDataFile;
|
||||
break;
|
||||
case STORE_DATA_P1_CASH:
|
||||
dst = SharedMemory.cashDataFile;
|
||||
break;
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
return;
|
||||
}
|
||||
|
||||
short dataLen = Util.makeShort((byte) 0x00, apduBuffer[ISO7816.OFFSET_LC]);
|
||||
|
||||
if (dataLen > MAX_DATA_LENGTH) {
|
||||
if (dataLen >= (short) dst.length) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
JCSystem.beginTransaction();
|
||||
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_LC, data, (short) 0, (short)(dataLen + 1));
|
||||
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_LC, dst, (short) 0, (short)(dataLen + 1));
|
||||
JCSystem.commitTransaction();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ public class NDEFApplet extends Applet {
|
|||
private static final short NDEF_READ_SIZE = (short) 0xff;
|
||||
|
||||
private static final byte[] NDEF_CAPS_FILE = {
|
||||
(byte) 0x00, (byte) 0x0f, (byte) 0x20, (byte) 0x00, (byte) 0xff, (byte) 0x00, (byte) 0x01, (byte) 0x04,
|
||||
(byte) 0x06, (byte) 0xe1, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0xff
|
||||
(byte) 0x0f, (byte) 0x00, (byte) 0x0f, (byte) 0x20, (byte) 0x00, (byte) 0xff, (byte) 0x00, (byte) 0x01,
|
||||
(byte) 0x04, (byte) 0x06, (byte) 0xe1, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0xff
|
||||
};
|
||||
|
||||
private short selectedFile;
|
||||
|
@ -50,9 +50,9 @@ public class NDEFApplet extends Applet {
|
|||
short c9Off = (short)(bOffset + bArray[bOffset] + 1); // Skip AID
|
||||
c9Off += (short)(bArray[c9Off] + 1); // Skip Privileges and parameter length
|
||||
|
||||
short dataLen = Util.makeShort((byte) 0x00, bArray[c9Off++]);
|
||||
if ((dataLen > 2) && ((short)(dataLen - 2) == Util.makeShort(bArray[c9Off], bArray[(short)(c9Off + 1)]))) {
|
||||
Util.arrayCopyNonAtomic(bArray, c9Off, SharedMemory.ndefDataFile, (short) 0, dataLen);
|
||||
short dataLen = Util.makeShort((byte) 0x00, bArray[c9Off]);
|
||||
if ((dataLen > 2) && ((short)(dataLen - 2) == Util.makeShort(bArray[(short)(c9Off + 1)], bArray[(short)(c9Off + 2)]))) {
|
||||
Util.arrayCopyNonAtomic(bArray, c9Off, SharedMemory.ndefDataFile, (short) 0, (short)(dataLen + 1));
|
||||
}
|
||||
|
||||
register(bArray, (short) (bOffset + 1), bArray[bOffset]);
|
||||
|
@ -113,22 +113,20 @@ public class NDEFApplet extends Applet {
|
|||
byte[] apduBuffer = apdu.getBuffer();
|
||||
|
||||
byte[] data;
|
||||
short dataLen;
|
||||
|
||||
switch(selectedFile) {
|
||||
case FILEID_NDEF_CAPS:
|
||||
data = NDEF_CAPS_FILE;
|
||||
dataLen = (short) NDEF_CAPS_FILE.length;
|
||||
break;
|
||||
case FILEID_NDEF_DATA:
|
||||
data = SharedMemory.ndefDataFile;
|
||||
dataLen = (short) (Util.makeShort(data[0], data[1]) + 2);
|
||||
break;
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
short dataLen = Util.makeShort((byte) 0x00, data[0]);
|
||||
short offset = Util.getShort(apduBuffer, ISO7816.OFFSET_P1);
|
||||
|
||||
if (offset < 0 || offset >= dataLen) {
|
||||
|
@ -144,6 +142,9 @@ public class NDEFApplet extends Applet {
|
|||
le = (short)(dataLen - offset);
|
||||
}
|
||||
|
||||
// skip the len byte in data
|
||||
offset++;
|
||||
|
||||
apdu.setOutgoingLength(le);
|
||||
apdu.sendBytesLong(data, offset, le);
|
||||
}
|
||||
|
|
|
@ -5,5 +5,8 @@ package im.status.keycard;
|
|||
*/
|
||||
class SharedMemory {
|
||||
/** The NDEF data file. Read through the NDEFApplet. **/
|
||||
static final byte[] ndefDataFile = new byte[SecureChannel.SC_MAX_PLAIN_LENGTH];
|
||||
static final byte[] ndefDataFile = new byte[SecureChannel.SC_MAX_PLAIN_LENGTH + 1];
|
||||
|
||||
/** The Cash data file. Read through the CashApplet. **/
|
||||
static final byte[] cashDataFile = new byte[KeycardApplet.MAX_DATA_LENGTH + 1];
|
||||
}
|
||||
|
|
|
@ -153,6 +153,7 @@ public class KeycardTest {
|
|||
aid = AIDUtil.create(Identifiers.CASH_AID);
|
||||
bos.write(Identifiers.CASH_INSTANCE_AID.length);
|
||||
bos.write(Identifiers.CASH_INSTANCE_AID);
|
||||
bos.write(new byte[] {0x01, 0x00, 0x02, (byte) 0xC9, 0x00});
|
||||
|
||||
simulator.installApplet(aid, CashApplet.class, bos.toByteArray(), (short) 0, (byte) bos.size());
|
||||
bos.reset();
|
||||
|
@ -497,40 +498,6 @@ public class KeycardTest {
|
|||
assertNotEquals(null, path);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("SET NDEF command")
|
||||
@Capabilities("ndef")
|
||||
void setNDEFTest() throws Exception {
|
||||
byte[] ndefData = {
|
||||
(byte) 0x00, (byte) 0x24, (byte) 0xd4, (byte) 0x0f, (byte) 0x12, (byte) 0x61, (byte) 0x6e, (byte) 0x64,
|
||||
(byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d,
|
||||
(byte) 0x3a, (byte) 0x70, (byte) 0x6b, (byte) 0x67, (byte) 0x69, (byte) 0x6d, (byte) 0x2e, (byte) 0x73,
|
||||
(byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x75, (byte) 0x73, (byte) 0x2e, (byte) 0x65, (byte) 0x74,
|
||||
(byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, (byte) 0x75, (byte) 0x6d
|
||||
};
|
||||
|
||||
// Security condition violation: SecureChannel not open
|
||||
APDUResponse response = cmdSet.setNDEF(ndefData);
|
||||
assertEquals(0x6985, response.getSw());
|
||||
|
||||
cmdSet.autoOpenSecureChannel();
|
||||
|
||||
// Security condition violation: PIN not verified
|
||||
response = cmdSet.setNDEF(ndefData);
|
||||
assertEquals(0x6985, response.getSw());
|
||||
|
||||
response = cmdSet.verifyPIN("000000");
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
// Good case.
|
||||
response = cmdSet.setNDEF(ndefData);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
// Good case with no length.
|
||||
response = cmdSet.setNDEF(Arrays.copyOfRange(ndefData, 2, ndefData.length));
|
||||
assertEquals(0x9000, response.getSw());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("VERIFY PIN command")
|
||||
@Capabilities("credentialsManagement")
|
||||
|
@ -1305,7 +1272,7 @@ public class KeycardTest {
|
|||
|
||||
if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
|
||||
// Security condition violation: SecureChannel not open
|
||||
response = cmdSet.storePublicData(new byte[20]);
|
||||
response = cmdSet.storeData(new byte[20], KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x6985, response.getSw());
|
||||
|
||||
cmdSet.autoOpenSecureChannel();
|
||||
|
@ -1313,7 +1280,7 @@ public class KeycardTest {
|
|||
|
||||
if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
|
||||
// Security condition violation: PIN not verified
|
||||
response = cmdSet.storePublicData(new byte[20]);
|
||||
response = cmdSet.storeData(new byte[20], KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x6985, response.getSw());
|
||||
|
||||
response = cmdSet.verifyPIN("000000");
|
||||
|
@ -1321,7 +1288,7 @@ public class KeycardTest {
|
|||
}
|
||||
|
||||
// Data too long
|
||||
response = cmdSet.storePublicData(new byte[128]);
|
||||
response = cmdSet.storeData(new byte[128], KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x6A80, response.getSw());
|
||||
|
||||
byte[] data = new byte[127];
|
||||
|
@ -1331,38 +1298,80 @@ public class KeycardTest {
|
|||
}
|
||||
|
||||
// Correct data
|
||||
response = cmdSet.storePublicData(data);
|
||||
response = cmdSet.storeData(data, KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
// Read data back with secure channel
|
||||
response = cmdSet.getPublicData();
|
||||
response = cmdSet.getData(KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
assertArrayEquals(data, response.getData());
|
||||
|
||||
// Empty data
|
||||
response = cmdSet.storePublicData(new byte[0]);
|
||||
response = cmdSet.storeData(new byte[0], KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
response = cmdSet.getPublicData();
|
||||
response = cmdSet.getData(KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
assertEquals(0, response.getData().length);
|
||||
|
||||
// Shorter data
|
||||
data = Arrays.copyOf(data, 20);
|
||||
response = cmdSet.storePublicData(data);
|
||||
response = cmdSet.storeData(data, KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
// GET DATA without Secure Channel
|
||||
cmdSet.select().checkOK();
|
||||
|
||||
response = cmdSet.getPublicData();
|
||||
response = cmdSet.getData(KeycardCommandSet.STORE_DATA_P1_PUBLIC);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
assertArrayEquals(data, response.getData());
|
||||
|
||||
if (cmdSet.getApplicationInfo().hasNDEFCapability()) {
|
||||
byte[] ndefData = {
|
||||
(byte) 0x00, (byte) 0x24, (byte) 0xd4, (byte) 0x0f, (byte) 0x12, (byte) 0x61, (byte) 0x6e, (byte) 0x64,
|
||||
(byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d,
|
||||
(byte) 0x3a, (byte) 0x70, (byte) 0x6b, (byte) 0x67, (byte) 0x69, (byte) 0x6d, (byte) 0x2e, (byte) 0x73,
|
||||
(byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x75, (byte) 0x73, (byte) 0x2e, (byte) 0x65, (byte) 0x74,
|
||||
(byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x65, (byte) 0x75, (byte) 0x6d
|
||||
};
|
||||
|
||||
// Security condition violation: SecureChannel not open
|
||||
response = cmdSet.setNDEF(ndefData);
|
||||
assertEquals(0x6985, response.getSw());
|
||||
|
||||
cmdSet.autoOpenSecureChannel();
|
||||
|
||||
// Security condition violation: PIN not verified
|
||||
response = cmdSet.setNDEF(ndefData);
|
||||
assertEquals(0x6985, response.getSw());
|
||||
|
||||
response = cmdSet.verifyPIN("000000");
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
// Good case.
|
||||
response = cmdSet.setNDEF(ndefData);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
// Good case with no length.
|
||||
response = cmdSet.setNDEF(Arrays.copyOfRange(ndefData, 2, ndefData.length));
|
||||
assertEquals(0x9000, response.getSw());
|
||||
}
|
||||
|
||||
data[0] = (byte) 0xAA;
|
||||
|
||||
response = cmdSet.storeData(data, KeycardCommandSet.STORE_DATA_P1_CASH);
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
||||
CashCommandSet cashCmdSet = new CashCommandSet(sdkChannel);
|
||||
response = cashCmdSet.select();
|
||||
assertEquals(0x9000, response.getSw());
|
||||
CashApplicationInfo info = new CashApplicationInfo(response.getData());
|
||||
assertArrayEquals(data, info.getPubData());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test the Cash applet")
|
||||
@Tag("manual")
|
||||
void cashTest() throws Exception {
|
||||
CashCommandSet cashCmdSet = new CashCommandSet(sdkChannel);
|
||||
APDUResponse response = cashCmdSet.select();
|
||||
|
|
Loading…
Reference in New Issue