1 /* 2 * Copyright (C) 2021 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 package com.android.server.uwb.secure.iso7816; 17 18 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.OFFSET_CLA; 19 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.OFFSET_INS; 20 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.OFFSET_LC; 21 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.OFFSET_P1; 22 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.OFFSET_P2; 23 24 import androidx.annotation.VisibleForTesting; 25 26 import com.android.server.uwb.util.Hex; 27 28 import com.google.common.base.Objects; 29 import com.google.common.base.Preconditions; 30 import com.google.common.collect.ImmutableSet; 31 import com.google.common.primitives.UnsignedBytes; 32 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.nio.ByteBuffer; 36 import java.util.Arrays; 37 import java.util.Collection; 38 39 import javax.annotation.Nullable; 40 41 /** 42 * Representation of an ISO7816-4 APDU command. Standard and extended length APDUs are supported. 43 * For standard APDUs, the maximum command length is 255 bytes and the maximum response length is 44 * 256 bytes. For extended APDUs, the maximum command length is 65535 bytes and the maximum response 45 * length is 65536 bytes. 46 */ 47 public class CommandApdu { 48 49 /** Sets the Le byte to return as many bytes as possible (all remaining) up to 256. */ 50 public static final int LE_ANY = 0; 51 52 private static final int NO_EXPECTED_RESPONSE = -1; 53 54 private static final int MAX_CDATA_LEN = 255; // As per ISO7816-4 for standard length APDU's. 55 private static final int MAX_RDATA_LEN = 256; // Maximum standard length response. 56 57 // ISO7816-4 APDU components. 58 private final byte mCla; // Class byte. 59 60 private final byte mIns; // Instruction byte. 61 62 private final byte mP1; // Parameter 1. 63 64 private final byte mP2; // Parameter 2. 65 66 private final int mLc; // Length of command data. 67 68 private final int mLe; // Expected length of response. 69 70 private final boolean mForceExtended; 71 72 // cdata is always cloned (an alternative immutable class would cost in performance) 73 private final byte[] mCdata; // Command data. 74 75 private final ImmutableSet<StatusWord> mExpected; 76 77 /** 78 * Constructs a case 4 APDU (Command with data and an expected response). 79 * 80 * <p>When the {@link CommandApdu} instance is created, only the low order byte is used for the 81 * values of cla, ins, p1 and p2. They are int's in the constructor as a convenience only so as 82 * to ensure the caller does not need to cast to a byte. The casting and masking is handled 83 * internally. 84 * 85 * <p>The command data in an {@link CommandApdu} instance is immutable. The data passed in will 86 * be copied and updates to the original data will not be reflected in the {@link CommandApdu} 87 * instance. 88 */ 89 @VisibleForTesting CommandApdu( int cla, int ins, int p1, int p2, @Nullable byte[] cdata, int le, boolean forceExtended, StatusWord... exp)90 CommandApdu( 91 int cla, 92 int ins, 93 int p1, 94 int p2, 95 @Nullable byte[] cdata, 96 int le, 97 boolean forceExtended, 98 StatusWord... exp) { 99 Preconditions.checkArgument(exp.length > 0); 100 101 this.mCla = (byte) (cla & 0xff); 102 this.mIns = (byte) (ins & 0xff); 103 this.mP1 = (byte) (p1 & 0xff); 104 this.mP2 = (byte) (p2 & 0xff); 105 this.mCdata = (cdata != null) ? cdata.clone() : new byte[0]; 106 this.mLc = this.mCdata.length; 107 this.mLe = le; 108 this.mForceExtended = forceExtended; 109 110 Preconditions.checkArgument((mLc >> Short.SIZE) == 0, "Lc must be between 0 and 65,535: %s", 111 mLc); 112 Preconditions.checkArgument( 113 le == NO_EXPECTED_RESPONSE || (le >> Short.SIZE) == 0, 114 "Le must be between 0 and 65,535: %s", 115 le); 116 117 mExpected = ImmutableSet.copyOf(exp); 118 // for now, don't allow any unlisted status words to be set as expected 119 Preconditions.checkArgument(StatusWord.areAllKnown(mExpected)); 120 } 121 122 /** 123 * Gets the encoded byte stream that represents this APDU. 124 * 125 * <p>The encoded form is: <code>cla | 126 * ins | p1 | p2 | lc | data | le</code> 127 */ getEncoded()128 public byte[] getEncoded() { 129 // Minimum APDU length (case 1 APDU's). 130 int len = 4; 131 boolean extended = mForceExtended; 132 133 // Adjust length for any command data. 134 if (mLc > 0) { 135 // Add the data length plus make space for Lc. 136 len += 1 + mLc; 137 138 if (mLc > MAX_CDATA_LEN) { 139 len += 2; // Add room for an extended length APDU. 140 extended = true; 141 } 142 } else { 143 if (mLe > MAX_RDATA_LEN) { 144 extended = true; 145 } 146 } 147 148 // Adjust for Le if present. 149 if (mLe > NO_EXPECTED_RESPONSE) { 150 len++; 151 152 // Check if we need to make Le extended as well. 153 if (extended) { 154 len += 2; 155 } 156 } 157 158 // Create the APDU header. 159 byte[] apdu = new byte[len]; 160 apdu[OFFSET_CLA] = mCla; 161 apdu[OFFSET_INS] = mIns; 162 apdu[OFFSET_P1] = mP1; 163 apdu[OFFSET_P2] = mP2; 164 165 int off = OFFSET_LC; 166 167 // Check to see if data needs to be added to the command. 168 if (mLc > 0) { 169 // Only add Lc if there is data. 170 if (extended) { 171 apdu[off++] = 0; 172 apdu[off++] = (byte) (mLc >> 8); 173 apdu[off++] = (byte) (mLc & 0xff); 174 System.arraycopy(mCdata, 0, apdu, off, mLc); 175 off += mLc; 176 } else { 177 apdu[off++] = (byte) mLc; 178 System.arraycopy(mCdata, 0, apdu, off, mLc); 179 off += mLc; 180 } 181 } 182 183 if (mLe > NO_EXPECTED_RESPONSE) { 184 if (extended) { 185 apdu[off++] = 0; 186 apdu[off++] = (byte) (mLe >> 8); 187 apdu[off++] = (byte) (mLe & 0xff); 188 } else { 189 // When the length is exactly 256, the value is cast to 0x00. 190 // A command expecting no data does not send an Le. 191 apdu[off] = (byte) mLe; 192 } 193 } 194 195 return apdu; 196 } 197 198 /** 199 * Gets the CLA of APDU. 200 */ getCla()201 public byte getCla() { 202 return mCla; 203 } 204 205 /** 206 * Gets the INS of APDU. 207 */ getIns()208 public byte getIns() { 209 return mIns; 210 } 211 212 /** 213 * Gets the P1 of APDU. 214 */ getP1()215 public byte getP1() { 216 return mP1; 217 } 218 219 /** 220 * Gets the P2 of APDU. 221 */ getP2()222 public byte getP2() { 223 return mP2; 224 } 225 226 /** Returns true if this commands expects data back from the card. i.e. If le is >= 0. */ hasLe()227 public boolean hasLe() { 228 return mLe != NO_EXPECTED_RESPONSE; 229 } 230 231 /** 232 * Gets the LE of APDU. 233 */ getLe()234 public int getLe() { 235 Preconditions.checkState(hasLe()); 236 return mLe; 237 } 238 239 /** 240 * Returns a copy of the command data for the APDU. Updates to this copy will not affect the 241 * internal copy in this instance. 242 */ getCommandData()243 public byte[] getCommandData() { 244 return mCdata.clone(); 245 } 246 247 /** 248 * Returns the expected {@link StatusWord} responses for this {@link CommandApdu}. Any {@link 249 * StatusWord} that is expected will not cause an exception to be thrown. 250 */ getExpected()251 public ImmutableSet<StatusWord> getExpected() { 252 return mExpected; 253 } 254 255 /** 256 * Check if the given status word is accepted. 257 */ acceptsStatusWord(StatusWord actual)258 public boolean acceptsStatusWord(StatusWord actual) { 259 return mExpected.contains(actual); 260 } 261 262 /** 263 * check if the status word of the given ResponseApdu is accepted. 264 */ acceptsResponse(ResponseApdu response)265 public boolean acceptsResponse(ResponseApdu response) { 266 return acceptsStatusWord(StatusWord.fromInt(response.getStatusWord())); 267 } 268 269 @Override toString()270 public String toString() { 271 StringWriter stringWriter = new StringWriter(); 272 PrintWriter printWriter = new PrintWriter(stringWriter); 273 274 printWriter.printf("Command : CLA=%02x, INS=%02x, P1=%02x, P2=%02x", mCla, mIns, mP1, mP2); 275 276 if (mLc > 0) { 277 printWriter.printf(", Lc=%04x [%s]", mLc, Hex.encode(mCdata)); 278 } 279 280 if (mLe > NO_EXPECTED_RESPONSE) { 281 printWriter.printf(", Le=%04x", mLe); 282 } 283 284 return stringWriter.toString(); 285 } 286 287 /** 288 * Parses a command APDU and returns an {@link CommandApdu} instance. Currently only supports 289 * standard length APDU's. 290 */ parse(byte[] command)291 public static CommandApdu parse(byte[] command) { 292 ByteBuffer buf = ByteBuffer.wrap(Preconditions.checkNotNull(command)); 293 byte cla = buf.get(); 294 byte ins = buf.get(); 295 byte p1 = buf.get(); 296 byte p2 = buf.get(); 297 298 Builder builder = builder(cla, ins, p1, p2); 299 300 if (buf.hasRemaining()) { 301 int lc = UnsignedBytes.toInt(buf.get()); 302 303 if (!buf.hasRemaining()) { 304 builder.setLe(lc); 305 } else { 306 byte[] cdata = new byte[lc]; 307 buf.get(cdata); 308 builder.setCdata(cdata); 309 310 if (buf.hasRemaining()) { 311 builder.setLe(UnsignedBytes.toInt(buf.get())); 312 } 313 } 314 } 315 316 if (buf.hasRemaining()) { 317 throw new IllegalArgumentException("Invalid APDU: " + Hex.encode(command)); 318 } 319 320 return builder.build(); 321 } 322 323 @Override equals(@ullable Object obj)324 public boolean equals(@Nullable Object obj) { 325 if (obj == null) { 326 return false; 327 } 328 329 if (this.getClass() == obj.getClass()) { 330 CommandApdu other = (CommandApdu) obj; 331 // @formatter:off 332 return this.mCla == other.mCla 333 && this.mIns == other.mIns 334 && this.mP1 == other.mP1 335 && this.mP2 == other.mP2 336 && Arrays.equals(this.mCdata, other.mCdata) 337 && this.mLe == other.mLe 338 && this.mExpected.equals(other.mExpected); 339 // @formatter:on 340 } 341 return false; 342 } 343 344 @Override hashCode()345 public int hashCode() { 346 return Objects.hashCode(mCla, mIns, mP1, mP2, Arrays.hashCode(mCdata), mLe, mExpected); 347 } 348 349 /** 350 * Help method to get the Builder of the CommandApdu. 351 */ builder(int cla, int ins, int p1, int p2)352 public static Builder builder(int cla, int ins, int p1, int p2) { 353 return new Builder(cla, ins, p1, p2); 354 } 355 356 /** Builder for {@link CommandApdu} instances. */ 357 public static class Builder { 358 359 // ISO7816-4 APDU components. 360 private final byte mCla; // Class byte. 361 362 private final byte mIns; // Instruction byte. 363 364 private final byte mP1; // Parameter 1. 365 366 private final byte mP2; // Parameter 2. 367 368 private int mLe = NO_EXPECTED_RESPONSE; // Expected length of response. 369 370 private byte[] mCdata = {}; // Command data. 371 372 @Nullable private StatusWord[] mExpected = null; 373 374 private boolean mForceExtended = false; 375 Builder(int cla, int ins, int p1, int p2)376 private Builder(int cla, int ins, int p1, int p2) { 377 this.mCla = (byte) cla; 378 this.mIns = (byte) ins; 379 this.mP1 = (byte) p1; 380 this.mP2 = (byte) p2; 381 } 382 383 /** 384 * Sets the LE of the CommandApdu. 385 */ setLe(int le)386 public Builder setLe(int le) { 387 this.mLe = le; 388 return this; 389 } 390 391 /** 392 * Sets the data field of the CommandApdu. 393 */ setCdata(byte[] cdata)394 public Builder setCdata(byte[] cdata) { 395 this.mCdata = cdata; 396 return this; 397 } 398 399 /** 400 * Sets the expected status words of the response for the CommandApdu. 401 * Slightly less efficient helper method that makes going from an instance 402 * to a builder easier. 403 */ setExpected(Collection<StatusWord> expected)404 public Builder setExpected(Collection<StatusWord> expected) { 405 return setExpected(expected.toArray(new StatusWord[expected.size()])); 406 } 407 408 /** 409 * Sets the expected status words of the response for the CommandApdu. 410 */ setExpected(StatusWord... expected)411 public Builder setExpected(StatusWord... expected) { 412 Preconditions.checkArgument(expected.length > 0); 413 this.mExpected = expected; 414 return this; 415 } 416 417 /** 418 * Sets the extended length bit of the CommandApdu. 419 */ setExtendedLength()420 public Builder setExtendedLength() { 421 mForceExtended = true; 422 return this; 423 } 424 425 /** 426 * Builds the instance of CommandApdu. 427 */ build()428 public CommandApdu build() { 429 return new CommandApdu( 430 mCla, 431 mIns, 432 mP1, 433 mP2, 434 mCdata, 435 mLe, 436 mForceExtended, 437 mExpected != null ? mExpected : new StatusWord[] {StatusWord.SW_NO_ERROR}); 438 } 439 } 440 } 441