Support configurable AIDs for ARAM
1. Try to retrieve ARs from a configurable list of AIDs.
If all AIDs are not accessible, then try default AID.
2. Add PKG_REF_DO/PERM_AR_DO for AR parsing.
Bug:139078767
Test: Access rules could be retrieved from different AIDs.
Change-Id: Id14418ebd39646d4b50e2ad240f50bc74507f816
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..cc310a0
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- The list of AIDs are the candidate of the ARA AID in ESE.
+ The first available AID will be taken as the ARA AID. -->
+ <string-array name="config_ara_aid_candidate_list_ese" translatable="false" />
+</resources>
diff --git a/src/com/android/se/Terminal.java b/src/com/android/se/Terminal.java
index e17ac88..0062206 100644
--- a/src/com/android/se/Terminal.java
+++ b/src/com/android/se/Terminal.java
@@ -712,6 +712,10 @@
return mAccessControlEnforcer;
}
+ public Context getContext() {
+ return mContext;
+ }
+
/** Dump data for debug purpose . */
public void dump(PrintWriter writer) {
writer.println("SECURE ELEMENT SERVICE TERMINAL: " + mName);
diff --git a/src/com/android/se/security/AccessControlEnforcer.java b/src/com/android/se/security/AccessControlEnforcer.java
index 22b82f0..90843a8 100644
--- a/src/com/android/se/security/AccessControlEnforcer.java
+++ b/src/com/android/se/security/AccessControlEnforcer.java
@@ -84,7 +84,10 @@
mAccessRuleCache = new AccessRuleCache();
}
- public static byte[] getDefaultAccessControlAid() {
+ public byte[] getDefaultAccessControlAid() {
+ if (mAraController != null) {
+ return mAraController.getAccessControlAid();
+ }
return AraController.getAraMAid();
}
@@ -454,6 +457,14 @@
writer.println("mUseArf: " + mUseArf);
writer.println("mUseAra: " + mUseAra);
+ if (mUseAra && mAraController != null) {
+ if (getDefaultAccessControlAid() == null) {
+ writer.println("AraInUse: default applet");
+ } else {
+ writer.println("AraInUse: " + ByteArrayConverter.byteArrayToHexString(
+ getDefaultAccessControlAid()));
+ }
+ }
writer.println("mInitialChannelAccess:");
writer.println(mInitialChannelAccess.toString());
writer.println();
diff --git a/src/com/android/se/security/ara/AraController.java b/src/com/android/se/security/ara/AraController.java
index bc4ea19..52223b6 100644
--- a/src/com/android/se/security/ara/AraController.java
+++ b/src/com/android/se/security/ara/AraController.java
@@ -35,10 +35,15 @@
package com.android.se.security.ara;
+import android.content.Context;
+import android.text.TextUtils;
import android.util.Log;
import com.android.se.Channel;
+import com.android.se.R;
+import com.android.se.SecureElementService;
import com.android.se.Terminal;
+import com.android.se.internal.ByteArrayConverter;
import com.android.se.security.AccessRuleCache;
import com.android.se.security.ChannelAccess;
import com.android.se.security.gpac.BerTlv;
@@ -73,27 +78,65 @@
private AccessRuleCache mAccessRuleCache = null;
private Terminal mTerminal = null;
private AccessRuleApplet mApplet = null;
+ private Context mContext = null;
+ private String[] mAids = new String[0];
+ private byte[] mAccessControlAid = ARA_M_AID;
public AraController(AccessRuleCache cache, Terminal terminal) {
mAccessRuleCache = cache;
mTerminal = terminal;
+ mContext = mTerminal.getContext();
+ if (mTerminal.getName().startsWith(SecureElementService.ESE_TERMINAL)) {
+ mAids = mContext.getResources().getStringArray(
+ R.array.config_ara_aid_candidate_list_ese);
+ }
}
public static byte[] getAraMAid() {
return ARA_M_AID;
}
+ public byte[] getAccessControlAid() {
+ return mAccessControlAid;
+ }
+
/**
* Initialize the AraController, reads the refresh tag.
* and fetch the access rules
*/
public synchronized void initialize() throws IOException, NoSuchElementException {
- Channel channel = mTerminal.openLogicalChannelWithoutChannelAccess(getAraMAid());
- if (channel == null) {
- throw new MissingResourceException("could not open channel", "", "");
+ Channel channel = null;
+ byte[] aid = null;
+
+ // try to fetch access rules from ARA Aid list
+ for (String araAid : mAids) {
+ if (!TextUtils.isEmpty(araAid)) {
+ aid = ByteArrayConverter.hexStringToByteArray(araAid);
+ } else {
+ aid = null;
+ }
+ try {
+ channel = mTerminal.openLogicalChannelWithoutChannelAccess(aid);
+ if (channel == null) {
+ throw new MissingResourceException("could not open channel", "", "");
+ }
+ mAccessControlAid = aid;
+ break;
+ } catch (NoSuchElementException e) {
+ Log.i(mTag, "applet:" + araAid + " is not accessible");
+ continue;
+ }
}
- // set access conditions to access ARA-M.
+ // try to fetch access rules from ARA-M if all ARA in Aid list is not accessible
+ if (channel == null) {
+ channel = mTerminal.openLogicalChannelWithoutChannelAccess(getAraMAid());
+ if (channel == null) {
+ throw new MissingResourceException("could not open channel", "", "");
+ }
+ }
+
+ // set access conditions to access ARA.
ChannelAccess araChannelAccess = new ChannelAccess();
araChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
araChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
@@ -104,7 +147,7 @@
mApplet = new AccessRuleApplet(channel);
byte[] tag = mApplet.readRefreshTag();
// if refresh tag is equal to the previous one it is not
- // neccessary to read all rules again.
+ // necessary to read all rules again.
if (mAccessRuleCache.isRefreshTagEqual(tag)) {
Log.i(mTag, "Refresh tag unchanged. Using access rules from cache.");
return;
diff --git a/src/com/android/se/security/gpac/AR_DO.java b/src/com/android/se/security/gpac/AR_DO.java
index ebb5a6e..491b099 100755
--- a/src/com/android/se/security/gpac/AR_DO.java
+++ b/src/com/android/se/security/gpac/AR_DO.java
@@ -49,15 +49,31 @@
private APDU_AR_DO mApduAr = null;
private NFC_AR_DO mNfcAr = null;
+ private PERM_AR_DO mPermAr = null;
public AR_DO(byte[] rawData, int valueIndex, int valueLength) {
super(rawData, TAG, valueIndex, valueLength);
}
+ public AR_DO(APDU_AR_DO apduArDo, NFC_AR_DO nfcArDo, PERM_AR_DO permArDo) {
+ super(null, TAG, 0, 0);
+ mApduAr = apduArDo;
+ mNfcAr = nfcArDo;
+ mPermAr = permArDo;
+ }
+
public AR_DO(APDU_AR_DO apduArDo, NFC_AR_DO nfcArDo) {
super(null, TAG, 0, 0);
mApduAr = apduArDo;
mNfcAr = nfcArDo;
+ mPermAr = null;
+ }
+
+ public AR_DO(PERM_AR_DO permArDo) {
+ super(null, TAG, 0, 0);
+ mApduAr = null;
+ mNfcAr = null;
+ mPermAr = permArDo;
}
public APDU_AR_DO getApduArDo() {
@@ -68,19 +84,27 @@
return mNfcAr;
}
+ public PERM_AR_DO getPermArDo() {
+ return mPermAr;
+ }
+
@Override
/**
* Interpret value.
*
* <p>Tag: E3
*
- * <p>Value: Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A concatenation
- * of one or two AR-DO(s). If two AR-DO(s) are present these must have different types.
+ * <p>Value:
+ * 1. Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A
+ * concatenation of one or two AR-DO(s). If two AR-DO(s) are present these must have
+ * different types.
+ * 2. PERM-AR-DO
*/
public void interpret() throws ParserException {
this.mApduAr = null;
this.mNfcAr = null;
+ this.mPermAr = null;
byte[] data = getRawData();
int index = getValueIndex();
@@ -98,6 +122,9 @@
} else if (temp.getTag() == NFC_AR_DO.TAG) { // NFC-AR-DO
mNfcAr = new NFC_AR_DO(data, temp.getValueIndex(), temp.getValueLength());
mNfcAr.interpret();
+ } else if (temp.getTag() == PERM_AR_DO.TAG) { // PERM-AR-DO
+ mPermAr = new PERM_AR_DO(data, temp.getValueIndex(), temp.getValueLength());
+ mPermAr.interpret();
} else {
// un-comment following line if a more restrictive
// behavior is necessary.
@@ -106,7 +133,7 @@
index = temp.getValueIndex() + temp.getValueLength();
} while (getValueIndex() + getValueLength() > index);
- if (mApduAr == null && mNfcAr == null) {
+ if (mApduAr == null && mNfcAr == null && mPermAr == null) {
throw new ParserException("No valid DO in AR-DO!");
}
}
@@ -117,8 +144,11 @@
*
* <p>Tag: E3
*
- * <p>Value: Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A concatenation
- * of one or two AR-DO(s). If two AR-DO(s) are present these must have different types.
+ * <p>Value:
+ * 1. Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A
+ * concatenation of one or two AR-DO(s). If two AR-DO(s) are present these must have
+ * different types.
+ * 2. PERM-AR-DO
*/
public void build(ByteArrayOutputStream stream) throws DO_Exception {
@@ -134,6 +164,10 @@
mNfcAr.build(temp);
}
+ if (mPermAr != null) {
+ mPermAr.build(temp);
+ }
+
BerTlv.encodeLength(temp.size(), stream);
try {
stream.write(temp.toByteArray());
diff --git a/src/com/android/se/security/gpac/PERM_AR_DO.java b/src/com/android/se/security/gpac/PERM_AR_DO.java
new file mode 100644
index 0000000..f927042
--- /dev/null
+++ b/src/com/android/se/security/gpac/PERM_AR_DO.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+package com.android.se.security.gpac;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * PERM-AR-DO: The access rule for Device API control consists of a permission bit mask of 8 bytes.
+ */
+public class PERM_AR_DO extends BerTlv {
+
+ public static final int TAG = 0xDB;
+
+ public static final int PERM_MASK_LEN = 8;
+
+ private byte[] mPermissionMask = new byte[0];
+
+ public PERM_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
+ super(rawData, TAG, valueIndex, valueLength);
+ }
+
+ public PERM_AR_DO(byte[] permissionMask) {
+ super(permissionMask, TAG, 0, (permissionMask == null ? 0 : permissionMask.length));
+ if (permissionMask != null) mPermissionMask = permissionMask;
+ }
+
+ public byte[] getPermissionMask() {
+ return mPermissionMask;
+ }
+
+ @Override
+ /**
+ * Tag: DB Length: 8 Value: Contains a permission mask
+ */
+ public void interpret() throws ParserException {
+ mPermissionMask = new byte[0];
+
+ byte[] data = getRawData();
+ int index = getValueIndex();
+ int length = getValueLength();
+
+ if (index + getValueLength() > data.length) {
+ throw new ParserException("Not enough data for PERM-AR-DO!");
+ }
+
+ if (length == PERM_MASK_LEN) {
+ mPermissionMask = new byte[length];
+ System.arraycopy(data, index, mPermissionMask, 0, length);
+ } else {
+ throw new ParserException("Invalid length of PERM-AR-DO!");
+ }
+ }
+
+ @Override
+ /**
+ * Tag: DB Length: 8 Value: Contains a permission mask
+ */
+ public void build(ByteArrayOutputStream stream) throws DO_Exception {
+
+ // sanity checks
+ if (mPermissionMask.length != PERM_MASK_LEN) {
+ throw new DO_Exception("Invalid value length for PERM-AR-DO!");
+ }
+
+ // write tag
+ stream.write(getTag());
+ try {
+ stream.write(mPermissionMask.length);
+ stream.write(mPermissionMask);
+ } catch (IOException ioe) {
+ throw new DO_Exception("PERM could not be written!");
+ }
+ }
+}
diff --git a/src/com/android/se/security/gpac/PKG_REF_DO.java b/src/com/android/se/security/gpac/PKG_REF_DO.java
new file mode 100644
index 0000000..a35415a
--- /dev/null
+++ b/src/com/android/se/security/gpac/PKG_REF_DO.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+package com.android.se.security.gpac;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * PKG-REF-DO: The PKG-REF-DO is used for retrieving and storing the corresponding access rules
+ * for a device application (which is identified by the package name) from and to
+ * the ARA
+ */
+public class PKG_REF_DO extends BerTlv {
+
+ public static final int TAG = 0xCA;
+
+ private String mPackageName = null;
+
+ public PKG_REF_DO(byte[] rawData, int valueIndex, int valueLength) {
+ super(rawData, TAG, valueIndex, valueLength);
+ }
+
+ public PKG_REF_DO(byte[] pkg) {
+ super(pkg, TAG, 0, (pkg == null ? 0 : pkg.length));
+ if (pkg != null) {
+ mPackageName = new String(pkg);
+ }
+ }
+
+ public PKG_REF_DO() {
+ super(null, TAG, 0, 0);
+ }
+
+ /**
+ * Comapares two PKG_REF_DO objects and returns true if they are equal
+ */
+ public static boolean equals(PKG_REF_DO obj1, PKG_REF_DO obj2) {
+ if (obj1 == null) {
+ return (obj2 == null) ? true : false;
+ }
+ return obj1.equals(obj2);
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String toString() {
+ return new String("PKG_REF_DO: " + mPackageName);
+ }
+
+ /**
+ * Tags: CA Length: max length is 128 bytes
+ *
+ * <p>Value: pkg: identifies ASCII encoded package name
+ *
+ */
+ @Override
+ public void interpret() throws ParserException {
+ byte[] data = getRawData();
+ int index = getValueIndex();
+
+ // sanity checks
+ if (getValueLength() > 128) {
+ throw new ParserException("Invalid value length for PKG-REF-DO!");
+ }
+
+ if (index + getValueLength() > data.length) {
+ throw new ParserException("Not enough data for PKG-REF-DO!");
+ }
+
+ byte[] pkg = new byte[getValueLength()];
+ System.arraycopy(data, index, pkg, 0, getValueLength());
+ mPackageName = new String(pkg);
+ }
+
+ /**
+ * Tags: CA Length: max length is 128 bytes
+ *
+ * <p>Value: pkg: identifies ASCII encoded package name
+ *
+ */
+ @Override
+ public void build(ByteArrayOutputStream stream) throws DO_Exception {
+ byte[] pkg = mPackageName.getBytes();
+ // sanity checks
+ if (pkg.length > 128) {
+ throw new DO_Exception("Invalid value length for PKG-REF-DO!");
+ }
+
+ stream.write(getTag());
+
+ try {
+ stream.write(pkg.length);
+ stream.write(pkg);
+ } catch (IOException ioe) {
+ throw new DO_Exception("PKG could not be written!");
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PKG_REF_DO) {
+ PKG_REF_DO pkg_ref_do = (PKG_REF_DO) obj;
+ if (getTag() == pkg_ref_do.getTag()) {
+ if (mPackageName != null) {
+ return mPackageName.equals(pkg_ref_do.mPackageName);
+ } else {
+ return (pkg_ref_do.mPackageName == null);
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ this.build(stream);
+ } catch (DO_Exception e) {
+ return 1;
+ }
+ byte[] data = stream.toByteArray();
+ int hash = Arrays.hashCode(data);
+ return hash;
+ }
+}
diff --git a/src/com/android/se/security/gpac/REF_DO.java b/src/com/android/se/security/gpac/REF_DO.java
index 7ccf547..0400e96 100644
--- a/src/com/android/se/security/gpac/REF_DO.java
+++ b/src/com/android/se/security/gpac/REF_DO.java
@@ -47,15 +47,24 @@
private AID_REF_DO mAidDo = null;
private Hash_REF_DO mHashDo = null;
+ private PKG_REF_DO mPkgDo = null;
public REF_DO(byte[] rawData, int valueIndex, int valueLength) {
super(rawData, TAG, valueIndex, valueLength);
}
+ public REF_DO(AID_REF_DO aidRefDo, Hash_REF_DO hashRefDo, PKG_REF_DO pkgRefDo) {
+ super(null, TAG, 0, 0);
+ mAidDo = aidRefDo;
+ mHashDo = hashRefDo;
+ mPkgDo = pkgRefDo;
+ }
+
public REF_DO(AID_REF_DO aidRefDo, Hash_REF_DO hashRefDo) {
super(null, TAG, 0, 0);
mAidDo = aidRefDo;
mHashDo = hashRefDo;
+ mPkgDo = null;
}
@Override
@@ -68,6 +77,10 @@
}
if (mHashDo != null) {
b.append(mHashDo.toString());
+ b.append(' ');
+ }
+ if (mPkgDo != null) {
+ b.append(mPkgDo.toString());
}
return b.toString();
}
@@ -80,12 +93,19 @@
return mHashDo;
}
+ public PKG_REF_DO getPkgDo() {
+ return mPkgDo;
+ }
+
/**
* Interpret data.
*
* <p>Tags: E1 -> Length: n
*
- * <p>Value: AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO.
+ * <p>Value:
+ * 1. AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO.
+ * 2. Hash-REF-DO or Hash-REF-DO | PKG-REF-DO A concatenation of a Hash-REF-DO and a
+ * PKG-REF-DO.
*
* <p>Length: n bytes.
*/
@@ -94,6 +114,7 @@
mAidDo = null;
mHashDo = null;
+ mPkgDo = null;
byte[] data = getRawData();
int index = getValueIndex();
@@ -113,6 +134,9 @@
} else if (temp.getTag() == Hash_REF_DO.TAG) { // Hash-REF-DO
mHashDo = new Hash_REF_DO(data, temp.getValueIndex(), temp.getValueLength());
mHashDo.interpret();
+ } else if (temp.getTag() == PKG_REF_DO.TAG) { // PKG-REF-DO
+ mPkgDo = new PKG_REF_DO(data, temp.getValueIndex(), temp.getValueLength());
+ mPkgDo.interpret();
} else {
// uncomment following line if a more restrictive
// behaviour is necessary.
@@ -121,6 +145,10 @@
index = temp.getValueIndex() + temp.getValueLength();
} while (getValueIndex() + getValueLength() > index);
+ if (mAidDo != null && !mAidDo.isCarrierPrivilege() && mHashDo != null && mPkgDo != null) {
+ throw new ParserException("Unexpected combination of SEAC DOs and DAC DO");
+ }
+
// A rule without AID is a Carrier Privilege Rule.
// Enforces the AID to be the Carrier Privilege AID to avoid a null AID.
if (mAidDo == null && mHashDo != null) {
@@ -138,19 +166,30 @@
}
/**
- * Tag: E1 Length: n Value: AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a
- * Hash-REF-DO.
+ * Tag: E1 Length: n Value:
+ * 1. AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO.
+ * 2. Hash-REF-DO or Hash-REF-DO | PKG-REF-DO A concatenation of a Hash-REF-DO and a
+ * PKG-REF-DO.
*/
@Override
public void build(ByteArrayOutputStream stream) throws DO_Exception {
ByteArrayOutputStream temp = new ByteArrayOutputStream();
- if (mAidDo == null || mHashDo == null) {
+ if (mHashDo == null) {
throw new DO_Exception("REF-DO: Required DO missing!");
}
- mAidDo.build(temp);
- mHashDo.build(temp);
+ if (mAidDo != null) {
+ mAidDo.build(temp);
+ }
+
+ if (mHashDo != null) {
+ mHashDo.build(temp);
+ }
+
+ if (mPkgDo != null) {
+ mPkgDo.build(temp);
+ }
byte[] data = temp.toByteArray();
BerTlv tlv = new BerTlv(data, getTag(), 0, data.length);
@@ -164,7 +203,9 @@
if (getTag() == ref_do.getTag()) {
if (AID_REF_DO.equals(mAidDo, ref_do.mAidDo)) {
if (Hash_REF_DO.equals(mHashDo, ref_do.mHashDo)) {
- return true;
+ if (PKG_REF_DO.equals(mPkgDo, ref_do.mPkgDo)) {
+ return true;
+ }
}
}
}