diff options
author | Yoshiaki Naka <yoshiaki.naka@sony.com> | 2018-02-06 04:28:53 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-02-06 04:28:53 +0000 |
commit | 0c8c1368ece62a46daed16f5319d394244864939 (patch) | |
tree | 5657344bbbda326e80d838c241b0894f215a342e | |
parent | 799af455634b182e94042242c726ef77411aa909 (diff) | |
parent | 2fc68caa3970a157f0052ba6f692aac8c61706f1 (diff) | |
download | SecureElement-0c8c1368ece62a46daed16f5319d394244864939.tar.gz |
Validate the APDU command before sending it to the lower layer am: 77676c0353 am: 966827ad23
am: 2fc68caa39
Change-Id: Ibc57f6e211ba65a065a662b2e107cb86fef13974
-rw-r--r--[-rwxr-xr-x] | src/com/android/se/Channel.java | 11 | ||||
-rw-r--r-- | src/com/android/se/CommandApduValidator.java | 130 |
2 files changed, 133 insertions, 8 deletions
diff --git a/src/com/android/se/Channel.java b/src/com/android/se/Channel.java index 2d6eaa2..56d4f9d 100755..100644 --- a/src/com/android/se/Channel.java +++ b/src/com/android/se/Channel.java @@ -119,9 +119,9 @@ public class Channel implements IBinder.DeathRecipient { if (mChannelAccess.getCallingPid() != mCallingPid) { throw new SecurityException("Wrong Caller PID."); } - if (command.length < 4) { - throw new IllegalArgumentException("Command too short"); - } + + // Validate the APDU command format and throw IllegalArgumentException, if necessary. + CommandApduValidator.execute(command); if (((command[0] & (byte) 0x80) == 0) && ((command[0] & (byte) 0x60) != (byte) 0x20)) { @@ -132,11 +132,6 @@ public class Channel implements IBinder.DeathRecipient { if ((command[1] == (byte) 0xA4) && (command[2] == (byte) 0x04)) { throw new SecurityException("SELECT by DF name command not allowed"); } - } else if (command[0] == (byte) 0xFF) { - throw new IllegalArgumentException("CLA(0xFF) not allowed"); - } else if ((command[1] == (byte) 0x9F) || (command[1] == (byte) 0x6F)) { - throw new IllegalArgumentException("the command INS is invalid " - + "(INS = 0x6F or INS = 0x9F)"); } checkCommand(command); diff --git a/src/com/android/se/CommandApduValidator.java b/src/com/android/se/CommandApduValidator.java new file mode 100644 index 0000000..dc4a8be --- /dev/null +++ b/src/com/android/se/CommandApduValidator.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Contributed by: Giesecke & Devrient GmbH. + */ + +package com.android.se; + +/** + * Validates APDU command format and throw IllegalArgumentException, if anything is wrong. + */ +public class CommandApduValidator { + + private static final int CMD_APDU_LENGTH_CASE1 = 4; + private static final int CMD_APDU_LENGTH_CASE2 = 5; + private static final int CMD_APDU_LENGTH_CASE2_EXTENDED = 7; + private static final int CMD_APDU_LENGTH_CASE3_WITHOUT_DATA = 5; + private static final int CMD_APDU_LENGTH_CASE3_WITHOUT_DATA_EXTENDED = 7; + private static final int CMD_APDU_LENGTH_CASE4_WITHOUT_DATA = 6; + private static final int CMD_APDU_LENGTH_CASE4_WITHOUT_DATA_EXTENDED = 9; + + private static final int MAX_EXPECTED_DATA_LENGTH = 65536; + + private static final int OFFSET_CLA = 0; + private static final int OFFSET_INS = 1; + private static final int OFFSET_P3 = 4; + private static final int OFFSET_DATA = 5; + private static final int OFFSET_DATA_EXTENDED = 7; + + private CommandApduValidator() { + } + + /** + * Executes the validation for the specified APDU command. + * + * @param apdu a command APDU as byte array. + * + * @throws IllegalArgumentException If the command does not follow the APDU command format. + */ + public static void execute(byte[] apdu) throws IllegalArgumentException { + if (apdu.length < CMD_APDU_LENGTH_CASE1) { + throw new IllegalArgumentException("Invalid length for command (" + apdu.length + ")."); + } + checkCla(apdu[OFFSET_CLA]); + checkIns(apdu[OFFSET_INS]); + + if (apdu.length == CMD_APDU_LENGTH_CASE1) { + return; // Case 1 + } + + if (apdu.length == CMD_APDU_LENGTH_CASE2) { + checkLe((int) 0x0FF & apdu[OFFSET_P3]); + return; // Case 2S + } + + if (apdu[OFFSET_P3] != (byte) 0x00) { + int lc = ((int) 0x0FF & apdu[OFFSET_P3]); + if (apdu.length == CMD_APDU_LENGTH_CASE3_WITHOUT_DATA + lc) { + return; // Case 3S + } + if (apdu.length == CMD_APDU_LENGTH_CASE4_WITHOUT_DATA + lc) { + checkLe((int) 0x0FF & apdu[apdu.length - 1]); + return; // Case 4S + } + throw new IllegalArgumentException("Unexpected value of Lc (" + lc + ")"); + } + + if (apdu.length == CMD_APDU_LENGTH_CASE2_EXTENDED) { + checkLe((((int) 0x0FF & apdu[OFFSET_DATA]) << 8) + + ((int) 0x0FF & apdu[OFFSET_DATA + 1])); + return; // Case 2E + } + + if (apdu.length <= OFFSET_DATA_EXTENDED) { + throw new IllegalArgumentException("Unexpected value of Lc or Le" + apdu.length); + } + + int lc = (((int) 0x0FF & apdu[OFFSET_DATA]) << 8) + ((int) 0x0FF & apdu[OFFSET_DATA + 1]); + if (lc == 0) { + throw new IllegalArgumentException("Lc can't be 0"); + } + + if (apdu.length == CMD_APDU_LENGTH_CASE3_WITHOUT_DATA_EXTENDED + + lc) { + return; // Case 3E + } + + if (apdu.length == CMD_APDU_LENGTH_CASE4_WITHOUT_DATA_EXTENDED + lc) { + checkLe((((int) 0x0FF & apdu[apdu.length - 2]) << 8) + + ((int) 0x0FF & apdu[apdu.length - 1])); + return; // Case 4E + } + throw new IllegalArgumentException("Unexpected value of Lc (" + lc + ")"); + } + + private static void checkCla(byte cla) throws IllegalArgumentException { + if (cla == (byte) 0xFF) { + throw new IllegalArgumentException( + "Invalid value of CLA (" + Integer.toHexString(cla) + ")"); + } + } + + private static void checkIns(byte ins) throws IllegalArgumentException { + if ((ins & 0x0F0) == 0x60 || ((ins & 0x0F0) == 0x90)) { + throw new IllegalArgumentException( + "Invalid value of INS (" + Integer.toHexString(ins) + "). " + + "0x6X and 0x9X are not valid values"); + } + } + + private static void checkLe(int le) throws IllegalArgumentException { + if (le < 0 || le > MAX_EXPECTED_DATA_LENGTH) { + throw new IllegalArgumentException( + "Invalid value for le parameter (" + le + ")."); + } + } +} |