| /* |
| * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
| * |
| * Not a Contribution. |
| */ |
| |
| /* |
| * 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 org.simalliance.openmobileapi.service.security.arf.PKCS15; |
| |
| import java.security.AccessControlException; |
| import java.util.MissingResourceException; |
| |
| import org.simalliance.openmobileapi.service.IChannel; |
| import org.simalliance.openmobileapi.service.security.arf.SecureElement; |
| import org.simalliance.openmobileapi.service.security.arf.SecureElementException; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFACMain; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFACRules; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFDIR; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFDODF; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFODF; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFCDF; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.EFTCF; |
| import org.simalliance.openmobileapi.service.security.arf.PKCS15.PKCS15Exception; |
| import java.security.cert.CertificateException; |
| import java.security.cert.X509Certificate; |
| import android.os.Build; |
| import android.os.SystemProperties; |
| import android.util.Log; |
| import java.util.ArrayList; |
| /** |
| * Handles PKCS#15 topology |
| ***************************************************/ |
| public class PKCS15Handler { |
| |
| public static final String TAG = "SmartcardService ACE ARF"; |
| |
| // 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 |
| }; |
| |
| // Handle to "Secure Element" |
| private SecureElement mSEHandle; |
| // "Secure Element" label |
| private String mSELabel=null; |
| |
| // Handle to "Logical Channel" allocated by the SE |
| private IChannel 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; |
| EFTCF TCFObject = null; |
| // SIM Allowed modes: |
| private boolean mSimIoAllowed; |
| private boolean mSimAllianceAllowed; |
| private boolean mX509found = false; |
| public ArrayList<X509Certificate> CertifList = null; |
| /** |
| * Updates "Access Control Rules" |
| */ |
| private boolean updateACRules() |
| throws Exception, PKCS15Exception, SecureElementException |
| { |
| byte[] ACRulesPath=null; |
| try { |
| ACRulesPath=mACMainObject.analyseFile(); |
| } catch (Exception e) { |
| mACMainObject=null; |
| mSEHandle.resetAccessRules(); |
| throw e; |
| } |
| // Check if rules must be updated |
| if (ACRulesPath != null) { |
| Log.d(TAG, "Access Rules needs to be updated..."); |
| if (mACRulesObject==null) { |
| mACRulesObject=new EFACRules(mSEHandle); |
| } |
| mSEHandle.clearAccessRuleCache(); |
| try { |
| mACRulesObject.analyseFile(ACRulesPath); |
| } catch (Exception e) { |
| Log.v(TAG, "Exception: clear access rule cache and refresh tag"); |
| mSEHandle.resetAccessRules(); |
| throw e; |
| } |
| return true; |
| } else { |
| Log.d(TAG, "Refresh Tag has not been changed..."); |
| return false; |
| } |
| } |
| |
| public ArrayList<X509Certificate> getCertif () |
| throws PKCS15Exception, SecureElementException, CertificateException { |
| if (mX509found) { |
| Log.v(TAG, "returning CertifList"); |
| return CertifList; |
| } else { |
| throw new PKCS15Exception("Cannot get x509 certificates via EF Dir"); |
| } |
| } |
| /** |
| * Initializes "Access Control" entry point [ACMain] |
| */ |
| private void initACEntryPoint() |
| throws PKCS15Exception, SecureElementException, CertificateException |
| { |
| |
| byte[] DODFPath=null; |
| |
| readAllowedSimMode(); |
| |
| for(int ind=0;ind<CONTAINER_AIDS.length;ind++) { |
| if (selectACRulesContainer(CONTAINER_AIDS[ind])) { |
| |
| 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; |
| |
| EFCDF CDFObject=new EFCDF(mSEHandle); |
| if (CDFObject.checkCDF()) { |
| TCFObject=new EFTCF(mSEHandle); |
| CertifList = TCFObject.analyseFile(); |
| mX509found = true; |
| } |
| |
| } 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; |
| } |
| } |
| } |
| |
| /** |
| * Selects "Access Control Rules" container |
| * @param AID Identification of the GPAC Applet/PKCS#15 ADF; |
| * <code>null</code> for EF_DIR file |
| * @return <code>true</code> when container is active; |
| * <code>false</code> otherwise |
| */ |
| private boolean selectACRulesContainer(byte[] aid) |
| throws PKCS15Exception,SecureElementException |
| { |
| boolean isActiveContainer=true; |
| |
| if (aid==null) { |
| mArfChannel = null; |
| |
| // some devices use logical channels to access filesystem directly. This is done with an empty byte array. |
| // if open logical channel does not work, last fallback is using SIM_IO (AT-CRSM). |
| // 2012-11-08 |
| if(mSimAllianceAllowed) |
| mArfChannel = mSEHandle.openLogicalArfChannel(new byte[]{}); |
| |
| if (mArfChannel != null) { |
| Log.i(TAG, "Logical channels are used to access to PKC15"); |
| mSEHandle.setSeInterface(SecureElement.SIM_ALLIANCE); |
| } |
| else { |
| if(mSimIoAllowed) { |
| // Since ARF gets only active if the terminal belongs to a SIM/UICC |
| // we have to switch to SIM_IO |
| Log.i(TAG, "Fall back into ARF with SIM_IO"); |
| mSEHandle.setSeInterface(SecureElement.SIM_IO); |
| } |
| else { |
| Log.i(TAG, "SIM IO is not allowed: cannot access to ARF"); |
| isActiveContainer = false; |
| } |
| } |
| |
| if(isActiveContainer && mPkcs15Path == null ) { // estimate PKCS15 path only if it is not known already. |
| mACMainPath = null; |
| // EF_DIR parsing |
| EFDIR DIRObject=new EFDIR(mSEHandle); |
| mPkcs15Path=DIRObject.lookupAID(PKCS15_AID); |
| if( mPkcs15Path == null ) { |
| Log.i(TAG, "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 PKCS15Exception("Cannot select PKCS#15 directory via EF Dir"); |
| } |
| } |
| } |
| // if an AID is given use logical channel. |
| else { |
| if(!mSimAllianceAllowed) { |
| isActiveContainer = false; |
| } |
| else { |
| // Selection of Applet/ADF via AID is done via SCAPI and logical Channels |
| mSEHandle.setSeInterface(SecureElement.SIM_ALLIANCE); |
| if ((mArfChannel=mSEHandle.openLogicalArfChannel(aid))==null) { |
| isActiveContainer=false; |
| Log.w(TAG,"GPAC/PKCS#15 ADF not found!!"); |
| } |
| else { |
| // ARF is selected via AID. |
| if( mPkcs15Path != null ){ // if there is a change from path selection to AID selection, then reset AC Main path. |
| mACMainPath = null; |
| } |
| mPkcs15Path = null; // selection is done via AID |
| } |
| } |
| } |
| return isActiveContainer; |
| } |
| |
| /** |
| * Constructor |
| * @param handle Handle to "Secure Element" |
| */ |
| public PKCS15Handler(SecureElement handle) { |
| mSEHandle=handle; |
| } |
| |
| /** |
| * 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) { |
| mSELabel=secureElement; |
| Log.v(TAG,"- Loading "+mSELabel+" rules..."); |
| try { |
| initACEntryPoint(); |
| return updateACRules(); |
| } catch (Exception e) { |
| if( e instanceof MissingResourceException ){ |
| // this indicates that no channel is left for accessing the SE element |
| throw (MissingResourceException)e; |
| } |
| Log.e(TAG,mSELabel+" rules not correctly initialized! " + e.getLocalizedMessage()); |
| throw new AccessControlException(e.getLocalizedMessage()); |
| } finally { |
| // Close previously opened channel |
| if (mArfChannel!=null) |
| mSEHandle.closeArfChannel(); |
| } |
| } |
| |
| /** |
| * Read security allowed sim mode |
| */ |
| private void readAllowedSimMode() { |
| if(!Build.IS_DEBUGGABLE) { |
| mSimIoAllowed = true; |
| mSimAllianceAllowed = true; |
| } else { |
| String level = SystemProperties.get("service.seek.arf", "simio simalliance"); |
| level = SystemProperties.get("persist.service.seek.arf", level); |
| |
| if(level.contains("simio")) mSimIoAllowed = true; else mSimIoAllowed = false; |
| if(level.contains("simalliance")) mSimAllianceAllowed = true; else mSimAllianceAllowed = false; |
| } |
| |
| Log.i(TAG, "Allowed SIM mode: SimIo=" + mSimIoAllowed + " SimAlliance=" + mSimAllianceAllowed ); |
| } |
| } |