/*
* Copyright (C) 2017 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.
*/
/*
* Copyright (c) 2014-2017, The Linux Foundation.
*/
/*
* Copyright (C) 2011 Deutsche Telekom, A.G.
*
* 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.security.arf.pkcs15;
import android.util.Log;
import com.android.se.Channel;
import com.android.se.security.arf.SecureElement;
import com.android.se.security.arf.SecureElementException;
import java.io.IOException;
import java.security.AccessControlException;
import java.security.cert.CertificateException;
import java.util.MissingResourceException;
import java.util.NoSuchElementException;
/** Handles PKCS#15 topology */
public class PKCS15Handler {
// AID of the GPAC Applet/ADF
public static final byte[] GPAC_ARF_AID = {
(byte) 0xA0, 0x00, 0x00, 0x00, 0x18, 0x47, 0x50, 0x41, 0x43, 0x2D, 0x31, 0x35
};
// AID of the PKCS#15 ADF
public static final byte[] PKCS15_AID = {
(byte) 0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35
};
// AIDs of "Access Control Rules" containers
public static final byte[][] CONTAINER_AIDS = {PKCS15_AID, GPAC_ARF_AID, null};
public final String mTag = "SecureElement-PKCS15Handler";
// Handle to "Secure Element"
private SecureElement mSEHandle;
// "Secure Element" label
private String mSELabel = null;
// Handle to "Logical Channel" allocated by the SE
private Channel mArfChannel = null;
// "EF Access Control Main" object
private EFACMain mACMainObject = null;
// EF AC Rules object
private EFACRules mACRulesObject = null;
private byte[] mPkcs15Path = null;
private byte[] mACMainPath = null;
private boolean mACMFfound = true;
/**
* Constructor
*
* @param handle Handle to "Secure Element"
*/
public PKCS15Handler(SecureElement handle) {
mSEHandle = handle;
}
/** Updates "Access Control Rules" */
private boolean updateACRules() throws CertificateException, IOException,
MissingResourceException, NoSuchElementException, PKCS15Exception,
SecureElementException {
byte[] ACRulesPath = null;
if (!mACMFfound) {
mSEHandle.resetAccessRules();
mACMainPath = null;
if (mArfChannel != null) mSEHandle.closeArfChannel();
this.initACEntryPoint();
}
try {
ACRulesPath = mACMainObject.analyseFile();
mACMFfound = true;
} catch (IOException e) {
// IOException must be propagated to the access control enforcer.
throw e;
} catch (Exception e) {
Log.i(mTag, "ACMF Not found !");
mACMainObject = null;
mSEHandle.resetAccessRules();
mACMFfound = false;
throw e;
}
// Check if rules must be updated
if (ACRulesPath != null) {
Log.i(mTag, "Access Rules needs to be updated...");
if (mACRulesObject == null) {
mACRulesObject = new EFACRules(mSEHandle);
}
mSEHandle.clearAccessRuleCache();
mACMainPath = null;
if (mArfChannel != null) mSEHandle.closeArfChannel();
this.initACEntryPoint();
try {
mACRulesObject.analyseFile(ACRulesPath);
} catch (IOException e) {
// IOException must be propagated to the access control enforcer.
throw e;
} catch (Exception e) {
Log.i(mTag, "Exception: clear access rule cache and refresh tag");
mSEHandle.resetAccessRules();
throw e;
}
return true;
} else {
Log.i(mTag, "Refresh Tag has not been changed...");
return false;
}
}
/** Initializes "Access Control" entry point [ACMain] */
private void initACEntryPoint() throws CertificateException, IOException,
MissingResourceException, NoSuchElementException, PKCS15Exception,
SecureElementException {
byte[] DODFPath = null;
boolean absent = true;
for (int ind = 0; ind < CONTAINER_AIDS.length; ind++) {
try {
boolean result = selectACRulesContainer(CONTAINER_AIDS[ind]);
// NoSuchElementException was not thrown by the terminal.
// The terminal confirmed that the specified applet or PKCS#15 ADF exists
// or could not determine that it does not exists on the secure element.
absent = false;
if (!result) {
continue;
}
byte[] acMainPath = null;
if (mACMainPath == null) {
EFODF ODFObject = new EFODF(mSEHandle);
DODFPath = ODFObject.analyseFile(mPkcs15Path);
EFDODF DODFObject = new EFDODF(mSEHandle);
acMainPath = DODFObject.analyseFile(DODFPath);
mACMainPath = acMainPath;
} else {
if (mPkcs15Path != null) {
acMainPath = new byte[mPkcs15Path.length + mACMainPath.length];
System.arraycopy(mPkcs15Path, 0, acMainPath, 0, mPkcs15Path.length);
System.arraycopy(mACMainPath, 0, acMainPath, mPkcs15Path.length,
mACMainPath.length);
} else {
acMainPath = mACMainPath;
}
}
mACMainObject = new EFACMain(mSEHandle, acMainPath);
break;
} catch (NoSuchElementException e) {
// The specified applet or PKCS#15 ADF does not exist.
// Let us check the next candidate.
}
}
if (absent) {
// All the candidate applet and/or PKCS#15 ADF cannot be found on the secure element.
throw new NoSuchElementException("No ARF exists");
}
}
/**
* Selects "Access Control Rules" container
*
* @param AID Identification of the GPAC Applet/PKCS#15 ADF; null
for EF_DIR file
* @return true
when container is active; false
otherwise
*/
private boolean selectACRulesContainer(byte[] aid) throws IOException, MissingResourceException,
NoSuchElementException, PKCS15Exception, SecureElementException {
if (aid == null) {
mArfChannel = mSEHandle.openLogicalArfChannel(new byte[]{});
if (mArfChannel != null) {
Log.i(mTag, "Logical channels are used to access to PKC15");
} else {
return false;
}
// estimate PKCS15 path only if it is not known already.
if (mPkcs15Path == null) {
mACMainPath = null;
// EF_DIR parsing
EFDIR DIRObject = new EFDIR(mSEHandle);
mPkcs15Path = DIRObject.lookupAID(PKCS15_AID);
if (mPkcs15Path == null) {
Log.i(mTag, "Cannot use ARF: cannot select PKCS#15 directory via EF Dir");
// TODO: Here it might be possible to set a default path
// so that SIMs without EF-Dir could be supported.
throw new NoSuchElementException("Cannot select PKCS#15 directory via EF Dir");
}
}
} else {
// if an AID is given use logical channel.
// Selection of Applet/ADF via AID is done via SCAPI and logical Channels
mArfChannel = mSEHandle.openLogicalArfChannel(aid);
if (mArfChannel == null) {
Log.w(mTag, "GPAC/PKCS#15 ADF not found!!");
return false;
}
// ARF is selected via AID.
// if there is a change from path selection to AID
// selection, then reset AC Main path.
if (mPkcs15Path != null) {
mACMainPath = null;
}
mPkcs15Path = null; // selection is done via AID
}
return true;
}
/**
* Loads "Access Control Rules" from container
*
* @return false if access rules where not read due to constant refresh tag.
*/
public synchronized boolean loadAccessControlRules(String secureElement) throws IOException,
MissingResourceException, NoSuchElementException {
mSELabel = secureElement;
Log.i(mTag, "- Loading " + mSELabel + " rules...");
try {
initACEntryPoint();
return updateACRules();
} catch (IOException | MissingResourceException | NoSuchElementException e) {
throw e;
} catch (Exception e) {
Log.e(mTag, mSELabel + " rules not correctly initialized! " + e.getLocalizedMessage());
throw new AccessControlException(e.getLocalizedMessage());
} finally {
// Close previously opened channel
if (mArfChannel != null) mSEHandle.closeArfChannel();
}
}
}