add (draft of) cash applet

This commit is contained in:
Michele Balistreri 2019-05-24 09:41:49 +02:00
parent ac3c24852f
commit 09c725dcb2
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
2 changed files with 117 additions and 1 deletions

View File

@ -29,7 +29,10 @@ javacard {
aid = '0xA0:0x00:0x00:0x08:0x04:0x00:0x01:0x02' aid = '0xA0:0x00:0x00:0x08:0x04:0x00:0x01:0x02'
className = 'NDEFApplet' className = 'NDEFApplet'
} }
applet {
aid = '0xA0:0x00:0x00:0x08:0x04:0x00:0x01:0x03'
className = 'CashApplet'
}
version = '2.3' version = '2.3'
} }
} }

View File

@ -0,0 +1,113 @@
package im.status.keycard;
import javacard.framework.*;
import javacard.security.*;
public class CashApplet extends Applet {
private static final short SIGN_OUT_OFF = ISO7816.OFFSET_CDATA + MessageDigest.LENGTH_SHA_256;
private KeyPair keypair;
private ECPublicKey publicKey;
private ECPrivateKey privateKey;
private Crypto crypto;
private SECP256k1 secp256k1;
private Signature signature;
/**
* Invoked during applet installation. Creates an instance of this class. The installation parameters are passed in
* the given buffer.
*
* @param bArray installation parameters buffer
* @param bOffset offset where the installation parameters begin
* @param bLength length of the installation parameters
*/
public static void install(byte[] bArray, short bOffset, byte bLength) {
new CashApplet(bArray, bOffset, bLength);
}
/**
* Application constructor. All memory allocation is done here. The reason for this is two-fold: first the card might
* not have Garbage Collection so dynamic allocation will eventually eat all memory. The second reason is to be sure
* that if the application installs successfully, there is no risk of running out of memory because of other applets
* allocating memory. The constructor also registers the applet with the JCRE so that it becomes selectable.
*
* @param bArray installation parameters buffer
* @param bOffset offset where the installation parameters begin
* @param bLength length of the installation parameters
*/
public CashApplet(byte[] bArray, short bOffset, byte bLength) {
crypto = new Crypto();
secp256k1 = new SECP256k1(crypto);
keypair = new KeyPair(KeyPair.ALG_EC_FP, SECP256k1.SECP256K1_KEY_SIZE);
publicKey = (ECPublicKey) keypair.getPublic();
privateKey = (ECPrivateKey) keypair.getPrivate();
secp256k1.setCurveParameters(publicKey);
secp256k1.setCurveParameters(privateKey);
keypair.genKeyPair();
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
signature.init(privateKey, Signature.MODE_SIGN);
register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
public void process(APDU apdu) throws ISOException {
apdu.setIncomingAndReceive();
// Since selection can happen not only by a SELECT command, we check for that separately.
if (selectingApplet()) {
selectApplet(apdu);
return;
}
byte[] apduBuffer = apdu.getBuffer();
try {
switch (apduBuffer[ISO7816.OFFSET_INS]) {
case KeycardApplet.INS_SIGN:
sign(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
}
} catch (CryptoException ce) {
ISOException.throwIt((short)(ISO7816.SW_UNKNOWN | ce.getReason()));
} catch (Exception e) {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
}
private void selectApplet(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
apduBuffer[0] = KeycardApplet.TLV_PUB_KEY;
apduBuffer[1] = (byte) publicKey.getW(apduBuffer, (short) 2);
apdu.setOutgoingAndSend((short) 0, (short)(apduBuffer[1] + 2));
}
private void sign(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
apduBuffer[SIGN_OUT_OFF] = KeycardApplet.TLV_SIGNATURE_TEMPLATE;
apduBuffer[(short) (SIGN_OUT_OFF + 3)] = KeycardApplet.TLV_PUB_KEY;
short outLen = apduBuffer[(short) (SIGN_OUT_OFF + 4)] = Crypto.KEY_PUB_SIZE;
publicKey.getW(apduBuffer, (short) (SIGN_OUT_OFF + 5));
outLen += 5;
short sigOff = (short) (SIGN_OUT_OFF + outLen);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
outLen += crypto.fixS(apduBuffer, sigOff);
apduBuffer[(short) (SIGN_OUT_OFF + 1)] = (byte) 0x81;
apduBuffer[(short) (SIGN_OUT_OFF + 2)] = (byte) (outLen - 3);
apdu.setOutgoingAndSend(SIGN_OUT_OFF, outLen);
}
}