1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.weaver; 18 19import javacard.framework.AID; 20import javacard.framework.APDU; 21import javacard.framework.Applet; 22import javacard.framework.ISO7816; 23import javacard.framework.ISOException; 24import javacard.framework.JCSystem; 25import javacard.framework.Shareable; 26import javacard.framework.Util; 27 28public class Weaver extends Applet { 29 // Keep constants in sync with esed 30 // Uses the full AID which needs to be kept in sync on updates. 31 public static final byte[] CORE_APPLET_AID 32 = new byte[] {(byte) 0xA0, 0x00, 0x00, 0x04, 0x76, 0x57, 0x56, 33 0x52, 0x43, 0x4F, 0x52, 0x45, 0x30, 34 0x01, 0x01, 0x01}; 35 36 public static final byte CORE_APPLET_SLOTS_INTERFACE = 0; 37 38 private Slots mSlots; 39 40 protected Weaver() { 41 register(); 42 } 43 44 /** 45 * Installs this applet. 46 * 47 * @param params the installation parameters 48 * @param offset the starting offset of the parameters 49 * @param length the length of the parameters 50 */ 51 public static void install(byte[] params, short offset, byte length) { 52 new Weaver(); 53 } 54 55 /** 56 * Get a handle on the slots after the applet is registered but before and APDUs are received. 57 */ 58 @Override 59 public boolean select() { 60 mSlots = null; 61 return true; 62 } 63 64 /** 65 * Processes an incoming APDU. 66 * 67 * @param apdu the incoming APDU 68 * @exception ISOException with the response bytes per ISO 7816-4 69 */ 70 @Override 71 public void process(APDU apdu) { 72 // TODO(drewry,ascull) Move this back into select. 73 if (mSlots == null) { 74 AID coreAid = JCSystem.lookupAID(CORE_APPLET_AID, (short) 0, (byte) CORE_APPLET_AID.length); 75 if (coreAid == null) { 76 ISOException.throwIt((short)0x0010); 77 } 78 79 mSlots = (Slots) JCSystem.getAppletShareableInterfaceObject( 80 coreAid, CORE_APPLET_SLOTS_INTERFACE); 81 if (mSlots == null) { 82 ISOException.throwIt((short)0x0012); 83 } 84 } 85 86 87 final byte buffer[] = apdu.getBuffer(); 88 final byte cla = buffer[ISO7816.OFFSET_CLA]; 89 final byte ins = buffer[ISO7816.OFFSET_INS]; 90 91 // Handle standard commands 92 if (apdu.isISOInterindustryCLA()) { 93 switch (ins) { 94 case ISO7816.INS_SELECT: 95 // Do nothing, successfully 96 return; 97 default: 98 ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 99 } 100 } 101 102 // Handle custom applet commands 103 switch (ins) { 104 case Consts.INS_GET_NUM_SLOTS: 105 getNumSlots(apdu); 106 return; 107 108 case Consts.INS_WRITE: 109 write(apdu); 110 return; 111 112 case Consts.INS_READ: 113 read(apdu); 114 return; 115 116 case Consts.INS_ERASE_VALUE: 117 eraseValue(apdu); 118 return; 119 120 case Consts.INS_ERASE_ALL: 121 eraseAll(apdu); 122 return; 123 124 default: 125 ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 126 } 127 } 128 129 /** 130 * Get the number of slots. 131 * 132 * p1: 0 133 * p2: 0 134 * data: _ 135 */ 136 private void getNumSlots(APDU apdu) { 137 p1p2Unused(apdu); 138 //dataUnused(apdu); 139 // TODO(ascull): how to handle the cases of APDU properly? 140 prepareToSend(apdu, (short) 4); 141 apdu.setOutgoingLength((short) 4); 142 143 final byte buffer[] = apdu.getBuffer(); 144 Util.setShort(buffer, (short) 0, (short) 0); 145 Util.setShort(buffer, (short) 2, mSlots.getNumSlots()); 146 147 apdu.sendBytes((short) 0, (byte) 4); 148 } 149 150 public static final short WRITE_DATA_BYTES 151 = Consts.SLOT_ID_BYTES + Consts.SLOT_KEY_BYTES + Consts.SLOT_VALUE_BYTES; 152 private static final byte WRITE_DATA_SLOT_ID_OFFSET = ISO7816.OFFSET_CDATA; 153 private static final byte WRITE_DATA_KEY_OFFSET 154 = WRITE_DATA_SLOT_ID_OFFSET + Consts.SLOT_ID_BYTES; 155 private static final byte WRITE_DATA_VALUE_OFFSET 156 = WRITE_DATA_KEY_OFFSET + Consts.SLOT_KEY_BYTES; 157 158 /** 159 * Write to a slot. 160 * 161 * p1: 0 162 * p2: 0 163 * data: [slot ID] [key data] [value data] 164 */ 165 private void write(APDU apdu) { 166 p1p2Unused(apdu); 167 receiveData(apdu, WRITE_DATA_BYTES); 168 169 final byte buffer[] = apdu.getBuffer(); 170 final short slotId = getSlotId(buffer, WRITE_DATA_SLOT_ID_OFFSET); 171 mSlots.write(slotId, buffer, WRITE_DATA_KEY_OFFSET, buffer, WRITE_DATA_VALUE_OFFSET); 172 } 173 174 public static final short READ_DATA_BYTES 175 = Consts.SLOT_ID_BYTES + Consts.SLOT_KEY_BYTES; 176 private static final byte READ_DATA_SLOT_ID_OFFSET = ISO7816.OFFSET_CDATA; 177 private static final byte READ_DATA_KEY_OFFSET 178 = WRITE_DATA_SLOT_ID_OFFSET + Consts.SLOT_ID_BYTES; 179 180 /** 181 * Read a slot. 182 * 183 * p1: 0 184 * p2: 0 185 * data: [slot ID] [key data] 186 */ 187 private void read(APDU apdu) { 188 final byte successSize = 1 + Consts.SLOT_VALUE_BYTES; 189 final byte failSize = 1 + 4; 190 191 p1p2Unused(apdu); 192 receiveData(apdu, READ_DATA_BYTES); 193 prepareToSend(apdu, successSize); 194 195 final byte buffer[] = apdu.getBuffer(); 196 final short slotId = getSlotId(buffer, READ_DATA_SLOT_ID_OFFSET); 197 198 final byte err = mSlots.read(slotId, buffer, READ_DATA_KEY_OFFSET, buffer, (short) 1); 199 buffer[(short) 0] = err; 200 if (err == Consts.READ_SUCCESS) { 201 apdu.setOutgoingLength(successSize); 202 apdu.sendBytes((short) 0, successSize); 203 } else { 204 apdu.setOutgoingLength(failSize); 205 apdu.sendBytes((short) 0, failSize); 206 } 207 } 208 209 public static final short ERASE_VALUE_BYTES = Consts.SLOT_ID_BYTES; 210 private static final byte ERASE_VALUE_SLOT_ID_OFFSET = ISO7816.OFFSET_CDATA; 211 212 /** 213 * Erase the value of a slot. 214 * 215 * p1: 0 216 * p2: 0 217 * data: [slot ID] 218 */ 219 private void eraseValue(APDU apdu) { 220 p1p2Unused(apdu); 221 receiveData(apdu, ERASE_VALUE_BYTES); 222 223 final byte buffer[] = apdu.getBuffer(); 224 final short slotId = getSlotId(buffer, READ_DATA_SLOT_ID_OFFSET); 225 mSlots.eraseValue(slotId); 226 } 227 228 /** 229 * Erase all slots. 230 * 231 * p1: 0 232 * p2: 0 233 * data: _ 234 */ 235 private void eraseAll(APDU apdu) { 236 p1p2Unused(apdu); 237 dataUnused(apdu); 238 mSlots.eraseAll(); 239 } 240 241 /** 242 * Check that the parameters are 0. 243 * 244 * They are not being used but should be under control. 245 */ 246 private void p1p2Unused(APDU apdu) { 247 final byte buffer[] = apdu.getBuffer(); 248 if (buffer[ISO7816.OFFSET_P1] != 0 || buffer[ISO7816.OFFSET_P2] != 0) { 249 ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); 250 } 251 } 252 253 /** 254 * Check that no data was provided. 255 */ 256 private void dataUnused(APDU apdu) { 257 receiveData(apdu, (short) 0); 258 } 259 260 /** 261 * Calls setIncomingAndReceive() on the APDU and checks the length is as expected. 262 */ 263 private void receiveData(APDU apdu, short expectedLength) { 264 final short bytesRead = apdu.setIncomingAndReceive(); 265 if (apdu.getIncomingLength() != expectedLength || bytesRead != expectedLength) { 266 ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); 267 } 268 } 269 270 /** 271 * The slot ID in the API is 32-bits but the applet works with 16-bit IDs. 272 */ 273 private short getSlotId(byte[] bArray, short bOff) { 274 if (bArray[bOff] != 0 || bArray[(short) (bOff + 1)] != 0) { 275 ISOException.throwIt(Consts.SW_INVALID_SLOT_ID); 276 } 277 return Util.getShort(bArray,(short) (bOff + 2)); 278 } 279 280 /** 281 * Calls setOutgoing() on the APDU, checks the length is as expected and calls 282 * setOutgoingLength() with that length. 283 * 284 * Still need to call setOutgoingLength() after this method. 285 */ 286 private void prepareToSend(APDU apdu, short expectedMaxLength) { 287 final short outDataLen = apdu.setOutgoing(); 288 if (outDataLen != expectedMaxLength) { 289 ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); 290 } 291 } 292} 293