add identify card command to cash applet

This commit is contained in:
Michele Balistreri 2022-07-26 10:04:10 +02:00
parent a692b6b838
commit 01c46f7009
5 changed files with 70 additions and 57 deletions

View File

@ -76,6 +76,9 @@ public class CashApplet extends Applet {
case KeycardApplet.INS_SIGN:
sign(apdu);
break;
case IdentApplet.INS_IDENTIFY_CARD:
IdentApplet.identifyCard(apdu, null, signature);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;

View File

@ -7,6 +7,11 @@ import javacard.security.*;
* The applet's main class. All incoming commands a processed by this class.
*/
public class IdentApplet extends Applet {
static final byte TLV_CERT = (byte) 0x8A;
static final byte CERT_VALID = (byte) 0xAA;
static final byte INS_IDENTIFY_CARD = (byte) 0x14;
/**
* Invoked during applet installation. Creates an instance of this class. The installation parameters are passed in
* the given buffer.
@ -65,7 +70,7 @@ public class IdentApplet extends Applet {
byte[] apduBuffer = apdu.getBuffer();
apdu.setIncomingAndReceive();
if (SharedMemory.idCert[0] == SharedMemory.CERT_VALID) {
if (SharedMemory.idCert[0] == CERT_VALID) {
Util.arrayCopyNonAtomic(SharedMemory.idCert, (short) 1, apduBuffer, (short) 0, SharedMemory.CERT_LEN);
apdu.setOutgoingAndSend((short) 0, SharedMemory.CERT_LEN);
}
@ -73,7 +78,7 @@ public class IdentApplet extends Applet {
}
private void processStoreData(APDU apdu) {
if (SharedMemory.idCert[0] == SharedMemory.CERT_VALID) {
if (SharedMemory.idCert[0] == CERT_VALID) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
@ -86,6 +91,52 @@ public class IdentApplet extends Applet {
Util.arrayCopyNonAtomic(apduBuffer, ISO7816.OFFSET_CDATA, SharedMemory.idCert, (short) 1, SharedMemory.CERT_LEN);
SharedMemory.idPrivate.setS(apduBuffer, (short) (ISO7816.OFFSET_CDATA + SharedMemory.CERT_LEN), Crypto.KEY_SECRET_SIZE);
SharedMemory.idCert[0] = SharedMemory.CERT_VALID;
SharedMemory.idCert[0] = CERT_VALID;
}
/**
* Processes the IDENTIFY CARD command according to the application's specifications.
*
* @param apdu the JCRE-owned APDU object.
*/
static void identifyCard(APDU apdu, SecureChannel secureChannel, Signature signature) {
byte[] apduBuffer = apdu.getBuffer();
short len;
if (secureChannel != null && secureChannel.isOpen()) {
len = secureChannel.preprocessAPDU(apduBuffer);
} else {
len = (short) (apduBuffer[ISO7816.OFFSET_LC] & (short) 0xff);
}
if (SharedMemory.idCert[0] != CERT_VALID) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
if (len != MessageDigest.LENGTH_SHA_256) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
short off = SecureChannel.SC_OUT_OFFSET;
apduBuffer[off++] = KeycardApplet.TLV_SIGNATURE_TEMPLATE;
apduBuffer[off++] = (byte) 0x81;
off++;
apduBuffer[off++] = TLV_CERT;
apduBuffer[off++] = (byte) SharedMemory.CERT_LEN;
Util.arrayCopyNonAtomic(SharedMemory.idCert, (short) 1, apduBuffer, off, SharedMemory.CERT_LEN);
off += SharedMemory.CERT_LEN;
short outLen = (short)(SharedMemory.CERT_LEN + 5);
signature.init(SharedMemory.idPrivate, Signature.MODE_SIGN);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, off);
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte)(outLen - 3);
if (secureChannel != null && secureChannel.isOpen()) {
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR);
} else {
apdu.setOutgoingAndSend(SecureChannel.SC_OUT_OFFSET, outLen);
}
}
}

View File

@ -13,7 +13,6 @@ public class KeycardApplet extends Applet {
static final byte INS_GET_STATUS = (byte) 0xF2;
static final byte INS_INIT = (byte) 0xFE;
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;
@ -99,7 +98,6 @@ public class KeycardApplet extends Applet {
static final byte TLV_UID = (byte) 0x8F;
static final byte TLV_KEY_UID = (byte) 0x8E;
static final byte TLV_CAPABILITIES = (byte) 0x8D;
static final byte TLV_CERT = (byte) 0x8A;
static final byte CAPABILITY_SECURE_CHANNEL = (byte) 0x01;
static final byte CAPABILITY_KEY_MANAGEMENT = (byte) 0x02;
@ -249,8 +247,8 @@ public class KeycardApplet extends Applet {
case SecureChannel.INS_UNPAIR:
unpair(apdu);
break;
case INS_IDENTIFY_CARD:
identifyCard(apdu);
case IdentApplet.INS_IDENTIFY_CARD:
IdentApplet.identifyCard(apdu, secureChannel, signature);
break;
case INS_GET_STATUS:
getStatus(apdu);
@ -368,8 +366,8 @@ public class KeycardApplet extends Applet {
puk.update(apduBuffer, (short)(ISO7816.OFFSET_CDATA + PIN_LENGTH), PUK_LENGTH);
JCSystem.commitTransaction();
} else if (apduBuffer[ISO7816.OFFSET_INS] == INS_IDENTIFY_CARD) {
identifyCard(apdu);
} else if (apduBuffer[ISO7816.OFFSET_INS] == IdentApplet.INS_IDENTIFY_CARD) {
IdentApplet.identifyCard(apdu, null, signature);
} else {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
@ -479,53 +477,6 @@ public class KeycardApplet extends Applet {
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
}
/**
* Processes the IDENTIFY CARD command according to the application's specifications.
*
* @param apdu the JCRE-owned APDU object.
*/
private void identifyCard(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
short len;
if (secureChannel.isOpen()) {
len = secureChannel.preprocessAPDU(apduBuffer);
} else {
len = (short) (apduBuffer[ISO7816.OFFSET_LC] & (short) 0xff);
}
if (SharedMemory.idCert[0] != SharedMemory.CERT_VALID) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
if (len != MessageDigest.LENGTH_SHA_256) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
short off = SecureChannel.SC_OUT_OFFSET;
apduBuffer[off++] = TLV_SIGNATURE_TEMPLATE;
apduBuffer[off++] = (byte) 0x81;
off++;
apduBuffer[off++] = TLV_CERT;
apduBuffer[off++] = (byte) SharedMemory.CERT_LEN;
Util.arrayCopyNonAtomic(SharedMemory.idCert, (short) 1, apduBuffer, off, SharedMemory.CERT_LEN);
off += SharedMemory.CERT_LEN;
short outLen = (short)(SharedMemory.CERT_LEN + 5);
signature.init(SharedMemory.idPrivate, Signature.MODE_SIGN);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, off);
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte)(outLen - 3);
if (secureChannel.isOpen()) {
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR);
} else {
apdu.setOutgoingAndSend(SecureChannel.SC_OUT_OFFSET, outLen);
}
}
/**
* 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

View File

@ -6,7 +6,6 @@ import javacard.security.*;
* Keep references to data structures shared across applet instances of this package.
*/
class SharedMemory {
static final byte CERT_VALID = (byte) 0xAA;
static final short CERT_LEN = 98;
/** The NDEF data file. Read through the NDEFApplet. **/

View File

@ -298,6 +298,15 @@ public class KeycardTest {
assertEquals(0x9000, response.getSw());
caPub = Certificate.verifyIdentity(challenge, response.getData());
assertArrayEquals(expectedCaPub, caPub);
random.nextBytes(challenge);
CashCommandSet cashCmdSet = new CashCommandSet(sdkChannel);
response = cashCmdSet.select();
assertEquals(0x9000, response.getSw());
response = cashCmdSet.identifyCard(challenge);
assertEquals(0x9000, response.getSw());
caPub = Certificate.verifyIdentity(challenge, response.getData());
assertArrayEquals(expectedCaPub, caPub);
}
@Test