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