├── emv ├── .classpath ├── .jcop ├── .project └── src │ └── smart │ ├── Pin.java │ ├── Log.java │ ├── ProtocolState.java │ ├── Emv.java │ ├── Crypto.java │ └── FileSystem.java ├── LICENSE └── README.md /emv/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /emv/.jcop: -------------------------------------------------------------------------------- 1 | 1.000000000001.0A0000000032010 -------------------------------------------------------------------------------- /emv/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | emv 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | com.ibm.bluez.jcop.eclipse.jcopbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | com.ibm.bluez.jcop.eclipse.jcopnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Leandro Machado 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EMV Applet for Javacard 2.2.1 2 | 3 | This is a fully working EMV applet for javacard 2.2.1. 4 | 5 | Current STANDARD features are: 6 | - Data access and basic instructions (SELECT, PIN VERIFY, PROCESSING OPTIONS, GENERATE AC) 7 | 8 | Current CUSTOM features are: 9 | - Received APDU logging (for debugging purposes); 10 | - PIN VERIFY always return OK (9000); 11 | - Fixed IAD and AC output (can be "hot swapped" without having to reflash the cap file, using PUT DATA commands); 12 | 13 | Unfortunately, no personalization commands are implemented. You will need to edit the source code to change the EF data. You can also use my other tool, [ArrayEdit](https://github.com/tiosolid/array_edit), to make this task a lot easier. 14 | 15 | # Notice 16 | 17 | This applet was stitched using source code from all over the internet and a lot of my own code. The `Crypto.java` file was entirely made by another person, but I don't remember where I found it, sorry :( 18 | 19 | # Building 20 | 21 | This applet must be built using a very ancient version of Eclipse (INDIGO SR2 3.7.2) and JCOP Tools from IBM (Google is your friend for this one) since I didn't have access to newer jcop cards while testing this. 22 | 23 | # Disclaimer 24 | 25 | Use this applet at your own risk. Im not responsible for anything you do with it. -------------------------------------------------------------------------------- /emv/src/smart/Pin.java: -------------------------------------------------------------------------------- 1 | package smart; 2 | 3 | import javacard.framework.APDU; 4 | import javacard.framework.ISO7816; 5 | import javacard.framework.ISOException; 6 | import javacard.framework.OwnerPIN; 7 | 8 | public class Pin { 9 | private final OwnerPIN pinObject; 10 | final static byte PIN_TRY_LIMIT = (byte) 0x03; 11 | final static byte PIN_SIZE = (byte) 0x02; 12 | 13 | public Pin() { 14 | pinObject = new OwnerPIN(PIN_TRY_LIMIT, PIN_SIZE); 15 | pinObject.update(new byte[] { (byte) 0x12, (byte) 0x34 }, (short) 0, 16 | (byte) 2); 17 | } 18 | 19 | public void verify(APDU apdu) { 20 | byte[] buf = apdu.getBuffer(); 21 | 22 | // ALWAYS return 9000 23 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 24 | 25 | if (buf[ISO7816.OFFSET_P2] != (byte) (0x80)) { 26 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); // we only support 27 | // transaction_data 28 | // PIN 29 | } 30 | if (pinObject.getTriesRemaining() == 0) { 31 | ISOException.throwIt((short) 0x6983); // PIN blocked 32 | return; 33 | } 34 | 35 | /* 36 | * EP: For the code below to be correct, digits in the PIN object need 37 | * to be coded in the same way as in the APDU, ie. using 4 bit words. 38 | */ 39 | 40 | if (pinObject.check(buf, (short) (ISO7816.OFFSET_CDATA + 1), PIN_SIZE)) { 41 | // protocolState.setCVMPerformed(PLAINTEXT_PIN); 42 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 43 | } else { 44 | ISOException.throwIt((short) ((short) (0x63C0) + (short) pinObject 45 | .getTriesRemaining())); 46 | } 47 | } 48 | 49 | public void update(APDU apdu) { 50 | byte[] buf = apdu.getBuffer(); 51 | if ((buf[ISO7816.OFFSET_P1] != (byte) 0x00) 52 | || (buf[ISO7816.OFFSET_P2] != (byte) 0x01)) { 53 | ISOException.throwIt((short) 0x6A86); // '86': Incorrect parameters 54 | // P1-P2 55 | } 56 | // TODO: Check Data size (cant be smaller than 2 bytes) 57 | pinObject.update(buf, (short) (ISO7816.OFFSET_CDATA), (byte) 2); 58 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 59 | } 60 | 61 | public void update(byte[] pin, short offset, byte length) { 62 | pinObject.update(pin, offset, length); 63 | } 64 | 65 | public byte getTriesRemaining() { 66 | return (byte) 0x03; 67 | // return pinObject.getTriesRemaining(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /emv/src/smart/Log.java: -------------------------------------------------------------------------------- 1 | package smart; 2 | 3 | import javacard.framework.Util; 4 | import javacard.framework.ISO7816; 5 | 6 | public class Log { 7 | private boolean log_enabled; 8 | public final static short MAX_LOG_SIZE = 255; 9 | private final byte[] log_array; 10 | private short log_size; 11 | 12 | public Log() { 13 | log_enabled = true; 14 | log_array = new byte[MAX_LOG_SIZE]; 15 | log_size = 0; 16 | } 17 | 18 | // Append data to the end of the log 19 | public void write(byte[] buffer) { 20 | if (!isEnabled() || isLogFull()) { 21 | return; 22 | } // Do nothing if log is disabled or full 23 | 24 | short apdu_size = 4; // CLA - INS - P1 - P2, minimum 25 | if (buffer[ISO7816.OFFSET_LC] != 0x00) { 26 | apdu_size = (short) (apdu_size + buffer[ISO7816.OFFSET_LC] + 1); 27 | } // LC != 0 means we have a data body 28 | 29 | // Check if the message fits in the remaining log space 30 | if (apdu_size > getFreeSpace()) { 31 | return; 32 | } 33 | 34 | // Prepare the message to be inserted atomically 35 | byte[] entry = new byte[apdu_size + 1]; // Extra byte for the length 36 | entry[0] = (byte) apdu_size; 37 | Util.arrayCopy(buffer, (short) 0, entry, (short) 1, apdu_size); 38 | 39 | // Finally, add the message to the log 40 | Util.arrayCopy(entry, (short) 0, log_array, getSize(), 41 | (short) entry.length); 42 | 43 | // Increment the log size 44 | log_size = (short) (log_size + entry.length); 45 | } 46 | 47 | /** Read all the entries in the log */ 48 | public byte[] read() { 49 | return log_array; 50 | } 51 | 52 | // Clear all the log entries 53 | public void clear() { 54 | Util.arrayFillNonAtomic(log_array, (short) 0, MAX_LOG_SIZE, (byte) 0x00); 55 | log_size = (short) 0; 56 | } 57 | 58 | /** Return the actual log size */ 59 | public short getSize() { 60 | return log_size; 61 | } 62 | 63 | /** Return log free space */ 64 | public short getFreeSpace() { 65 | return (short) (MAX_LOG_SIZE - getSize()); 66 | } 67 | 68 | /** Check if the log is full */ 69 | public boolean isLogFull() { 70 | if (log_size < MAX_LOG_SIZE) { 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | // Get if the log facility is enabled or not 78 | public boolean isEnabled() { 79 | return log_enabled; 80 | } 81 | 82 | public void enable() { 83 | log_enabled = true; 84 | } 85 | 86 | public void disable() { 87 | log_enabled = false; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /emv/src/smart/ProtocolState.java: -------------------------------------------------------------------------------- 1 | package smart; 2 | 3 | import javacard.framework.APDU; 4 | import javacard.framework.ISO7816; 5 | import javacard.framework.ISOException; 6 | import javacard.framework.JCSystem; 7 | import javacard.framework.Util; 8 | 9 | public class ProtocolState { 10 | private short atc; 11 | private short lastOnlineATC; 12 | 13 | // constants to record the (persistent) lifecycle state 14 | public static byte PERSONALISATION = (byte) 0x00; 15 | public static byte READY = (byte) 0x01; 16 | public static byte BLOCKED = (byte) 0x02; 17 | 18 | /** 19 | * Volatile protocol state; records if CVM has been performed, and if ACs 20 | * have been generated 21 | */ 22 | private final byte volatileState[]; 23 | 24 | /** 25 | * 2 byte Card Verification Results 26 | */ 27 | private final byte[] cvr; 28 | 29 | public byte getFirstACGenerated() { 30 | return volatileState[1]; 31 | } 32 | 33 | public void setFirstACGenerated(byte ACType) { 34 | volatileState[1] = ACType; 35 | } 36 | 37 | public byte getSecondACGenerated() { 38 | return volatileState[2]; 39 | } 40 | 41 | public void setSecondACGenerated(byte ACType) { 42 | volatileState[2] = ACType; 43 | } 44 | 45 | public byte getCVMPerformed() { 46 | return volatileState[0]; 47 | } 48 | 49 | public void setCVMPerformed(byte CVMType) { 50 | volatileState[0] = CVMType; 51 | } 52 | 53 | public short getATC() { 54 | return (short) 0x00C3; 55 | // return atc; 56 | } 57 | 58 | public void setATC(short newATC) { 59 | atc = newATC; 60 | } 61 | 62 | private void increaseATC() { 63 | // if (atc == MAX) { BLOCK THIS CARD!! }, but we ignore security here 64 | atc = (short) (atc + 1); 65 | } 66 | 67 | public short getLastOnlineATC() { 68 | return lastOnlineATC; 69 | } 70 | 71 | public void setLastOnlineATC(short newLOATC) { 72 | lastOnlineATC = newLOATC; 73 | } 74 | 75 | public ProtocolState() { 76 | volatileState = JCSystem.makeTransientByteArray((short) 3, 77 | JCSystem.CLEAR_ON_DESELECT); 78 | cvr = JCSystem.makeTransientByteArray((short) 2, 79 | JCSystem.CLEAR_ON_DESELECT); 80 | atc = (short) 0x0005; 81 | lastOnlineATC = (short) 0x0005; 82 | } 83 | 84 | /* 85 | * Starts a new session. This resets all session data and increases the ATC, 86 | * but does not generate a session key yet. 87 | */ 88 | public void startNewSession() { 89 | setFirstACGenerated(smart.Emv.NONE); 90 | setSecondACGenerated(smart.Emv.NONE); 91 | setCVMPerformed(smart.Emv.NONE); 92 | increaseATC(); 93 | } 94 | 95 | /* 96 | * Sets the last online ATC equal to the current ATC 97 | */ 98 | public void onlineSessionCompleted() { 99 | lastOnlineATC = atc; 100 | } 101 | 102 | /* 103 | * Returns the 4 byte CVR (Card Verification Results). Details are described 104 | * in Book 3, Annex C7.3 105 | */ 106 | public byte[] getCVR() { 107 | return null; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /emv/src/smart/Emv.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package smart; 5 | 6 | import javacard.framework.Applet; 7 | import javacard.framework.ISOException; 8 | import javacard.framework.ISO7816; 9 | import javacard.framework.APDU; 10 | import javacard.framework.Util; 11 | import javacard.framework.JCSystem; 12 | 13 | /** 14 | * @author EMV 15 | * 16 | */ 17 | 18 | public class Emv extends Applet { 19 | // Constants for the Supported CLAs (afaik, 00 and 80 are a must) 20 | final static byte EMV_CLA = (byte) 0x80; 21 | 22 | // Constants for supported INS (ISO7816) 23 | // Bare minimum 24 | final static byte INS_VERIFY = (byte) 0x20; // EMV 25 | final static byte INS_PUT_DATA = (byte) 0xDA; 26 | final static byte INS_GET_DATA = (byte) 0xCA; // EMV 27 | final static byte INS_SELECT_FILE = (byte) 0xA4; 28 | final static byte INS_READ_RECORD = (byte) 0xB2; // EMV 29 | final static byte INS_WRITE_RECORD = (byte) 0xD2; 30 | final static byte INS_APPEND_RECORD = (byte) 0xE2; 31 | final static byte INS_PIN_UNBLOCK = (byte) 0x24; // EMV 32 | 33 | // Must be supported according to a commercial EMV applet, not sure for 34 | // current application 35 | final static byte INS_EXTERNAL_AUTH = (byte) 0x82; // EMV 36 | final static byte INS_INTERNAL_AUTH = (byte) 0x88; // EMV 37 | final static byte INS_GENERATE_AC = (byte) 0xAE; // Page 57 - EMV Book 38 | final static byte INS_GET_CHALLENGE = (byte) 0x84; // Page 60 - EMV Book 39 | final static byte INS_GET_PROC_OPTIONS = (byte) 0xA8; // Page 63 - EMV Book 40 | 41 | // Custom Instructions 42 | // TODO Add support to an INS to block ATC / LOATC incrementing? 43 | final static byte INS_UPDATE_AC = (byte) 0x72; 44 | final static byte INS_DISABLE_AC_REP = (byte) 0x74; 45 | final static byte INS_CLEAR_LOG = (byte) 0x76; 46 | final static byte INS_DISABLE_IAD_REP = (byte) 0x78; 47 | 48 | /* codes for cryptogram types used in P1 */ 49 | final static byte ARQC_CODE = (byte) 0x80; 50 | final static byte TC_CODE = (byte) 0x40; 51 | final static byte AAC_CODE = (byte) 0x00; 52 | final static byte RFU_CODE = (byte) 0xC0; 53 | 54 | /* types of AC */ 55 | final static byte NONE = (byte) 0x00; 56 | final static byte ARQC = (byte) 0x01; 57 | final static byte TC = (byte) 0x02; 58 | final static byte AAC = (byte) 0x03; 59 | 60 | final Pin pin; 61 | final ProtocolState protocolState; 62 | final FileSystem fileSystem; 63 | final Crypto crypto; 64 | final Log log; 65 | 66 | private final byte[] response; 67 | 68 | private Emv() { 69 | pin = new Pin(); 70 | protocolState = new ProtocolState(); 71 | fileSystem = new FileSystem(); 72 | crypto = new Crypto(this); 73 | log = new Log(); 74 | 75 | response = JCSystem.makeTransientByteArray((short) 256, 76 | JCSystem.CLEAR_ON_DESELECT); 77 | } 78 | 79 | public static void install(byte[] bArray, short bOffset, byte bLength) { 80 | // GP-complaint JavaCard applet registration 81 | new Emv().register(bArray, (short) (bOffset + 1), bArray[bOffset]); 82 | } 83 | 84 | public void process(APDU apdu) { 85 | byte[] buf = apdu.getBuffer(); 86 | 87 | // Good practice: Return 9000 on SELECT 88 | if (selectingApplet()) { 89 | log.write(buf); // Log applet selection 90 | // When selecting the application, returns it description (FCI) 91 | Util.arrayCopyNonAtomic(FileSystem.fci, (byte) 0, buf, 92 | ISO7816.OFFSET_CDATA, (short) FileSystem.fci.length); 93 | apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, 94 | (short) FileSystem.fci.length); 95 | return; 96 | } 97 | 98 | if (buf[ISO7816.OFFSET_INS] != INS_SELECT_FILE) { 99 | log.write(buf); 100 | } // for now only log non select commands to make the log smaller 101 | 102 | // Switch the INS parameter from the APDU 103 | switch (buf[ISO7816.OFFSET_INS]) { 104 | case INS_APPEND_RECORD: 105 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 106 | break; 107 | case INS_EXTERNAL_AUTH: 108 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 109 | break; 110 | case INS_GENERATE_AC: 111 | // get remaining data 112 | short len = (short) (buf[ISO7816.OFFSET_LC] & 0xFF); 113 | if (len != apdu.setIncomingAndReceive()) { 114 | ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); 115 | } 116 | // check for request of CDA signature 117 | if ((buf[ISO7816.OFFSET_P1] & 0x10) == 0x10) { 118 | // CDA signature requested, which we don't support (yet) 119 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); 120 | } 121 | if (protocolState.getFirstACGenerated() == NONE) { 122 | generateFirstAC(apdu, buf); 123 | } else if (protocolState.getSecondACGenerated() == NONE) { 124 | generateSecondAC(apdu, buf); 125 | } else 126 | // trying to generate a third AC 127 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 128 | break; 129 | case INS_GET_CHALLENGE: 130 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 131 | break; 132 | case INS_GET_DATA: 133 | getData(apdu); 134 | break; 135 | case INS_GET_PROC_OPTIONS: 136 | getProcessingOptions(apdu); 137 | break; 138 | case INS_INTERNAL_AUTH: 139 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 140 | break; 141 | case INS_PIN_UNBLOCK: 142 | pin.update(apdu); 143 | break; 144 | case INS_PUT_DATA: 145 | putData(apdu); 146 | break; 147 | case INS_READ_RECORD: 148 | FileSystem.readRecord(apdu); 149 | break; 150 | case INS_SELECT_FILE: 151 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 152 | // FileSystem.selectFile(); 153 | break; 154 | case INS_VERIFY: 155 | pin.verify(apdu); 156 | break; 157 | case INS_WRITE_RECORD: 158 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 159 | break; 160 | case INS_DISABLE_AC_REP: 161 | crypto.disableAcReplication(); 162 | break; 163 | case INS_DISABLE_IAD_REP: 164 | crypto.disableIadReplication(); 165 | case INS_CLEAR_LOG: 166 | log.clear(); 167 | break; 168 | case (byte) 0x00: 169 | break; 170 | default: 171 | // good practice: If you don't know the INStruction, say so: 172 | ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 173 | } 174 | } 175 | 176 | /* 177 | * Process the PUT DATA APDU (INS=DA) PUT DATA is used to store primitive 178 | * data (like values) can be used, for example, to store a new pin retry 179 | * count (if it could be altered via APDUs) 180 | */ 181 | public void putData(APDU apdu) { 182 | /* 183 | * buf[OFFSET_P1..OFFSET_P2] should contains of the following tags 9F36 184 | * - ATC 9F13 - Last online ATC 9F4F - Log Format 9F70 - AC to be 185 | * replicated 9F74 - IAD to be replicated See: EMV BOOK - Page 61 186 | */ 187 | byte[] buf = apdu.getBuffer(); 188 | 189 | if (buf[ISO7816.OFFSET_P1] == (byte) 0x9F) { 190 | // TODO: Check data length (cant be smaller than 2 bytes) 191 | switch (buf[ISO7816.OFFSET_P2]) { 192 | case 0x36: // ATC 193 | protocolState.setATC(Util.makeShort(buf[ISO7816.OFFSET_CDATA], 194 | buf[ISO7816.OFFSET_CDATA + 1])); 195 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 196 | break; 197 | case 0x13: // Last online ATC 198 | protocolState.setLastOnlineATC(Util.makeShort( 199 | buf[ISO7816.OFFSET_CDATA], 200 | buf[ISO7816.OFFSET_CDATA + 1])); 201 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 202 | break; 203 | case 0x70: // AC Replication 204 | crypto.setAc(apdu); 205 | break; 206 | case 0x74: // IAD replication 207 | setIad(apdu); 208 | break; 209 | case 0x4F: // Log Format - not supported yet 210 | default: 211 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); 212 | break; 213 | } 214 | } 215 | } 216 | 217 | /* 218 | * Process the GET DATA APDU (CLA=80 INS=CA) GET DATA is used to read 219 | * primitive data (like the PIN try counter value) 220 | */ 221 | private void getData(APDU apdu) { 222 | /* 223 | * buf[OFFSET_P1..OFFSET_P2] should contains of the following tags 9F36 224 | * - ATC 9F17 - PIN Try Counter 9F13 - Last online ATC 9F4F - Log Format 225 | * 9F72 - Log Data See: EMV BOOK - Page 61 226 | */ 227 | byte[] buf = apdu.getBuffer(); 228 | 229 | if (buf[ISO7816.OFFSET_P1] == (byte) 0x9F) { 230 | buf[0] = (byte) 0x9F; 231 | buf[1] = buf[ISO7816.OFFSET_P2]; 232 | switch (buf[ISO7816.OFFSET_P2]) { 233 | // The buf[ISO7816.OFFSET_P1,ISO7816.OFFSET_P2] already contains the 234 | // right Tag, 235 | // so we can write the Length and Value to the next bytes in the buf 236 | // and then send this. 237 | case 0x36: // ATC 238 | buf[ISO7816.OFFSET_P2 + 1] = (byte) 0x02; // length 2 bytes 239 | Util.setShort(buf, (short) (ISO7816.OFFSET_P2 + 2), 240 | protocolState.getATC()); // value 241 | // send the 5 byte long TLV for ATC 242 | apdu.setOutgoingAndSend(ISO7816.OFFSET_P1, (short) 5); 243 | break; 244 | 245 | case 0x17: // PIN Try Counter 246 | buf[ISO7816.OFFSET_P2 + 1] = (byte) 0x01; // length 1 byte 247 | buf[ISO7816.OFFSET_P2 + 2] = pin.getTriesRemaining(); // value 248 | // send the 4 byte TLV for PIN Try counter 249 | apdu.setOutgoingAndSend(ISO7816.OFFSET_P1, (short) 4); 250 | break; 251 | 252 | case 0x13: // Last online ATC 253 | buf[ISO7816.OFFSET_P2 + 1] = (byte) 0x02; // length 2 bytes 254 | Util.setShort(buf, (short) (ISO7816.OFFSET_P2 + 2), 255 | protocolState.getLastOnlineATC()); // value 256 | // send the 5 byte long TLV for last online ATC 257 | apdu.setOutgoingAndSend(ISO7816.OFFSET_P1, (short) 5); 258 | break; 259 | case 0x72: // Log Data 260 | apdu.setOutgoing(); 261 | apdu.setOutgoingLength(log.getSize()); 262 | apdu.sendBytesLong(log.read(), (short) 0, log.getSize()); 263 | break; 264 | case 0x4F: // Log Format - not supported yet 265 | default: 266 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); 267 | break; 268 | } 269 | } else { 270 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); 271 | } 272 | } 273 | 274 | /* 275 | * Process the GET PROCESSING OPTIONS APDU (CLA=80 INS=A8) returns the 276 | * Application Interchange Profile (AIP) and the Application File Locator 277 | * (AFL) See Page 63 - EMV BOOK 278 | */ 279 | public void getProcessingOptions(APDU apdu) { 280 | byte[] buf = apdu.getBuffer(); 281 | short readCount; 282 | readCount = apdu.setIncomingAndReceive(); 283 | Util.arrayCopyNonAtomic(FileSystem.aip_afl, (byte) 0, buf, 284 | ISO7816.OFFSET_CDATA, (short) FileSystem.aip_afl.length); 285 | apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, 286 | (short) FileSystem.aip_afl.length); 287 | } 288 | 289 | public void generateFirstAC(APDU apdu, byte[] apduBuffer) { 290 | // First 2 bits of P1 specify the type 291 | // These bits also have to be returned, as the Cryptogram Information 292 | // Data (CID); 293 | // See Book 3, Annex C6.5.5.4 294 | byte cid = (byte) (apduBuffer[ISO7816.OFFSET_P1] & 0xC0); 295 | if (cid == RFU_CODE || cid == AAC_CODE) { 296 | // not a request for TC or ARQC 297 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); 298 | } 299 | 300 | crypto.generateFirstACReponse(cid, apduBuffer, 301 | fileSystem.getCDOL1DataLength(), null, (short) 0, response, 302 | (short) 0); 303 | protocolState.setFirstACGenerated(cid); 304 | 305 | apdu.setOutgoing(); 306 | apdu.setOutgoingLength((short) (response[1] + 2)); 307 | apdu.sendBytesLong(response, (short) 0, (short) (response[1] + 2)); 308 | } 309 | 310 | public void generateSecondAC(APDU apdu, byte[] apduBuffer) { 311 | // First 2 bits of P1 specify the type 312 | // These bits also have to be returned, as the Cryptogram Information 313 | // Data (CID); 314 | // See Book 3, Sect 6.5.5.4 of the Common Core Definitions. 315 | byte cid = (byte) (apduBuffer[ISO7816.OFFSET_P1] & 0xC0); 316 | if (cid == RFU_CODE || cid == ARQC_CODE) { 317 | // not a request for TC or AAC 318 | ISOException.throwIt(ISO7816.SW_WRONG_P1P2); 319 | } 320 | 321 | crypto.generateSecondACReponse(cid, apduBuffer, 322 | fileSystem.getCDOL2DataLength(), null, (short) 0, response, 323 | (short) 0); 324 | protocolState.setSecondACGenerated(cid); 325 | 326 | apdu.setOutgoing(); 327 | apdu.setOutgoingLength((short) (response[1] + 2)); 328 | apdu.sendBytesLong(response, (short) 0, (short) (response[1] + 2)); 329 | } 330 | 331 | public void setIad(APDU apdu) { 332 | byte[] buf = apdu.getBuffer(); 333 | if (buf[ISO7816.OFFSET_LC] > (byte) 0x12) { // Max IAD size is 18 bytes 334 | ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); 335 | } 336 | 337 | crypto.setIad(buf); 338 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 339 | } 340 | } -------------------------------------------------------------------------------- /emv/src/smart/Crypto.java: -------------------------------------------------------------------------------- 1 | package smart; 2 | 3 | import javacard.framework.ISO7816; 4 | import javacard.framework.JCSystem; 5 | import javacard.framework.Util; 6 | import javacard.security.DESKey; 7 | import javacard.security.KeyBuilder; 8 | import javacard.security.Signature; 9 | import javacardx.crypto.Cipher; 10 | import javacard.framework.APDU; 11 | 12 | public class Crypto { 13 | 14 | /* Reference back to the applet that uses this EMVCrypto object */ 15 | private final Emv theApplet; 16 | 17 | private final byte[] sessionkey; 18 | 19 | /** 3DESKey ICC Master Key, shared with the bank */ 20 | private final DESKey mk; 21 | 22 | private final Cipher desCipher; 23 | private final Signature desMAC; 24 | 25 | /** 3DESKey session keys, derived from Master Key mk */ 26 | private final DESKey sk; 27 | 28 | /** 29 | * Scratchpad transient byte array for diversification data used to build 30 | * session key 31 | */ 32 | private final byte[] diversification_data; 33 | 34 | /** Transient byte array for storing ac transaction_data */ 35 | byte[] transaction_data; 36 | 37 | // Used to store an injected ac for replication 38 | private final byte[] replicated_ac; 39 | private boolean replicateAc; 40 | 41 | // Used to store an injected IAD for replication 42 | private final byte[] replicated_iad; 43 | private boolean replicate_iad; 44 | private short replicated_iad_length; 45 | 46 | public Crypto(Emv x) { 47 | theApplet = x; // reference back to the applet 48 | 49 | diversification_data = JCSystem.makeTransientByteArray((short) 8, 50 | JCSystem.CLEAR_ON_DESELECT); 51 | sessionkey = JCSystem.makeTransientByteArray((short) 16, 52 | JCSystem.CLEAR_ON_DESELECT); 53 | transaction_data = JCSystem.makeTransientByteArray((short) 256, 54 | JCSystem.CLEAR_ON_DESELECT); 55 | 56 | desCipher = Cipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M2, false); 57 | desMAC = Signature 58 | .getInstance(Signature.ALG_DES_MAC8_ISO9797_M2, false); 59 | 60 | mk = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, 61 | KeyBuilder.LENGTH_DES3_2KEY, false); 62 | mk.setKey(new byte[] { 0x11, 0x0C, 0x1D, 0x02, 0x03, 0x04, 0x0C, 63 | (byte) 0xDA, (byte) 0xFA, (byte) 0xCA, 0x04, 0x11, 0x12, 0x14, 64 | (byte) 0xCA, 0x16 }, (short) 0); 65 | sk = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, 66 | KeyBuilder.LENGTH_DES3_2KEY, false); 67 | 68 | replicated_ac = new byte[] { (byte) 0x4D, (byte) 0xE1, (byte) 0x4B, 69 | (byte) 0xFC, (byte) 0x2F, (byte) 0x73, (byte) 0xBA, (byte) 0xC4 }; 70 | replicateAc = true; 71 | 72 | replicated_iad = new byte[] { (byte) 0x02, (byte) 0x10, (byte) 0xA0, 73 | (byte) 0x00, (byte) 0x03, (byte) 0x22, (byte) 0x00, 74 | (byte) 0x00, (byte) 0x27, (byte) 0x11, (byte) 0x00, 75 | (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 76 | (byte) 0x00, (byte) 0x00, (byte) 0xFF }; 77 | replicate_iad = true; 78 | replicated_iad_length = (short) 18; 79 | } 80 | 81 | /* 82 | * Sets the current 3DES session key, based on the Application Transaction 83 | * Counter (ATC). 84 | * 85 | * It is done as described in Book 2, Annex A1.3.1, by encrypting ATC || F0 86 | * || 00 || 00 || 00 || 00 || 00 with the card's 3DES Master Key to obtain 87 | * the left 8 bytes, and encrypting ATC || OF || 00 || 00 || 00 || 00 || 00 88 | * with the card's 3DES Master Key to obtain the right 8 bytes. 89 | */ 90 | private void setSessionKey() { 91 | // as 8-byte diversification data we take the ATC followed by all zeroes 92 | Util.setShort(diversification_data, (short) 0, 93 | theApplet.protocolState.getATC()); 94 | Util.arrayFillNonAtomic(diversification_data, (short) 2, (short) 6, 95 | (byte) 0); 96 | 97 | desCipher.init(mk, Cipher.MODE_ENCRYPT); 98 | 99 | // compute left 8 bytes of the session key 100 | diversification_data[2] = (byte) 0xF0; 101 | desCipher.doFinal(diversification_data, (short) 0, (short) 8, 102 | sessionkey, (short) 0); 103 | 104 | // compute right 8 byte of the session key 105 | diversification_data[2] = (byte) 0x0F; 106 | desCipher.doFinal(diversification_data, (short) 0, (short) 8, 107 | sessionkey, (short) 0); 108 | 109 | sk.setKey(sessionkey, (short) 0); 110 | } 111 | 112 | /* 113 | * Computes a cryptogram, as described in Book 2, Sec 8.1, and stores it in 114 | * the given response buffer at the given offset. 115 | * 116 | * The cryptogram is an 8 byte MAC over data supplied by the terminal (as 117 | * specified by the CDOL1 or CDOL2) and data provided by the ICC. 118 | * 119 | * The data supplied by the terminal is in the ADPU buffer. This method does 120 | * not need to know what this data is, ie. does not need to know the CDOLs, 121 | * but only needs to know the total length of these data elements. 122 | * 123 | * As data provided by the ICC this method just uses the minimum recommended 124 | * set of data elements, ie the AIP and ATC (see Book 2, Sect 8.1.1), for 125 | * both the first and the second AC. Hence one method can be used for both. 126 | * 127 | * @requires apduBuffer != response, to avoid problems overwriting the 128 | * apduBuffer?? 129 | * 130 | * @param cid the type of AC, ie. AAC_CODE, TC_CODE, or ARCQ_CODE 131 | * 132 | * @param apduBuffer contains the terminal-supplied data to be signed in the 133 | * AC 134 | * 135 | * @param length length of the terminal-supplied data 136 | * 137 | * @param response the destination array where the AC is stored at given 138 | * offset 139 | * 140 | * @param offset offset in this response array 141 | */ 142 | private void computeAC(byte cid, byte[] apduBuffer, short length, 143 | byte[] response, short offset) { 144 | 145 | // Check if replicated AC func is enabled and act accordingly 146 | if (replicateAc == true) { 147 | Util.arrayCopy(replicated_ac, (short) 0, response, offset, 148 | (short) 8); 149 | } else { 150 | /* Collect the data to be MAC-ed in the array transaction_data */ 151 | 152 | // Copy CDOL from the APDU buffer, at offset 0: 153 | Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, transaction_data, 154 | (short) 0, length); 155 | // 2 bytes AIP, at offset length: 156 | Util.setShort(transaction_data, length, 157 | theApplet.fileSystem.getAIP()); 158 | // 2 bytes ATC, at offset length + 2: 159 | Util.setShort(transaction_data, (short) (length + 2), 160 | theApplet.protocolState.getATC()); 161 | 162 | // TODO What is the following data? 163 | transaction_data[(short) (length + 4)] = (byte) 0x80; 164 | transaction_data[(short) (length + 5)] = (byte) 0x0; 165 | transaction_data[(short) (length + 6)] = (byte) 0x0; 166 | 167 | // MAC is a CBC-MAC computed according to ISO/IEC 9797-1, padding 168 | // method 2 169 | desMAC.init(sk, Signature.MODE_SIGN); 170 | desMAC.sign(transaction_data, (short) 0, (short) (length + 7), 171 | response, offset); 172 | } 173 | } 174 | 175 | public void disableAcReplication() { 176 | replicateAc = false; 177 | } 178 | 179 | public void setAc(APDU apdu) { 180 | byte[] buf = apdu.getBuffer(); 181 | 182 | // Store the new AC to be replicated later and enable the functionality 183 | // TODO: Validate the new AC length before trying to save it (array out 184 | // of bounds) 185 | Util.arrayCopy(buf, (short) (ISO7816.OFFSET_CDATA), replicated_ac, 186 | (short) 0, (short) 8); 187 | replicateAc = true; 188 | 189 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 190 | } 191 | 192 | public void disableIadReplication(APDU apdu) { 193 | replicate_iad = false; 194 | apdu.setOutgoingAndSend((short) 0, (short) 0); // return 9000 195 | } 196 | 197 | public void setIad(byte[] buf) { 198 | // Store the new IAD to be replicated later and enable the functionality 199 | if (buf[ISO7816.OFFSET_LC] > (byte) 0x12) { 200 | Util.arrayCopy(buf, (short) (ISO7816.OFFSET_CDATA), replicated_iad, 201 | (short) 0, (short) 18); 202 | replicated_iad_length = (short) 18; 203 | } else { 204 | Util.arrayCopy(buf, (short) (ISO7816.OFFSET_CDATA), replicated_iad, 205 | (short) 0, (short) buf[ISO7816.OFFSET_LC]); 206 | replicated_iad_length = (short) buf[ISO7816.OFFSET_LC]; 207 | } 208 | 209 | replicate_iad = true; 210 | } 211 | 212 | public void disableIadReplication() { 213 | replicate_iad = false; 214 | } 215 | 216 | /* 217 | * Compute the first AC response APDU using Format 1. (See Book 3, Section 218 | * 6.5.5.4.) This method also sets the session key. 219 | * 220 | * The response contains the - CID: Cryptogram Information Data, 1 byte long 221 | * - ATC Application Transaction Counter, 2 bytes long - AC: Application 222 | * Cryptogram, 8 bytes long - optionally, IAD: Issuer Application Data, 30 223 | * bytes long 224 | * 225 | * @param cid the type of AC, ie. AAC_CODE, TC_CODE, or ARCQ_CODE 226 | * 227 | * @param apduBuffer contains the terminal-supplied data 228 | * 229 | * @param length length of the terminal-supplied data 230 | * 231 | * @param iad the IAD, or null, if IAD is omitted 232 | * 233 | * @param response the destination array where the response is stored, at 234 | * given offset 235 | */ 236 | public void generateFirstACReponse(byte cid, byte[] apduBuffer, 237 | short length, byte[] iad, short iad_length, byte[] response, 238 | short offset) { 239 | setSessionKey(); 240 | generateSecondACReponse(cid, apduBuffer, length, iad, iad_length, 241 | response, offset); 242 | } 243 | 244 | /* 245 | * Compute the second AC response APDU using Format 1. (See Book 3, Section 246 | * 6.5.5.4.) 247 | */ 248 | public void generateSecondACReponse(byte cid, byte[] apduBuffer, 249 | short length, byte[] iad, short iad_length, byte[] response, 250 | short offset) { 251 | 252 | // ARQC with CDA - Book 3, page 57, table 11 253 | // if (cid == (byte) 0x90) { 254 | if (true) { 255 | response[offset] = (byte) 0x77; // Tag for Format 2 cryptogram 256 | response[(short) offset + 1] = (byte) 0x00; // Base Length 257 | 258 | // 1 byte CID, the type of AC returned - TAG 9F27 length 01 259 | response[(short) offset + 1] = (byte) (response[(short) offset + 1] + (byte) 4); // Base 260 | // Length 261 | Util.setShort(response, (short) (offset + 2), (short) 0x9F27); // Tag 262 | response[(short) (offset + 4)] = (byte) 0x01; // Length 263 | response[(short) (offset + 5)] = cid; // Value 264 | 265 | // 2 byte ATC - Tag 9F36 length 02 266 | response[(short) offset + 1] = (byte) (response[(short) offset + 1] + (byte) 5); // Base 267 | // Length 268 | Util.setShort(response, (short) (offset + 6), (short) 0x9F36); // Tag 269 | response[(short) (offset + 8)] = (byte) 0x02; // Length 270 | Util.setShort(response, (short) (offset + 9), 271 | theApplet.protocolState.getATC()); // Value 272 | 273 | // the AC itself - Tag 9F26 - length 08 274 | response[(short) offset + 1] = (byte) (response[(short) offset + 1] + (byte) 11); // Base 275 | // Length 276 | Util.setShort(response, (short) (offset + 11), (short) 0x9F26); // Tag 277 | response[(short) (offset + 13)] = (byte) 0x08; // Length 278 | computeAC(cid, apduBuffer, length, response, (short) (offset + 14)); 279 | 280 | // finally we get the IAD - 9F10 - length 18 281 | Util.setShort(response, (short) (offset + 22), (short) 0x9F10); // Tag 282 | if (iad != null) { 283 | response[(short) (offset + 24)] = (byte) iad.length; // Length 284 | Util.arrayCopy(iad, (short) 0, response, (short) (offset + 25), 285 | (short) iad.length); 286 | response[(short) offset + 1] = (byte) (response[(short) offset + 1] 287 | + (byte) 0x03 + (byte) iad.length); // Base Length 288 | } else { 289 | if (replicate_iad == true) { 290 | // We use the IAD set for replication 291 | response[(short) (offset + 24)] = (byte) replicated_iad_length; // Length 292 | Util.arrayCopy(replicated_iad, (short) 0, response, 293 | (short) (offset + 25), replicated_iad_length); 294 | response[(short) offset + 1] = (byte) (response[(short) offset + 1] 295 | + (byte) 0x03 + (byte) replicated_iad_length); // Base 296 | // Length 297 | } else { 298 | // Force an IAD of 18 bytes consisting of all 0s 299 | response[(short) (offset + 24)] = (byte) 0x12; // Length 300 | Util.arrayFillNonAtomic(response, (short) (offset + 25), 301 | (short) 18, (byte) 0x0); 302 | response[(short) offset + 1] = (byte) (response[(short) offset + 1] 303 | + (byte) 0x03 + (byte) 18); // Base Length 304 | } 305 | } 306 | } 307 | // ARQC without CDA (cid 80) - Book 3, page 57, table 11 308 | else { 309 | // Code for old Tag format 80 response 310 | response[offset] = (byte) 0x80; // Tag for Format 1 cryptogram 311 | 312 | if (iad == null) { // Length: 1 byte CID + 2 byte ATC + 8 byte AC = 313 | // 11 314 | response[(short) (offset + 1)] = (byte) 11; 315 | } else { // Length: 1 byte CID + 2 byte ATC + 8 byte AC + iad_length 316 | // byte IAD 317 | response[(short) (offset + 1)] = (byte) (11 + iad_length); 318 | } 319 | // 1 byte CID, ie the type of AC returned - TAG 9F27 320 | response[(short) (offset + 2)] = cid; 321 | 322 | // 2 byte ATC, at offset 3: - 9F36 323 | Util.setShort(response, (short) (offset + 3), 324 | theApplet.protocolState.getATC()); 325 | 326 | // the AC itself - 9F26 327 | computeAC(cid, apduBuffer, length, response, (short) (offset + 5)); 328 | 329 | // finally we get the (optional) IAD - 9F10 330 | if (iad != null) { 331 | Util.arrayCopy(iad, (short) 0, response, (short) (offset + 13), 332 | (short) 18); 333 | } 334 | 335 | // Force an IAD of 18 bytes consisting of all 0s - Needed for 336 | // EMV-CAP reader 337 | response[(short) (offset + 1)] = (byte) 29; 338 | Util.arrayFillNonAtomic(response, (short) (offset + 13), 339 | (short) 18, (byte) 0x0); 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /emv/src/smart/FileSystem.java: -------------------------------------------------------------------------------- 1 | package smart; 2 | 3 | import javacard.framework.APDU; 4 | import javacard.framework.ISO7816; 5 | import javacard.framework.ISOException; 6 | 7 | //import javacard.framework.JCSystem; 8 | 9 | public class FileSystem { 10 | /* 11 | * Usual Structure: MF | |-+ EF 01 | --- Record 01 |-+ EF 02 | --- Record 01 12 | * | --- Record 02 |-+ EF 03 | --- Record 01 (for offline data / EMV reader 13 | * different icon) | --- Record 02 14 | */ 15 | 16 | // File Allocation Table (for now, use a fixed EF limit) 17 | private static final byte EF_1_ID = 1; 18 | private static final byte EF_2_ID = 2; 19 | private static final byte EF_3_ID = 3; 20 | 21 | // Dynamic EFs (will contain the Record Files (as array of bytes too) 22 | private static byte[] ef_1_data; 23 | private static byte[] ef_2_data; 24 | private static byte[] ef_3_data; 25 | 26 | private final static byte[] ef01_r01 = { (byte) 0x70, (byte) 0x33, (byte) 0x57, (byte) 0x13, (byte) 0x41, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0xD2, (byte) 0x10, (byte) 0x72, (byte) 0x06, (byte) 0x14, (byte) 0x71, (byte) 0x09, (byte) 0x40, (byte) 0x90, (byte) 0x87, (byte) 0x0F, (byte) 0x5F, (byte) 0x20, (byte) 0x08, (byte) 0x4A, (byte) 0x4F, (byte) 0x45, (byte) 0x20, (byte) 0x54, (byte) 0x45, (byte) 0x53, (byte) 0x54, (byte) 0x9F, (byte) 0x1F, (byte) 0x10, (byte) 0x31, (byte) 0x34, (byte) 0x37, (byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x34, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30 }; 27 | 28 | private final static byte[] ef01_r02 = { (byte) 0x70, (byte) 0x13, (byte) 0x9F, (byte) 0x08, (byte) 0x02, (byte) 0x00, (byte) 0x8D, (byte) 0x5F, (byte) 0x30, (byte) 0x02, (byte) 0x02, (byte) 0x06, (byte) 0x9F, (byte) 0x42, (byte) 0x02, (byte) 0x09, (byte) 0x86, (byte) 0x9F, (byte) 0x44, (byte) 0x01, (byte) 0x02 }; 29 | 30 | private final static byte[] ef01_r03 = { (byte) 0x70, (byte) 0x30, (byte) 0x8C, (byte) 0x15, (byte) 0x9F, (byte) 0x02, (byte) 0x06, (byte) 0x9F, (byte) 0x03, (byte) 0x06, (byte) 0x9F, (byte) 0x1A, (byte) 0x02, (byte) 0x95, (byte) 0x05, (byte) 0x5F, (byte) 0x2A, (byte) 0x02, (byte) 0x9A, (byte) 0x03, (byte) 0x9C, (byte) 0x01, (byte) 0x9F, (byte) 0x37, (byte) 0x04, (byte) 0x8D, (byte) 0x17, (byte) 0x8A, (byte) 0x02, (byte) 0x9F, (byte) 0x02, (byte) 0x06, (byte) 0x9F, (byte) 0x03, (byte) 0x06, (byte) 0x9F, (byte) 0x1A, (byte) 0x02, (byte) 0x95, (byte) 0x05, (byte) 0x5F, (byte) 0x2A, (byte) 0x02, (byte) 0x9A, (byte) 0x03, (byte) 0x9C, (byte) 0x01, (byte) 0x9F, (byte) 0x37, (byte) 0x04 }; 31 | 32 | private final static byte[] ef01_r04 = { (byte) 0x00 }; 33 | 34 | private final static byte[] ef01_r05 = { (byte) 0x00 }; 35 | 36 | private final static byte[] ef01_r06 = { (byte) 0x00 }; 37 | 38 | private final static byte[] ef02_r01 = { (byte) 0x70, (byte) 0x0E, (byte) 0x5A, (byte) 0x08, (byte) 0x41, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x5F, (byte) 0x34, (byte) 0x01, (byte) 0x00 }; 39 | 40 | private final static byte[] ef02_r02 = { (byte) 0x70, (byte) 0x16, (byte) 0x5F, (byte) 0x24, (byte) 0x03, (byte) 0x21, (byte) 0x07, (byte) 0x31, (byte) 0x9F, (byte) 0x07, (byte) 0x02, (byte) 0xFF, (byte) 0x80, (byte) 0x5F, (byte) 0x28, (byte) 0x02, (byte) 0x00, (byte) 0x76, (byte) 0x5F, (byte) 0x25, (byte) 0x03, (byte) 0x15, (byte) 0x07, (byte) 0x23 }; 41 | 42 | private final static byte[] ef02_r03 = { (byte) 0x70, (byte) 0x81, (byte) 0xE0, (byte) 0x8F, (byte) 0x01, (byte) 0x08, (byte) 0x90, (byte) 0x81, (byte) 0xB0, (byte) 0xBF, (byte) 0x63, (byte) 0xBF, (byte) 0xF5, (byte) 0x61, (byte) 0x97, (byte) 0x0C, (byte) 0x85, (byte) 0xA8, (byte) 0x27, (byte) 0xAD, (byte) 0xAB, (byte) 0xF8, (byte) 0x56, (byte) 0x68, (byte) 0x0B, (byte) 0xD6, (byte) 0x81, (byte) 0xD0, (byte) 0x99, (byte) 0xDD, (byte) 0xDD, (byte) 0x9F, (byte) 0xD4, (byte) 0xD9, (byte) 0xAB, (byte) 0xE5, (byte) 0x85, (byte) 0x09, (byte) 0xEC, (byte) 0x65, (byte) 0x38, (byte) 0x0C, (byte) 0xA5, (byte) 0xEE, (byte) 0x87, (byte) 0x3C, (byte) 0xA4, (byte) 0x9A, (byte) 0x15, (byte) 0x68, (byte) 0xEA, (byte) 0x77, (byte) 0x8B, (byte) 0x0A, (byte) 0x29, (byte) 0x9A, (byte) 0x83, (byte) 0x45, (byte) 0x8A, (byte) 0x8A, (byte) 0x0D, (byte) 0x70, (byte) 0x47, (byte) 0x3F, (byte) 0xE3, (byte) 0x5E, (byte) 0xF1, (byte) 0x36, (byte) 0x98, (byte) 0xBA, (byte) 0x6F, (byte) 0x94, (byte) 0xDF, (byte) 0xAB, (byte) 0x19, (byte) 0x43, (byte) 0xD9, (byte) 0xEF, (byte) 0x75, (byte) 0xAC, (byte) 0x3B, (byte) 0xFE, (byte) 0xC1, (byte) 0x6B, (byte) 0x47, (byte) 0x74, (byte) 0x4D, (byte) 0x32, (byte) 0x02, (byte) 0xA6, (byte) 0x03, (byte) 0x78, (byte) 0x31, (byte) 0x96, (byte) 0x0E, (byte) 0x1A, (byte) 0x2A, (byte) 0xF4, (byte) 0x30, (byte) 0xAE, (byte) 0x41, (byte) 0xDB, (byte) 0xD3, (byte) 0xE9, (byte) 0x63, (byte) 0xE0, (byte) 0x08, (byte) 0xD7, (byte) 0x91, (byte) 0xE7, (byte) 0xDC, (byte) 0x8F, (byte) 0x46, (byte) 0xC2, (byte) 0x54, (byte) 0x24, (byte) 0xFF, (byte) 0xF8, (byte) 0x08, (byte) 0xB5, (byte) 0xE3, (byte) 0xEE, (byte) 0xBC, (byte) 0x96, (byte) 0x0F, (byte) 0x80, (byte) 0xBD, (byte) 0x8E, (byte) 0x0F, (byte) 0x82, (byte) 0xD6, (byte) 0xC1, (byte) 0x98, (byte) 0x14, (byte) 0x00, (byte) 0xA9, (byte) 0xC7, (byte) 0x32, (byte) 0x3E, (byte) 0xE3, (byte) 0x38, (byte) 0x8D, (byte) 0xA4, (byte) 0xFA, (byte) 0xFF, (byte) 0x7B, (byte) 0xFE, (byte) 0x53, (byte) 0xA5, (byte) 0x98, (byte) 0xDA, (byte) 0x10, (byte) 0xC1, (byte) 0xB1, (byte) 0xDE, (byte) 0xF7, (byte) 0x5A, (byte) 0x6F, (byte) 0x7D, (byte) 0xE7, (byte) 0xC8, (byte) 0x71, (byte) 0x25, (byte) 0xC7, (byte) 0xB3, (byte) 0x74, (byte) 0x15, (byte) 0x86, (byte) 0x5F, (byte) 0x6B, (byte) 0xE0, (byte) 0x48, (byte) 0xDB, (byte) 0x66, (byte) 0x0F, (byte) 0x9D, (byte) 0x50, (byte) 0xC9, (byte) 0xB2, (byte) 0x5C, (byte) 0xF8, (byte) 0x1A, (byte) 0xBB, (byte) 0x96, (byte) 0x73, (byte) 0x9F, (byte) 0x32, (byte) 0x01, (byte) 0x03, (byte) 0x92, (byte) 0x24, (byte) 0xF8, (byte) 0xE1, (byte) 0x75, (byte) 0x61, (byte) 0x48, (byte) 0x32, (byte) 0x33, (byte) 0xFA, (byte) 0x5B, (byte) 0x36, (byte) 0x40, (byte) 0xBA, (byte) 0xE1, (byte) 0xCD, (byte) 0x7D, (byte) 0xA5, (byte) 0xEC, (byte) 0x55, (byte) 0xF4, (byte) 0xC4, (byte) 0xB3, (byte) 0x24, (byte) 0x61, (byte) 0xC6, (byte) 0x87, (byte) 0x39, (byte) 0xF8, (byte) 0x79, (byte) 0xBC, (byte) 0x9F, (byte) 0xB8, (byte) 0xD8, (byte) 0xEE, (byte) 0xA9, (byte) 0x0D, (byte) 0x2D }; 43 | 44 | private final static byte[] ef02_r04 = { (byte) 0x70, (byte) 0x1C, (byte) 0x9F, (byte) 0x0E, (byte) 0x05, (byte) 0x2C, (byte) 0x10, (byte) 0x98, (byte) 0x00, (byte) 0x00, (byte) 0x9F, (byte) 0x0F, (byte) 0x05, (byte) 0xD0, (byte) 0x68, (byte) 0x24, (byte) 0xF8, (byte) 0x00, (byte) 0x9F, (byte) 0x0D, (byte) 0x05, (byte) 0xD0, (byte) 0x68, (byte) 0x24, (byte) 0xA8, (byte) 0x00, (byte) 0x9F, (byte) 0x4A, (byte) 0x01, (byte) 0x82 }; 45 | 46 | private final static byte[] ef02_r05 = { (byte) 0x70, (byte) 0x16, (byte) 0x8E, (byte) 0x14, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x42, (byte) 0x01, (byte) 0x41, (byte) 0x03, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 47 | 48 | private final static byte[] ef02_r06 = { (byte) 0x70, (byte) 0x81, (byte) 0xB3, (byte) 0x93, (byte) 0x81, (byte) 0xB0, (byte) 0x8E, (byte) 0x69, (byte) 0xF5, (byte) 0xFA, (byte) 0x9C, (byte) 0xBC, (byte) 0xB6, (byte) 0x5B, (byte) 0x1C, (byte) 0x97, (byte) 0xCE, (byte) 0x3A, (byte) 0xB3, (byte) 0xBC, (byte) 0x5E, (byte) 0xE6, (byte) 0x3C, (byte) 0xB4, (byte) 0x0C, (byte) 0x7A, (byte) 0xF4, (byte) 0x5B, (byte) 0xFE, (byte) 0xED, (byte) 0x4C, (byte) 0x5E, (byte) 0xB7, (byte) 0x1A, (byte) 0xE1, (byte) 0x10, (byte) 0x57, (byte) 0x10, (byte) 0x62, (byte) 0x24, (byte) 0xFD, (byte) 0x78, (byte) 0x24, (byte) 0x80, (byte) 0x8E, (byte) 0xA1, (byte) 0x8B, (byte) 0x38, (byte) 0xF9, (byte) 0x79, (byte) 0x9F, (byte) 0x3C, (byte) 0xF8, (byte) 0x66, (byte) 0x41, (byte) 0xB2, (byte) 0xB0, (byte) 0x8B, (byte) 0xED, (byte) 0x29, (byte) 0x22, (byte) 0xB4, (byte) 0xB3, (byte) 0x77, (byte) 0x31, (byte) 0x3F, (byte) 0x6D, (byte) 0xF1, (byte) 0x19, (byte) 0x13, (byte) 0x3D, (byte) 0xBD, (byte) 0xC9, (byte) 0x3B, (byte) 0x14, (byte) 0x31, (byte) 0x09, (byte) 0xAD, (byte) 0x26, (byte) 0xCE, (byte) 0xBC, (byte) 0x7B, (byte) 0xB4, (byte) 0xF7, (byte) 0x10, (byte) 0x1F, (byte) 0x53, (byte) 0xE4, (byte) 0x99, (byte) 0xCA, (byte) 0x05, (byte) 0x22, (byte) 0x0D, (byte) 0x3C, (byte) 0xC6, (byte) 0xF6, (byte) 0xE3, (byte) 0xE0, (byte) 0x37, (byte) 0x86, (byte) 0xBC, (byte) 0x27, (byte) 0x47, (byte) 0x27, (byte) 0x42, (byte) 0x0D, (byte) 0x3A, (byte) 0x91, (byte) 0x00, (byte) 0xB0, (byte) 0x5F, (byte) 0xE8, (byte) 0x51, (byte) 0x41, (byte) 0x65, (byte) 0x8D, (byte) 0xA7, (byte) 0xD0, (byte) 0x68, (byte) 0x9C, (byte) 0x7C, (byte) 0x20, (byte) 0xF2, (byte) 0x82, (byte) 0xAE, (byte) 0x21, (byte) 0x58, (byte) 0x0F, (byte) 0xC4, (byte) 0x25, (byte) 0x2E, (byte) 0x30, (byte) 0xC6, (byte) 0xFA, (byte) 0x60, (byte) 0xF1, (byte) 0x80, (byte) 0x56, (byte) 0x93, (byte) 0xFB, (byte) 0xEF, (byte) 0x5D, (byte) 0xFF, (byte) 0xEF, (byte) 0xB0, (byte) 0x9A, (byte) 0x11, (byte) 0x04, (byte) 0xEC, (byte) 0x22, (byte) 0x5A, (byte) 0xB7, (byte) 0x9D, (byte) 0xD4, (byte) 0xC8, (byte) 0xCA, (byte) 0xCC, (byte) 0x25, (byte) 0x34, (byte) 0xF6, (byte) 0xEF, (byte) 0x1B, (byte) 0x2D, (byte) 0xB6, (byte) 0xA1, (byte) 0x96, (byte) 0xE2, (byte) 0x1B, (byte) 0x40, (byte) 0xB6, (byte) 0xD3, (byte) 0xAD, (byte) 0x8A, (byte) 0xC0, (byte) 0x99, (byte) 0xA6, (byte) 0x4C, (byte) 0x8C, (byte) 0x70, (byte) 0xBE, (byte) 0xD2, (byte) 0x8C }; 49 | 50 | private final static byte[] ef02_r07 = { (byte) 0x70, (byte) 0x1C, (byte) 0x9F, (byte) 0x0E, (byte) 0x05, (byte) 0x2C, (byte) 0x10, (byte) 0x98, (byte) 0x00, (byte) 0x00, (byte) 0x9F, (byte) 0x0F, (byte) 0x05, (byte) 0xD0, (byte) 0x68, (byte) 0x24, (byte) 0xF8, (byte) 0x00, (byte) 0x9F, (byte) 0x0D, (byte) 0x05, (byte) 0xD0, (byte) 0x68, (byte) 0x24, (byte) 0xA8, (byte) 0x00, (byte) 0x9F, (byte) 0x4A, (byte) 0x01, (byte) 0x82 }; 51 | 52 | private final static byte[] ef02_r08 = { (byte) (byte) 0x70, (byte) 0x16, (byte) 0x8E, (byte) 0x14, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x42, (byte) 0x03, (byte) 0x41, (byte) 0x03, (byte) 0x1E, (byte) 0x03, (byte) 0x1F, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 53 | 54 | private final static byte[] ef02_r09 = { (byte) 0x70, (byte) 0x81, (byte) 0xB3, (byte) 0x93, (byte) 0x81, (byte) 0xB0, (byte) 0x9F, (byte) 0x06, (byte) 0x1E, (byte) 0xEF, (byte) 0x4A, (byte) 0x23, (byte) 0xD0, (byte) 0x1B, (byte) 0x46, (byte) 0x5E, (byte) 0x86, (byte) 0x42, (byte) 0x3C, (byte) 0xBA, (byte) 0xD1, (byte) 0x88, (byte) 0x87, (byte) 0xE2, (byte) 0x43, (byte) 0x02, (byte) 0x9D, (byte) 0x79, (byte) 0x69, (byte) 0x54, (byte) 0x90, (byte) 0xA3, (byte) 0xC2, (byte) 0x1C, (byte) 0xB1, (byte) 0x4E, (byte) 0xFD, (byte) 0x8D, (byte) 0xAC, (byte) 0xAD, (byte) 0x94, (byte) 0x22, (byte) 0x77, (byte) 0x34, (byte) 0xB1, (byte) 0x6D, (byte) 0xCE, (byte) 0x2C, (byte) 0xA3, (byte) 0x65, (byte) 0x0B, (byte) 0x5C, (byte) 0xA0, (byte) 0xC9, (byte) 0x21, (byte) 0x88, (byte) 0x0A, (byte) 0x24, (byte) 0x8E, (byte) 0x04, (byte) 0x40, (byte) 0x15, (byte) 0xA6, (byte) 0x0D, (byte) 0xDA, (byte) 0x42, (byte) 0x08, (byte) 0x03, (byte) 0x57, (byte) 0x1A, (byte) 0x03, (byte) 0x42, (byte) 0x2D, (byte) 0x59, (byte) 0x5F, (byte) 0xD3, (byte) 0xB5, (byte) 0x91, (byte) 0x43, (byte) 0x75, (byte) 0x5B, (byte) 0xD5, (byte) 0x32, (byte) 0x77, (byte) 0x7C, (byte) 0x3A, (byte) 0x25, (byte) 0xAB, (byte) 0x1C, (byte) 0x95, (byte) 0x32, (byte) 0x18, (byte) 0xBF, (byte) 0xF8, (byte) 0xEC, (byte) 0x0E, (byte) 0xFA, (byte) 0xA0, (byte) 0x41, (byte) 0x1B, (byte) 0x6E, (byte) 0x62, (byte) 0xF0, (byte) 0x67, (byte) 0xED, (byte) 0x21, (byte) 0x35, (byte) 0x49, (byte) 0xA1, (byte) 0x27, (byte) 0x30, (byte) 0xCB, (byte) 0x47, (byte) 0x0A, (byte) 0xAE, (byte) 0x59, (byte) 0xB7, (byte) 0x01, (byte) 0x1E, (byte) 0xDE, (byte) 0x7D, (byte) 0x43, (byte) 0x1E, (byte) 0x66, (byte) 0x04, (byte) 0xBF, (byte) 0x72, (byte) 0x97, (byte) 0xD6, (byte) 0x86, (byte) 0x42, (byte) 0x0F, (byte) 0xBF, (byte) 0xB5, (byte) 0x4D, (byte) 0xBE, (byte) 0x44, (byte) 0x74, (byte) 0x95, (byte) 0x67, (byte) 0xE6, (byte) 0x1A, (byte) 0x98, (byte) 0xC1, (byte) 0x7B, (byte) 0x10, (byte) 0x8A, (byte) 0xB8, (byte) 0xF2, (byte) 0x59, (byte) 0xA1, (byte) 0x2E, (byte) 0xAD, (byte) 0x14, (byte) 0xA6, (byte) 0xF7, (byte) 0x00, (byte) 0x06, (byte) 0x31, (byte) 0xB6, (byte) 0x42, (byte) 0x7C, (byte) 0xAD, (byte) 0xFE, (byte) 0x49, (byte) 0xAC, (byte) 0xE9, (byte) 0x71, (byte) 0xBD, (byte) 0xCA, (byte) 0xE6, (byte) 0xAD, (byte) 0x7C, (byte) 0x23, (byte) 0x67, (byte) 0xE3, (byte) 0x58, (byte) 0xA9, (byte) 0x05, (byte) 0xD2, (byte) 0xAD, (byte) 0x7C }; 55 | 56 | private final static byte[] ef04_r01 = { (byte) 0x00 }; 57 | 58 | private static byte[] selectFile; // Will hold the ID of the selected EF (probably not supported for now) 59 | 60 | public final static byte[] fci = { (byte) 0x6F, (byte) 0x81, (byte) 0x88, (byte) 0x84, (byte) 0x07, (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x20, (byte) 0x10, (byte) 0xA5, (byte) 0x29, (byte) 0x87, (byte) 0x01, (byte) 0x02, (byte) 0x50, (byte) 0x0C, (byte) 0x56, (byte) 0x49, (byte) 0x53, (byte) 0x41, (byte) 0x45, (byte) 0x4C, (byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x52, (byte) 0x4F, (byte) 0x4E, (byte) 0x9F, (byte) 0x38, (byte) 0x03, (byte) 0x9F, (byte) 0x1A, (byte) 0x02, (byte) 0x5F, (byte) 0x2D, (byte) 0x02, (byte) 0x70, (byte) 0x74, (byte) 0x9F, (byte) 0x11, (byte) 0x01, (byte) 0x01, (byte) 0x9F, (byte) 0x12, (byte) 0x06, (byte) 0x44, (byte) 0x45, (byte) 0x42, (byte) 0x49, (byte) 0x54, (byte) 0x4F, (byte) 0x87, (byte) 0x01, (byte) 0x02, (byte) 0x50, (byte) 0x0C, (byte) 0x56, (byte) 0x49, (byte) 0x53, (byte) 0x41, (byte) 0x45, (byte) 0x4C, (byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x52, (byte) 0x4F, (byte) 0x4E, (byte) 0x9F, (byte) 0x38, (byte) 0x03, (byte) 0x9F, (byte) 0x1A, (byte) 0x02, (byte) 0x5F, (byte) 0x2D, (byte) 0x02, (byte) 0x70, (byte) 0x74, (byte) 0x9F, (byte) 0x11, (byte) 0x01, (byte) 0x01, (byte) 0x9F, (byte) 0x12, (byte) 0x06, (byte) 0x44, (byte) 0x45, (byte) 0x42, (byte) 0x49, (byte) 0x54, (byte) 0x4F, (byte) 0x87, (byte) 0x01, (byte) 0x02, (byte) 0x50, (byte) 0x0C, (byte) 0x56, (byte) 0x49, (byte) 0x53, (byte) 0x41, (byte) 0x45, (byte) 0x4C, (byte) 0x45, (byte) 0x43, (byte) 0x54, (byte) 0x52, (byte) 0x4F, (byte) 0x4E, (byte) 0x9F, (byte) 0x38, (byte) 0x03, (byte) 0x9F, (byte) 0x1A, (byte) 0x02, (byte) 0x5F, (byte) 0x2D, (byte) 0x04, (byte) 0x70, (byte) 0x74, (byte) 0x65, (byte) 0x6E, (byte) 0x9F, (byte) 0x11, (byte) 0x01, (byte) 0x01, (byte) 0x9F, (byte) 0x12, (byte) 0x06, (byte) 0x44, (byte) 0x45, (byte) 0x42, (byte) 0x49, (byte) 0x54, (byte) 0x4F }; 61 | 62 | public final static byte[] aip_afl = { (byte) 0x80, (byte) 0x0E, 63 | (byte) 0x5C, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x03, 64 | (byte) 0x00, (byte) 0x10, (byte) 0x01, (byte) 0x03, (byte) 0x02, 65 | (byte) 0x10, (byte) 0x04, (byte) 0x05, (byte) 0x01 }; // AIP 66 | 67 | /* 68 | * AIP - TAG 82 5800 (01011000) ---- 4000 SDA supported 1000 Cardholder 69 | * verification supported 0800 Terminal risk management is to be performed 70 | */ 71 | public final short aip = (short) 0x5800; 72 | 73 | /* 74 | * AFL: TAG 94 For now, use a static / fixed AFL TODO: Change the AFL based 75 | * in the actual card data structure 76 | */ 77 | public final static byte[] afl = { 78 | // 080101001001020018010201 = 08010100 10010200 18010201 79 | (byte) 0x08, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x10, 80 | (byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x18, (byte) 0x01, 81 | (byte) 0x02, (byte) 0x01 }; 82 | 83 | // "THE" INS (E2). Append a record (SIMPLE-TLV data) to the end of an EF 84 | public static void appendRecord(byte p2, byte[] data) { 85 | /* 86 | * P2 = EF identifier in SFI format data = record to be appended 87 | */ 88 | 89 | } 90 | 91 | public static void writeRecord(APDU apdu) { 92 | // For now, receives the data and writes it to the EF without any 93 | // validation 94 | byte[] buf = apdu.getBuffer(); 95 | byte data = buf[ISO7816.OFFSET_CDATA]; 96 | 97 | } 98 | 99 | public short getAIP() { 100 | return aip; 101 | } 102 | 103 | public void setAIP(short newAip) { 104 | 105 | } 106 | 107 | public static void selectFile() { 108 | // In theory, a direct select file (instead of an AID) is never used 109 | // When used, should select an EF / DF to read its information 110 | // Comercial EMV suport only the "native" SELECT command 111 | // TODO: Must return somenthing here but it cant be function not 112 | // supported since select is supported (but only and AID) 113 | } 114 | 115 | // TODO: Support only fixed size / quantity of EFs first 116 | public static void createFile() { 117 | 118 | } 119 | 120 | public static void readRecord(APDU apdu) { 121 | // TODO: Change this case to an array of EFs so we can address them by 122 | // position instead of switching 123 | 124 | /* 125 | * TODO: Se voce passa o read como 00B2010C o campo LE "nao existe" e o 126 | * bytesleft = 0, trigando o primeiro condicional do if se voce passa 127 | * qualquer outro campo (mesmo 00, que � "leia tudo que puder") o else � 128 | * trigado a unica forma de trigar bytesleft = 0 � NAO PASSANDO NADA, o 129 | * que � invalido (deveria ser considerado como passado 00 Ou seja: 130 | * colocado um 1 no if para sempre trigar o segundo, mas arrumar 131 | * corretamente para a le esperada 132 | */ 133 | 134 | byte[] buf = apdu.getBuffer(); 135 | short bytesLeft; 136 | short record = buf[ISO7816.OFFSET_P1]; 137 | byte ef = (byte) ((buf[ISO7816.OFFSET_P2] ^ 4) >> 3); 138 | 139 | switch (ef) { 140 | case (byte) 0x01: // three records 141 | switch (record) { 142 | /* 143 | * CASE ANTIGO, ANTES N�O LIDO case (byte)0x01: //three records 144 | * switch (record) { case (byte) 0x01: bytesLeft = 145 | * apdu.setOutgoing(); if (bytesLeft == (short) 1) { 146 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 147 | * (ef01_r01,(short)0,(short)0); } else { 148 | * apdu.setOutgoingLength((short)ef01_r01.length); 149 | * apdu.sendBytesLong (ef01_r01,(short)0,(short)ef01_r01.length); } 150 | */ 151 | case (byte) 0x01: 152 | bytesLeft = apdu.setOutgoing(); 153 | apdu.setOutgoingLength((short) ef01_r01.length); 154 | apdu.sendBytesLong(ef01_r01, (short) 0, (short) ef01_r01.length); 155 | 156 | break; 157 | case (byte) 0x02: 158 | bytesLeft = apdu.setOutgoing(); 159 | apdu.setOutgoingLength((short) ef01_r02.length); 160 | apdu.sendBytesLong(ef01_r02, (short) 0, (short) ef01_r02.length); 161 | 162 | break; 163 | case (byte) 0x03: 164 | bytesLeft = apdu.setOutgoing(); 165 | apdu.setOutgoingLength((short) ef01_r03.length); 166 | apdu.sendBytesLong(ef01_r03, (short) 0, (short) ef01_r03.length); 167 | 168 | break; 169 | case (byte) 0x04: 170 | bytesLeft = apdu.setOutgoing(); 171 | apdu.setOutgoingLength((short) ef01_r04.length); 172 | apdu.sendBytesLong(ef01_r04, (short) 0, (short) ef01_r04.length); 173 | 174 | break; 175 | case (byte) 0x05: 176 | bytesLeft = apdu.setOutgoing(); 177 | apdu.setOutgoingLength((short) ef01_r05.length); 178 | apdu.sendBytesLong(ef01_r05, (short) 0, (short) ef01_r05.length); 179 | 180 | break; 181 | case (byte) 0x06: 182 | bytesLeft = apdu.setOutgoing(); 183 | apdu.setOutgoingLength((short) ef01_r06.length); 184 | apdu.sendBytesLong(ef01_r06, (short) 0, (short) ef01_r06.length); 185 | 186 | break; 187 | } 188 | break; 189 | 190 | case (byte) 0x02: // five records 191 | switch (record) { 192 | case (byte) 0x01: 193 | bytesLeft = apdu.setOutgoing(); 194 | apdu.setOutgoingLength((short) ef02_r01.length); 195 | apdu.sendBytesLong(ef02_r01, (short) 0, (short) ef02_r01.length); 196 | break; 197 | case (byte) 0x02: 198 | bytesLeft = apdu.setOutgoing(); 199 | apdu.setOutgoingLength((short) ef02_r02.length); 200 | apdu.sendBytesLong(ef02_r02, (short) 0, (short) ef02_r02.length); 201 | break; 202 | case (byte) 0x03: 203 | bytesLeft = apdu.setOutgoing(); 204 | apdu.setOutgoingLength((short) ef02_r03.length); 205 | apdu.sendBytesLong(ef02_r03, (short) 0, (short) ef02_r03.length); 206 | 207 | break; 208 | case (byte) 0x04: 209 | bytesLeft = apdu.setOutgoing(); 210 | apdu.setOutgoingLength((short) ef02_r04.length); 211 | apdu.sendBytesLong(ef02_r04, (short) 0, (short) ef02_r04.length); 212 | 213 | break; 214 | case (byte) 0x05: 215 | bytesLeft = apdu.setOutgoing(); 216 | apdu.setOutgoingLength((short) ef02_r05.length); 217 | apdu.sendBytesLong(ef02_r05, (short) 0, (short) ef02_r05.length); 218 | 219 | break; 220 | case (byte) 0x06: 221 | bytesLeft = apdu.setOutgoing(); 222 | apdu.setOutgoingLength((short) ef02_r06.length); 223 | apdu.sendBytesLong(ef02_r06, (short) 0, (short) ef02_r06.length); 224 | 225 | break; 226 | case (byte) 0x07: 227 | bytesLeft = apdu.setOutgoing(); 228 | apdu.setOutgoingLength((short) ef02_r07.length); 229 | apdu.sendBytesLong(ef02_r07, (short) 0, (short) ef02_r07.length); 230 | 231 | break; 232 | 233 | case (byte) 0x08: 234 | bytesLeft = apdu.setOutgoing(); 235 | apdu.setOutgoingLength((short) ef02_r08.length); 236 | apdu.sendBytesLong(ef02_r08, (short) 0, (short) ef02_r08.length); 237 | 238 | break; 239 | 240 | case (byte) 0x09: 241 | bytesLeft = apdu.setOutgoing(); 242 | apdu.setOutgoingLength((short) ef02_r09.length); 243 | apdu.sendBytesLong(ef02_r09, (short) 0, (short) ef02_r09.length); 244 | 245 | break; 246 | } 247 | 248 | break; 249 | case (byte) 0x04: // mostra o pan 250 | bytesLeft = apdu.setOutgoing(); 251 | apdu.setOutgoingLength((short) ef04_r01.length); 252 | apdu.sendBytesLong(ef04_r01, (short) 0, (short) ef04_r01.length); 253 | 254 | break; 255 | } 256 | 257 | /* 258 | * try { bytesLeft = apdu.setOutgoing(); if (bytesLeft == (short) 0) { 259 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 260 | * (fs[ef][record],(short)0,(short)0); } else { 261 | * apdu.setOutgoingLength((short)fs[ef][record].length); 262 | * apdu.sendBytesLong 263 | * (fs[ef][record],(short)0,(short)fs[ef][record].length); } } catch 264 | * (Exception e) { ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND); } 265 | */ 266 | 267 | /* 268 | * switch (buf[ISO7816.OFFSET_P1]) { case (byte) 0x01: bytesLeft = 269 | * apdu.setOutgoing(); if (bytesLeft == (short) 1) { 270 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 271 | * (ef01_r01,(short)0,(short)0); } else { 272 | * apdu.setOutgoingLength((short)ef01_r01.length); apdu.sendBytesLong 273 | * (ef01_r01,(short)0,(short)ef01_r01.length); } break; 274 | * 275 | * case (short) 0x02: bytesLeft = apdu.setOutgoing(); if (bytesLeft == 276 | * (short) 1) { apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 277 | * (ef01_r02,(short)0,(short)0); } else { 278 | * apdu.setOutgoingLength((short)ef01_r02.length); apdu.sendBytesLong 279 | * (ef01_r02,(short)0,(short)ef01_r02.length); } break; case (short) 280 | * 0x03: bytesLeft = apdu.setOutgoing(); if (bytesLeft == (short) 1) { 281 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 282 | * (ef01_r03,(short)0,(short)0); } else { 283 | * apdu.setOutgoingLength((short)ef01_r03.length); apdu.sendBytesLong 284 | * (ef01_r03,(short)0,(short)ef01_r03.length); } break; case (short) 285 | * 0x04: bytesLeft = apdu.setOutgoing(); if (bytesLeft == (short) 1) { 286 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 287 | * (ef01_r04,(short)0,(short)0); } else { 288 | * apdu.setOutgoingLength((short)ef01_r04.length); apdu.sendBytesLong 289 | * (ef01_r04,(short)0,(short)ef01_r04.length); } break; case (short) 290 | * 0x05: bytesLeft = apdu.setOutgoing(); if (bytesLeft == (short) 1) { 291 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 292 | * (ef01_r05,(short)0,(short)0); } else { 293 | * apdu.setOutgoingLength((short)ef01_r05.length); apdu.sendBytesLong 294 | * (ef01_r05,(short)0,(short)ef01_r05.length); } break; case (short) 295 | * 0x06: bytesLeft = apdu.setOutgoing(); if (bytesLeft == (short) 1) { 296 | * apdu.setOutgoingLength((short) 0); apdu.sendBytesLong 297 | * (ef01_r06,(short)0,(short)0); } else { 298 | * apdu.setOutgoingLength((short)ef01_r06.length); apdu.sendBytesLong 299 | * (ef01_r06,(short)0,(short)ef01_r06.length); } break; default: 300 | * ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } 301 | */ 302 | } 303 | 304 | public short getCDOL2DataLength() { 305 | // TODO Check DOL Length Dinamically (TAG / LENGTH - no value) 306 | return 0x11; // 17 bytes 307 | } 308 | 309 | public short getCDOL1DataLength() { 310 | // TODO Check DOL Length Dinamically (TAG / LENGTH - no value) 311 | return 0x20; // 32 bytes 312 | } 313 | } --------------------------------------------------------------------------------