mirror of
https://github.com/SemaphoreNetwork/semaphore_network_ethsim.git
synced 2026-01-11 04:27:58 -05:00
1129 lines
27 KiB
Java
1129 lines
27 KiB
Java
package com.semaphore.usim;
|
|
|
|
import javacard.framework.*;
|
|
|
|
public class EthSIM {
|
|
// initial menu string
|
|
private byte[] STK_Header = { (byte) 'S', (byte) 'T', (byte) 'K', (byte) ' ', (byte) 'S', (byte) 'e', (byte) 'r',
|
|
(byte) 'v', (byte) 'i', (byte) 'c', (byte) 'e', (byte) 's', };
|
|
|
|
private byte[] Title = {
|
|
// "EthSIM Wallet"
|
|
(byte) 'E', (byte) 't', (byte) 'h', (byte) 'S', (byte) 'I', (byte) 'M', (byte) ' ', (byte) 'W', (byte) 'a',
|
|
(byte) 'l', (byte) 'l', (byte) 'e', (byte) 't', };
|
|
|
|
// Strings for Wallet Flows
|
|
// Send ETH
|
|
private byte[] SendEth = { (byte) 'S', (byte) 'e', (byte) 'n', (byte) 'd', (byte) ' ', (byte) 'E', (byte) 't',
|
|
(byte) 'h', (byte) ':' };
|
|
|
|
// GET INPUT TEXT
|
|
private byte[] AmountToSend = { (byte) 'A', (byte) 'm', (byte) 'o', (byte) 'u', (byte) 'n', (byte) 't', (byte) ' ',
|
|
(byte) 't', (byte) 'o', (byte) ' ', (byte) 'S', (byte) 'e', (byte) 'n', (byte) 'd', (byte) ':' };
|
|
|
|
private byte[] AmountDetails = { (byte) 'i', (byte) 'n', (byte) 'c', (byte) 'l', (byte) 'u', (byte) 'd', (byte) 'e',
|
|
(byte) ' ', (byte) 'd', (byte) 'e', (byte) 'c', (byte) 'i', (byte) 'm', (byte) 'a', (byte) 'l', (byte) ' ',
|
|
(byte) 'u', (byte) 'p', (byte) ' ', (byte) 't', (byte) 'o', (byte) ' ', (byte) '1', (byte) '8', (byte) ' ',
|
|
(byte) 'p', (byte) 'l', (byte) 'a', (byte) 'c', (byte) 'e', (byte) 's' };
|
|
|
|
private byte[] AddressToSendTo = { (byte) 'A', (byte) 'd', (byte) 'd', (byte) 'r', (byte) 'e', (byte) 's',
|
|
(byte) 's', (byte) ' ', (byte) 't', (byte) 'o', (byte) ' ', (byte) 'S', (byte) 'e', (byte) 'n', (byte) 'd',
|
|
(byte) ' ', (byte) 't', (byte) 'o', (byte) ':', };
|
|
|
|
private byte[] Sign = { (byte) 'S', (byte) 'i', (byte) 'g', (byte) 'n' };
|
|
|
|
private byte[] EnterPin = { (byte) 'E', (byte) 'n', (byte) 't', (byte) 'e', (byte) 'r', (byte) ' ', (byte) 'P',
|
|
(byte) 'I', (byte) 'N' };
|
|
|
|
private byte[] CopySig = { (byte) 'C', (byte) 'o', (byte) 'p', (byte) 'y', (byte) ' ', (byte) 'S', (byte) 'i',
|
|
(byte) 'g' };
|
|
|
|
private byte[] BlindSign = { (byte) 'B', (byte) 'l', (byte) 'i', (byte) 'n', (byte) 'd', (byte) ' ', (byte) 'S',
|
|
(byte) 'i', (byte) 'g', (byte) 'n' };
|
|
|
|
private byte[] NoSig = { (byte) 'N', (byte) 'o', (byte) ' ', (byte) 'S', (byte) 'i', (byte) 'g' };
|
|
|
|
private byte[] Success = { (byte) 'S', (byte) 'u', (byte) 'c', (byte) 'c', (byte) 'e', (byte) 's', (byte) 's' };
|
|
|
|
private byte[] Fail = { (byte) 'F', (byte) 'a', (byte) 'i', (byte) 'l' };
|
|
|
|
// buffers to save the values entered by user
|
|
private byte[] amountBuffer;
|
|
private byte[] addressBuffer;
|
|
private byte[] pinBuffer;
|
|
|
|
// hash and signature
|
|
private byte[] hashBuffer;
|
|
private byte[] sigBuffer;
|
|
|
|
// Item Identifiers:
|
|
private final byte AMOUNT_TO_SEND = (byte) 0x00;
|
|
private final byte ADDRESS_TO_SEND_TO = (byte) 0x01;
|
|
private final byte ENTER_PIN = (byte) 0x02;
|
|
private final byte BLIND_SIGN = (byte) 0x03;
|
|
private final byte COPY_SIG = (byte) 0x04;
|
|
|
|
// Most of the Lengths of these
|
|
public short AmtToSendLe = (short) 43;
|
|
public short AddToSendToLe = (short) 37;
|
|
public short EnterPinLe = (short) 27;
|
|
public short CopySigLe = (short) 26;
|
|
public short BlindSignLe = (short) 28;
|
|
|
|
// Mask defining the SIM Toolkit features required by the Applet
|
|
// It is a bitmask with 1s reflecting the needed profiles.
|
|
private byte[] terminalProfileMask = { (byte) 0x09, (byte) 0x03, (byte) 0x21, (byte) 0x70, (byte) 0x0D };
|
|
// Volatile RAM temporary buffer for storing intermediate data and results
|
|
// It is 180 bytes, enough for a long SMS + dialing number.
|
|
//
|
|
private byte[] tempBuffer;
|
|
|
|
// STK Commands
|
|
private final byte SETUP_MENU = (byte) 0x25;
|
|
private final byte SELECT_ITEM = (byte) 0x24;
|
|
private final byte GET_INPUT = (byte) 0x23;
|
|
private final byte DISPLAY_TEXT = (byte) 0x21;
|
|
|
|
// default command details
|
|
private static byte CMD_NUMBER = (byte) 0x01;
|
|
|
|
// selection 0 is setup menu
|
|
public byte selection = (byte) 0;
|
|
|
|
//
|
|
public byte getInputSelection = (byte) 0;
|
|
|
|
// current Byte being processed
|
|
private short offset;
|
|
|
|
public boolean stkSelected = false;
|
|
|
|
public byte[] tempBuf;
|
|
|
|
// input bufs
|
|
public byte[] inputAmt;
|
|
|
|
public byte[] inputAddress;
|
|
|
|
public byte[] inputPin;
|
|
|
|
// next fetch response buf
|
|
public byte[] nextFetchBuf;
|
|
//
|
|
public byte nextFetchLe;
|
|
|
|
public boolean initialFetch = true;
|
|
|
|
public byte nextToFetch;
|
|
|
|
// CONSTS
|
|
// menuLength
|
|
private byte envelopeLe = (byte) 93;
|
|
|
|
// *********ETH Wallet Class Objects***************//
|
|
public ETHWallet wallet;
|
|
// inputHash points to the hash input from the "Blind Sign" STK applet input
|
|
public byte[] inputHash;
|
|
// outputSig points to the sig that is output from the eth wallet class.
|
|
// public byte[] outputSig;
|
|
|
|
public static byte DERFormatLe = (byte) 74;
|
|
|
|
private Sha3 m_sha3 = null;
|
|
|
|
public EthSIM() {
|
|
tempBuffer = JCSystem.makeTransientByteArray((short) 64, JCSystem.CLEAR_ON_RESET);
|
|
amountBuffer = JCSystem.makeTransientByteArray((short) 24, JCSystem.CLEAR_ON_RESET);
|
|
// outputSig = JCSystem.makeTransientByteArray((short) 65, JCSystem.CLEAR_ON_RESET);
|
|
|
|
addressBuffer = JCSystem.makeTransientByteArray((short) 40, JCSystem.CLEAR_ON_RESET);
|
|
pinBuffer = JCSystem.makeTransientByteArray((short) 10, JCSystem.CLEAR_ON_RESET);
|
|
// this.initialFetch = true;
|
|
|
|
this.wallet = new ETHWallet(inputHash);
|
|
// m_sha3 = new Sha3();
|
|
|
|
JCSystem.beginTransaction();
|
|
byte[] old = this.nextFetchBuf;
|
|
|
|
if (old != null) {
|
|
JCSystem.requestObjectDeletion();
|
|
}
|
|
JCSystem.commitTransaction();
|
|
|
|
}
|
|
|
|
public void handleMenuSelectionEnvelope(APDU apdu) {
|
|
// todo: some data needs to be returned here.
|
|
// ignore making into buf.
|
|
// should be (short)0x9126
|
|
short sw = Util.makeShort((byte) 0x91, (byte) 0x5a);
|
|
// the selection is the main menu, fetch accordingly
|
|
|
|
this.stkSelected = true;
|
|
// return 91+Le resposnse
|
|
ISOException.throwIt(sw);
|
|
|
|
}
|
|
|
|
public void crateCommandDetails(byte[] cmdBuf, byte cmdType, byte cmdQualifier) {
|
|
|
|
cmdBuf[this.offset] = CMD_NUMBER;
|
|
this.offset++;
|
|
|
|
cmdBuf[this.offset] = cmdType;
|
|
this.offset++;
|
|
|
|
cmdBuf[this.offset] = cmdQualifier;
|
|
this.offset++;
|
|
|
|
}
|
|
|
|
// todo; switch for devid to 8281
|
|
public void createDevId(byte[] cmdBuf) {
|
|
this.offset++;
|
|
|
|
cmdBuf[this.offset] = (byte) 0x81;
|
|
this.offset++;
|
|
|
|
cmdBuf[this.offset] = (byte) 0x82;
|
|
this.offset++;
|
|
}
|
|
|
|
public void createInitialTLV(byte[] retBuf) {
|
|
this.offset++;
|
|
// TLV 81 is byte 6
|
|
retBuf[this.offset] = (byte) 0x81;
|
|
this.offset++;
|
|
// Le of CMD is byte 7 should == 3
|
|
retBuf[this.offset] = (byte) 0x03;
|
|
this.offset++;
|
|
|
|
}
|
|
|
|
// response to initial fetch
|
|
public void handleMenuSetup(byte[] retBuf, byte retLen) {
|
|
|
|
this.offset = 0;
|
|
this.tempBuf[this.offset] = (byte) 0xd0;
|
|
this.offset++;
|
|
this.tempBuf[this.offset] = retLen;
|
|
|
|
this.createInitialTLV(retBuf);
|
|
|
|
this.crateCommandDetails(retBuf, SETUP_MENU, (byte) 0x00);
|
|
// next byte should be TLV 82
|
|
retBuf[this.offset] = (byte) 0x82;
|
|
|
|
// next byte should be Le of the Dev Id
|
|
this.offset++;
|
|
retBuf[this.offset] = (byte) 0x02;
|
|
this.createDevId(retBuf);
|
|
// // next byte TLV is 85
|
|
retBuf[this.offset] = (byte) 0x85;
|
|
// // next byte is Le of the alpha identifier.
|
|
this.offset++;
|
|
|
|
retBuf[this.offset] = (byte) STK_Header.length;
|
|
this.offset++;
|
|
//
|
|
// // may need normal arryCopy
|
|
// // copy the STK Header into the buf
|
|
|
|
Util.arrayCopyNonAtomic(STK_Header, (short) 0, retBuf, (short) this.offset, (short) STK_Header.length);
|
|
// // off by one here?
|
|
this.offset += STK_Header.length;
|
|
// this.offset++;
|
|
// // TLV of 8f
|
|
// this.offset++;
|
|
retBuf[this.offset] = (byte) 0x8f;
|
|
// // Le of ItemID + item string ie. Title string length + 1
|
|
this.offset++;
|
|
retBuf[this.offset] = (byte) (Title.length + 1);
|
|
// // item identifier
|
|
this.offset++;
|
|
retBuf[this.offset] = (byte) 0x80;
|
|
//
|
|
// // copy title into tlv
|
|
this.offset++;
|
|
Util.arrayCopyNonAtomic(Title, (short) 0, retBuf, (short) this.offset, (short) Title.length);
|
|
this.offset += Title.length;
|
|
|
|
retBuf[this.offset] = (byte) 0x18;
|
|
this.offset++;
|
|
|
|
retBuf[this.offset] = (byte) 0x01;
|
|
this.offset++;
|
|
|
|
retBuf[this.offset] = (byte) 0x10;
|
|
|
|
this.offset = 0;
|
|
return;
|
|
}
|
|
|
|
public static byte createInitialTLVO(byte[] retBuf, byte offset) {
|
|
// TLV 81 is byte 6
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
// Le of CMD is byte 7 should == 3
|
|
retBuf[offset] = (byte) 0x03;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
public static byte createCommandDetailsO(byte[] cmdBuf, byte cmdType, byte cmdQualifier, byte offset) {
|
|
|
|
cmdBuf[offset] = CMD_NUMBER;
|
|
offset++;
|
|
|
|
cmdBuf[offset] = cmdType;
|
|
offset++;
|
|
|
|
cmdBuf[offset] = cmdQualifier;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
// todo; switch for devid to 8281
|
|
public static byte createDevIdO(byte[] cmdBuf, byte offset) {
|
|
|
|
// tag
|
|
cmdBuf[offset] = (byte) 0x82;
|
|
offset++;
|
|
// le
|
|
cmdBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
cmdBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
|
|
cmdBuf[offset] = (byte) 0x82;
|
|
return offset;
|
|
}
|
|
|
|
public void handleRootFetch(byte[] retBuf, byte retLen) {
|
|
|
|
byte offset = 0;
|
|
|
|
this.tempBuf[offset] = (byte) 0xd0;
|
|
offset++;
|
|
|
|
this.tempBuf[offset] = retLen;
|
|
offset++;
|
|
|
|
offset = createInitialTLVO(retBuf, offset);
|
|
offset++;
|
|
|
|
offset = createCommandDetailsO(retBuf, SELECT_ITEM, (byte) 0x80, offset);
|
|
offset++;
|
|
|
|
offset = createDevIdO(retBuf, offset);
|
|
offset++;
|
|
//
|
|
// // title menu items ?
|
|
retBuf[offset] = (byte) 0x05;
|
|
offset++;
|
|
|
|
// Root Menu Text
|
|
retBuf[offset] = (byte) SendEth.length;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(SendEth, (short) 0, retBuf, (short) offset, (short) SendEth.length);
|
|
offset += SendEth.length;
|
|
// TLV of 8f
|
|
retBuf[offset] = (byte) 0x8f;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) (AmountToSend.length + 1);
|
|
offset++;
|
|
//
|
|
// // item ID
|
|
retBuf[offset] = (byte) 0x00;
|
|
offset++;
|
|
//
|
|
Util.arrayCopyNonAtomic(AmountToSend, (short) 0, retBuf, (short) offset, (short) AmountToSend.length);
|
|
offset += AmountToSend.length;
|
|
//
|
|
retBuf[offset] = (byte) 0x8f;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) (AddressToSendTo.length + 1);
|
|
offset++;
|
|
//
|
|
// // item ID
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
//
|
|
Util.arrayCopyNonAtomic(AddressToSendTo, (short) 0, retBuf, (short) offset, (short) AddressToSendTo.length);
|
|
offset += AddressToSendTo.length;
|
|
//
|
|
retBuf[offset] = (byte) 0x8f;
|
|
offset++;
|
|
////
|
|
retBuf[offset] = (byte) (Sign.length + 1);
|
|
offset++;
|
|
//
|
|
// // item ID
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
//
|
|
Util.arrayCopyNonAtomic(Sign, (short) 0, retBuf, (short) offset, (short) Sign.length);
|
|
offset += Sign.length;
|
|
|
|
// Adding the BlindSig and CopySig menu item entries to the root menu
|
|
//
|
|
retBuf[offset] = (byte) 0x8f;
|
|
offset++;
|
|
////
|
|
retBuf[offset] = (byte) (BlindSign.length + 1);
|
|
offset++;
|
|
//
|
|
// // item ID
|
|
retBuf[offset] = (byte) 0x03;
|
|
offset++;
|
|
//
|
|
Util.arrayCopyNonAtomic(BlindSign, (short) 0, retBuf, (short) offset, (short) BlindSign.length);
|
|
offset += BlindSign.length;
|
|
//
|
|
retBuf[offset] = (byte) 0x8f;
|
|
offset++;
|
|
////
|
|
retBuf[offset] = (byte) (CopySig.length + 1);
|
|
offset++;
|
|
//
|
|
// // item ID
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
//
|
|
Util.arrayCopyNonAtomic(CopySig, (short) 0, retBuf, (short) offset, (short) CopySig.length);
|
|
offset += CopySig.length;
|
|
//
|
|
|
|
// 0x10??
|
|
retBuf[offset] = (byte) 0x10;
|
|
offset++;
|
|
|
|
// // 0x01 (Le)
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
// 0x00 Value?
|
|
// retBuf[offset] = (byte) 0x00;
|
|
// offset++;
|
|
|
|
}
|
|
|
|
// param success = 0 failure =
|
|
public short handleSetFetchDisplayText(byte[] retBuf, byte success) {
|
|
byte offset = 0;
|
|
|
|
retBuf[offset] = (byte) 0xd0;
|
|
offset++;
|
|
|
|
// //todo dyanmic
|
|
// retBuf[offset] = (byte) (retBuf.length - 2);
|
|
retBuf[offset] = (byte) 0x13;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
// Le of CMD is byte 7 should == 3
|
|
retBuf[offset] = (byte) 0x03;
|
|
offset++;
|
|
//
|
|
//
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
|
|
retBuf[offset] = DISPLAY_TEXT;
|
|
offset++;
|
|
|
|
// different cmd qualifier
|
|
retBuf[offset] = (byte) 0x80;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x82;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
|
|
// diff
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x8d;
|
|
offset++;
|
|
|
|
// todo: or fail
|
|
// retBuf[offset] = (byte) Success.length;
|
|
retBuf[offset] = (byte) 0x08;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
// todo probalby this is Success.length - 1 (we put the char encoding inside
|
|
// it's string
|
|
Util.arrayCopyNonAtomic(Success, (short) 0, retBuf, (short) offset, (short) Success.length);
|
|
// ret sw 9000
|
|
return (short) 0x9000;
|
|
|
|
//
|
|
|
|
}
|
|
|
|
public void handleSetFetchGetInput(byte[] retBuf, byte getInputId) {
|
|
|
|
short offset = 0;
|
|
|
|
retBuf[offset] = (byte) 0xd0;
|
|
offset++;
|
|
//
|
|
// //todo dyanmic
|
|
if (getInputId == 4) {
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x9C;
|
|
} else {
|
|
retBuf[offset] = (byte) (retBuf.length - 2);
|
|
}
|
|
|
|
offset++;
|
|
|
|
//
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
// Le of CMD is byte 7 should == 3
|
|
retBuf[offset] = (byte) 0x03;
|
|
offset++;
|
|
//
|
|
//
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
//
|
|
//
|
|
retBuf[offset] = GET_INPUT;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x82;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x82;
|
|
offset++;
|
|
//
|
|
retBuf[offset] = (byte) 0x8d;
|
|
offset++;
|
|
//
|
|
|
|
switch (getInputId) {
|
|
|
|
case AMOUNT_TO_SEND:
|
|
// next byte should be TLV 82
|
|
|
|
retBuf[offset] = (byte) (AmountToSend.length + 1);
|
|
offset++;
|
|
|
|
// //charset
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(AmountToSend, (short) 0, retBuf, (short) offset, (short) AmountToSend.length);
|
|
|
|
offset += AmountToSend.length;
|
|
|
|
retBuf[offset] = (byte) 0x91;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
|
|
// retBuf[offset] = (byte) 0x24;
|
|
retBuf[offset] = (byte) 0x80;
|
|
offset++;
|
|
|
|
// tag for default text
|
|
retBuf[offset] = (byte) 0x97;
|
|
offset++;
|
|
|
|
// le
|
|
retBuf[offset] = (byte) 0x08;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'e';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'a';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'd';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'b';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'e';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'e';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'f';
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case ADDRESS_TO_SEND_TO:
|
|
|
|
retBuf[offset] = (byte) (AddressToSendTo.length + 1);
|
|
offset++;
|
|
|
|
// //charset
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(AddressToSendTo, (short) 0, retBuf, (short) offset, (short) AddressToSendTo.length);
|
|
offset += AddressToSendTo.length;
|
|
|
|
retBuf[offset] = (byte) 0x91;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x28;
|
|
offset++;
|
|
break;
|
|
|
|
case ENTER_PIN:
|
|
|
|
retBuf[offset] = (byte) (EnterPin.length + 1);
|
|
offset++;
|
|
|
|
// charset
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(EnterPin, (short) 0, retBuf, (short) offset, (short) EnterPin.length);
|
|
offset += EnterPin.length;
|
|
|
|
retBuf[offset] = (byte) 0x91;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x28;
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case BLIND_SIGN:
|
|
|
|
retBuf[offset] = (byte) (BlindSign.length + 1);
|
|
offset++;
|
|
|
|
// //charset
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(BlindSign, (short) 0, retBuf, (short) offset, (short) BlindSign.length);
|
|
offset += BlindSign.length;
|
|
|
|
retBuf[offset] = (byte) 0x91;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x40;
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case COPY_SIG:
|
|
|
|
retBuf[offset] = (byte) (CopySig.length + 1);
|
|
offset++;
|
|
|
|
// charset
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(CopySig, (short) 0, retBuf, (short) offset, (short) CopySig.length);
|
|
offset += CopySig.length;
|
|
|
|
retBuf[offset] = (byte) 0x91;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x02;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x01;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x80;
|
|
offset++;
|
|
|
|
// todo: this never gets reached as a outputSig is initalized filled with 0s.
|
|
if (this.wallet.outputSig == null) {
|
|
|
|
// tag for default text
|
|
retBuf[offset] = (byte) 0x97;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x07;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
// there is no sig in the output buffer.
|
|
// a hash need signing
|
|
retBuf[offset] = (byte) 'N';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'o';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) ' ';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'S';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'i';
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 'g';
|
|
offset++;
|
|
|
|
} else {
|
|
//
|
|
// //tag for default text
|
|
retBuf[offset] = (byte) 0x97;
|
|
offset++;
|
|
|
|
// retBuf[offset] = (byte)(9 + (byte)this.wallet.outputSig.length);
|
|
// offset++;
|
|
//
|
|
// retBuf[offset] = (byte)0x04;
|
|
// offset++;
|
|
//
|
|
// Util.arrayCopyNonAtomic(this.wallet.outputSig, (short) 0, retBuf, (short) offset, (short) DERFormatLe);
|
|
// offset += DERFormatLe;
|
|
|
|
// strip out r&s points from the DER encoding
|
|
byte[] derDecodedSig = this.wallet.stripDEREncoding(this.wallet.outputSig);
|
|
// encode r&s as readable text
|
|
byte[] gsmEncodedSig = this.wallet.convertHexArrayToGSM7(derDecodedSig, (byte) 0);
|
|
|
|
// chars > 127
|
|
retBuf[offset] = (byte) 0x81;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 129;
|
|
offset++;
|
|
|
|
retBuf[offset] = (byte) 0x04;
|
|
offset++;
|
|
|
|
Util.arrayCopyNonAtomic(gsmEncodedSig, (short) 0, retBuf, (short) offset, (short) 128);
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
public byte handleResponseSelectItem(byte v) {
|
|
|
|
// // set next fetch response
|
|
// this.selection = v;
|
|
|
|
switch (v) {
|
|
case ((byte) AMOUNT_TO_SEND):
|
|
// todo: grab this from incoming apdu.
|
|
// todo: make this temp buf?
|
|
JCSystem.beginTransaction();
|
|
if (this.nextFetchBuf != null) {
|
|
JCSystem.requestObjectDeletion();
|
|
}
|
|
JCSystem.commitTransaction();
|
|
|
|
// add 10 for default text length adding
|
|
this.nextFetchBuf = new byte[AmtToSendLe];
|
|
|
|
this.handleSetFetchGetInput(this.nextFetchBuf, AMOUNT_TO_SEND);
|
|
|
|
return (byte) AmtToSendLe;
|
|
|
|
case ((byte) ADDRESS_TO_SEND_TO):
|
|
|
|
JCSystem.beginTransaction();
|
|
if (this.nextFetchBuf != null) {
|
|
JCSystem.requestObjectDeletion();
|
|
}
|
|
JCSystem.commitTransaction();
|
|
|
|
this.nextFetchBuf = new byte[AddToSendToLe];
|
|
|
|
this.handleSetFetchGetInput(this.nextFetchBuf, ADDRESS_TO_SEND_TO);
|
|
|
|
return (byte) AddToSendToLe;
|
|
|
|
case ((byte) ENTER_PIN):
|
|
|
|
JCSystem.beginTransaction();
|
|
if (this.nextFetchBuf != null) {
|
|
JCSystem.requestObjectDeletion();
|
|
}
|
|
JCSystem.commitTransaction();
|
|
|
|
this.nextFetchBuf = new byte[EnterPinLe];
|
|
|
|
this.handleSetFetchGetInput(this.nextFetchBuf, ENTER_PIN);
|
|
|
|
return (byte) EnterPinLe;
|
|
|
|
case ((byte) BLIND_SIGN):
|
|
|
|
JCSystem.beginTransaction();
|
|
if (this.nextFetchBuf != null) {
|
|
JCSystem.requestObjectDeletion();
|
|
}
|
|
JCSystem.commitTransaction();
|
|
|
|
this.nextFetchBuf = new byte[BlindSignLe];
|
|
|
|
this.handleSetFetchGetInput(this.nextFetchBuf, BLIND_SIGN);
|
|
|
|
// this.wallet.importKey();
|
|
// this.wallet.signWithKey(this.wallet.importedHash, this.outputSig);
|
|
|
|
return (byte) BlindSignLe;
|
|
|
|
case ((byte) COPY_SIG):
|
|
|
|
JCSystem.beginTransaction();
|
|
if (this.nextFetchBuf != null) {
|
|
JCSystem.requestObjectDeletion();
|
|
}
|
|
JCSystem.commitTransaction();
|
|
|
|
// try {
|
|
// this.wallet.importKey();
|
|
// this.wallet.signWithKey();
|
|
// }catch(Exception e) {
|
|
// ISOException.throwIt((short) 0x9160);
|
|
// }
|
|
this.selection = 4;
|
|
|
|
short bufferLe;
|
|
// allocate the buffer size here we already know if there will be a sig or not
|
|
if (this.wallet.outputSig == null) {
|
|
bufferLe = (short) 37;
|
|
} else {
|
|
// 2 * length to account for nibbles.
|
|
bufferLe = (short) (29 + (2 * this.wallet.outputSig.length));
|
|
|
|
}
|
|
// bufferLe = 31;
|
|
try {
|
|
this.nextFetchBuf = new byte[bufferLe];
|
|
|
|
} catch (ISOException e) {
|
|
ISOException.throwIt((short) 0x9199);
|
|
}
|
|
|
|
this.handleSetFetchGetInput(this.nextFetchBuf, COPY_SIG);
|
|
|
|
return (byte) (31 + 128);
|
|
|
|
}
|
|
|
|
return (byte) 0x00;
|
|
|
|
}
|
|
|
|
public void handleResponseGetInput(byte[] input, byte selection) {
|
|
// parse out input string
|
|
|
|
switch (selection) {
|
|
|
|
case ((byte) AMOUNT_TO_SEND):
|
|
// this.inputAmt = JCSystem.makeTransientByteArray((short) input.length, JCSystem.CLEAR_ON_RESET);
|
|
this.inputAmt = new byte[(byte) input.length];
|
|
Util.arrayCopyNonAtomic(this.inputAmt, (short) 0, input, (short) 0, (short) input.length);
|
|
// length of success string.
|
|
|
|
this.nextFetchLe = (byte) 21;
|
|
|
|
this.nextFetchBuf = JCSystem.makeTransientByteArray((short) this.nextFetchLe, JCSystem.CLEAR_ON_RESET);
|
|
//
|
|
handleSetFetchDisplayText(this.nextFetchBuf, (byte) 0);
|
|
break;
|
|
|
|
case ((byte) BLIND_SIGN):
|
|
// this.inputAddress = JCSystem.makeTransientByteArray((short) input.length, JCSystem.CLEAR_ON_RESET);
|
|
// todo should just be 32
|
|
this.inputHash = new byte[(byte) input.length];
|
|
|
|
Util.arrayCopyNonAtomic(this.inputHash, (short) 0, input, (short) 0, (short) input.length);
|
|
|
|
this.nextFetchLe = (byte) 15;
|
|
break;
|
|
|
|
case ((byte) ADDRESS_TO_SEND_TO):
|
|
// this.inputAddress = JCSystem.makeTransientByteArray((short) input.length, JCSystem.CLEAR_ON_RESET);
|
|
this.inputAddress = new byte[(byte) input.length];
|
|
Util.arrayCopyNonAtomic(this.inputAddress, (short) 0, input, (short) 0, (short) input.length);
|
|
this.nextFetchLe = (byte) 15;
|
|
break;
|
|
|
|
// todo handle pin as transient. etc.
|
|
case ((byte) ENTER_PIN):
|
|
// this.inputPin = JCSystem.makeTransientByteArray((short) input.length, JCSystem.CLEAR_ON_RESET);
|
|
this.inputPin = new byte[(byte) input.length];
|
|
Util.arrayCopyNonAtomic(this.inputPin, (short) 0, input, (short) 0, (short) input.length);
|
|
|
|
this.nextFetchLe = (byte) 15;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// this is a response from the terminal to the card, and indicate selection of
|
|
// items,
|
|
public short handleTerminalResponse(APDU apdu) {
|
|
// terminal response Get Item
|
|
short sw = (short) 0x9000;
|
|
|
|
byte offset = 1;
|
|
|
|
byte buf[] = apdu.getBuffer();
|
|
//
|
|
byte totalLen = buf[4];
|
|
|
|
// take incoming bytes and break it out.
|
|
// this.offset = ISO7816.OFFSET_INS + 4;
|
|
// this.tempBuf = JCSystem.makeTransientByteArray((short) (totalLen+2), JCSystem.CLEAR_ON_RESET);
|
|
// annoyingly need to init temp buf this way.
|
|
|
|
// todo change back to RAM allocation
|
|
// this.tempBuf = new byte[(byte) (totalLen + 32)];
|
|
|
|
this.tempBuf = JCSystem.makeTransientByteArray((short) (totalLen + 2), JCSystem.CLEAR_ON_RESET);
|
|
//
|
|
// // copy the incoming to tempbuf.
|
|
Util.arrayCopyNonAtomic(buf, (short) 4, this.tempBuf, (short) 0, (short) (totalLen + 1));
|
|
//
|
|
this.nextFetchLe = (byte) (totalLen + 2);
|
|
|
|
// // should be 81
|
|
byte cmdDetailTag = this.tempBuf[offset];
|
|
offset++;
|
|
// should always be 3
|
|
byte cmdDetailLe = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
// // usually 1
|
|
byte cmdNumber = this.tempBuf[offset];
|
|
offset++;
|
|
// // select etc
|
|
byte cmdType = this.tempBuf[offset];
|
|
offset++;
|
|
|
|
switch (cmdType) {
|
|
// was initially failing cause its shorter than other TRs.
|
|
case (DISPLAY_TEXT):
|
|
//A response to the display text diag.
|
|
//return the length of the root menu
|
|
sw = Util.makeShort((byte) 0x91, (byte) 0x60);
|
|
//set root menu selection.
|
|
this.selection = 0;
|
|
|
|
return sw;
|
|
}
|
|
|
|
byte cmdQualifier = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
// // devid
|
|
// // should be 02
|
|
byte devIdTag = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
byte devIdLe = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
// // 81/82 SIM <> TERMINAL
|
|
byte devFromId = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
byte devToId = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
// // Result TLV
|
|
byte resTag = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
byte resLe = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
byte resRes = this.tempBuf[offset];
|
|
offset++;
|
|
|
|
// //type 90 is sleect item
|
|
// type 8d for text
|
|
byte trTagType = this.tempBuf[offset];
|
|
offset++;
|
|
//
|
|
byte trTagLe = this.tempBuf[offset];
|
|
offset++;
|
|
|
|
// tag value or first char encoding
|
|
byte trTagV = this.tempBuf[offset];
|
|
offset++;
|
|
|
|
byte nextLe = (byte) 33;
|
|
|
|
// parse out cmd type
|
|
switch (cmdType) {
|
|
case (SELECT_ITEM):
|
|
nextLe = this.handleResponseSelectItem(trTagV);
|
|
sw = Util.makeShort((byte) 0x91, nextLe);
|
|
break;
|
|
|
|
case (GET_INPUT):
|
|
|
|
this.inputAmt = new byte[(byte) (trTagLe - 1)];
|
|
// why do I need to add 4 to offset here? dosent register
|
|
Util.arrayCopyNonAtomic(buf, (short) (offset + 4), this.inputAmt, (short) 0, (short) (trTagLe - 1));
|
|
|
|
if (resRes == 0) {
|
|
// get the input from the TR response
|
|
handleResponseGetInput(this.inputAmt, (byte) 0);
|
|
// byte[] tmp = new byte[(byte)20];
|
|
|
|
// set the fetch to be the success
|
|
// this.offset = 0;
|
|
// handleSetFetchDisplayText(tmp, (byte)0);
|
|
|
|
sw = Util.makeShort((byte) 0x91, (byte) 0x15);
|
|
} else {
|
|
sw = Util.makeShort((byte) 0x91, (byte) Fail.length);
|
|
}
|
|
// sw = (short) 0x9199;
|
|
break;
|
|
|
|
case (DISPLAY_TEXT):
|
|
// success length at least
|
|
// this.nextFetchBuf = new byte[(byte) 0x15];
|
|
// success
|
|
// sw = handleSetFetchDisplayText(this.nextFetchBuf, (byte) 0x00);
|
|
|
|
// okay pressed
|
|
// if(resRes == 0x10) {
|
|
// rset to initial fetch
|
|
// this.initialFetch = true;
|
|
// sw to the length of the initial fetch
|
|
sw = Util.makeShort((byte) 0x91, (byte) 0x60);
|
|
|
|
// }
|
|
|
|
break;
|
|
|
|
}
|
|
return sw;
|
|
|
|
}
|
|
|
|
public void handleFetch(APDU apdu) {
|
|
|
|
byte buf[] = apdu.getBuffer();
|
|
|
|
short totalLen = buf[ISO7816.OFFSET_INS + 3];
|
|
|
|
// init tlv stuff
|
|
short retLen = (short) (totalLen - 2);
|
|
|
|
// initial fetch will be initial STK Services MENU
|
|
if (this.initialFetch == true) {
|
|
this.tempBuf = JCSystem.makeTransientByteArray(totalLen, JCSystem.CLEAR_ON_RESET);
|
|
|
|
this.handleMenuSetup(this.tempBuf, (byte) retLen);
|
|
|
|
this.initialFetch = false;
|
|
|
|
apdu.setOutgoing();
|
|
apdu.setOutgoingLength((short) this.tempBuf.length);
|
|
apdu.sendBytesLong(this.tempBuf, (short) 0, (short) this.tempBuf.length);
|
|
// returns 9000
|
|
return;
|
|
} else if (this.selection == 0) {
|
|
this.tempBuf = JCSystem.makeTransientByteArray(totalLen, JCSystem.CLEAR_ON_RESET);
|
|
// if the selection is 0 indicate that the root menu has been chosen
|
|
|
|
this.handleRootFetch(this.tempBuf, (byte) retLen);
|
|
this.selection++;
|
|
//
|
|
// this.stkSelected = true;
|
|
apdu.setOutgoing();
|
|
apdu.setOutgoingLength((short) this.tempBuf.length);
|
|
apdu.sendBytesLong(this.tempBuf, (short) 0, (short) this.tempBuf.length);
|
|
//
|
|
return;
|
|
// next else handle everyting not initial selection
|
|
} else if (this.selection == 4) {
|
|
// everything else should have been triggered by a terminal response (ie ITEM
|
|
// SELECT) and
|
|
// the next fetch should have been precomputed and saved globally.
|
|
apdu.setOutgoing();
|
|
apdu.setOutgoingLength((short) (31 + 128));
|
|
apdu.sendBytesLong(this.nextFetchBuf, (short) 0, (short) (31 + 128));
|
|
|
|
} else {
|
|
|
|
// everything else should have been triggered by a terminal response (ie ITEM
|
|
// SELECT) and
|
|
// the next fetch should have been precomputed and saved globally.
|
|
apdu.setOutgoing();
|
|
apdu.setOutgoingLength((short) this.nextFetchBuf.length);
|
|
apdu.sendBytesLong(this.nextFetchBuf, (short) 0, (short) this.nextFetchBuf.length);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|