blob: 3b205e4484454e5e17ce91e5dde2c3a3a4344198 [file] [log] [blame]
/*
* Copyright 2012 Giesecke & Devrient GmbH.
*
* 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 org.simalliance.openmobileapi.service.security.ara;
import android.util.Log;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.MissingResourceException;
import org.simalliance.openmobileapi.service.CardException;
import org.simalliance.openmobileapi.service.IChannel;
import org.simalliance.openmobileapi.service.ISmartcardServiceCallback;
import org.simalliance.openmobileapi.service.ITerminal;
import org.simalliance.openmobileapi.service.SmartcardService;
import org.simalliance.openmobileapi.service.security.AccessControlEnforcer;
import org.simalliance.openmobileapi.service.security.AccessRuleCache;
import org.simalliance.openmobileapi.service.security.ChannelAccess;
import org.simalliance.openmobileapi.service.security.gpac.dataobjects.BerTlv;
import org.simalliance.openmobileapi.service.security.gpac.dataobjects.ParserException;
import org.simalliance.openmobileapi.service.security.gpac.dataobjects.REF_AR_DO;
import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Response_ALL_AR_DO;
import org.simalliance.openmobileapi.service.security.gpac.dataobjects.Response_DO_Factory;
public class AraController {
private AccessControlEnforcer mMaster = null;
private AccessRuleCache mAccessRuleCache = null;
private ITerminal mTerminal = null;
private AccessRuleApplet mApplet = null;
private boolean mNoSuchElement = false;
private String ACCESS_CONTROL_ENFORCER_TAG = "ACE ARA";
public static final byte[] ARA_M_AID = new byte[] {
(byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x51, (byte)0x41, (byte)0x43, (byte)0x4C,
(byte)0x00
};
public AraController(AccessControlEnforcer master ) {
mMaster = master;
mAccessRuleCache = mMaster.getAccessRuleCache();
mTerminal = mMaster.getTerminal();
}
public boolean isNoSuchElement(){
return mNoSuchElement;
}
public static byte[] getAraMAid() {
return ARA_M_AID;
}
public synchronized boolean initialize(
boolean loadAtStartup,
ISmartcardServiceCallback callback)
{
IChannel channel = null;
try {
channel = this.handleOpenChannel(callback);
} catch( MissingResourceException e ){
channel = null;
}
if( channel == null ){
throw new AccessControlException("could not open channel");
}
try {
// set new applet handler since a new channel is used.
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.
if( mAccessRuleCache.isRefreshTagEqual(tag)) {
Log.d(ACCESS_CONTROL_ENFORCER_TAG, "Refresh tag has not changed. Using access rules from cache.");
return false;
}
Log.d(ACCESS_CONTROL_ENFORCER_TAG, "Refresh tag has changed.");
// set new refresh tag and empty cache.
mAccessRuleCache.setRefreshTag(tag);
mAccessRuleCache.clearCache();
if( loadAtStartup ) {
// Read content from ARA
Log.d(ACCESS_CONTROL_ENFORCER_TAG, "Read ARs from ARA");
this.readAllAccessRules();
}
} catch (Exception e) {
Log.d(ACCESS_CONTROL_ENFORCER_TAG, "ARA error: " + e.getLocalizedMessage());
throw new AccessControlException(e.getLocalizedMessage()); // Throw Exception
} finally {
if( channel != null )
closeChannel(channel);
}
return true;
}
private IChannel handleOpenChannel( ISmartcardServiceCallback callback ){
IChannel channel = null;
String reason = "";
try {
channel = openChannel(mTerminal, getAraMAid(), callback);
} catch (Exception e) {
String msg = e.toString();
msg = " ARA-M couldn't be selected: " + msg;
Log.d(ACCESS_CONTROL_ENFORCER_TAG, msg);
if (e instanceof NoSuchElementException) {
mNoSuchElement = true;
// SELECT failed
// Access Rule Applet is not available => deny any access
reason = " No Access because ARA-M is not available";
Log.d(ACCESS_CONTROL_ENFORCER_TAG, msg );
throw new AccessControlException(reason);
} else if( e instanceof MissingResourceException ){
// re-throw exception
// fixes issue 23
// this indicates that no channel is left for accessing the SE element
Log.d(ACCESS_CONTROL_ENFORCER_TAG, "no channels left to access ARA-M: " + e.getMessage() );
throw (MissingResourceException)e;
} else {
// MANAGE CHANNEL failed or general error
// In order to be compliant with any UICC/SIM card on the market
// we are going to ignore the error and says that the ARA-M is not available.
// This not fully compliant with GP spec by required for mass compatibility.
mNoSuchElement = true;
reason = msg;
Log.d(ACCESS_CONTROL_ENFORCER_TAG," ARA-M can not be accessed: " + msg);
throw new AccessControlException(reason);
}
} // End of Exception handling
return channel;
}
/**
*
* @return true if rules are read, false if not necessary or not available, but no error
* @throws AccessControlException
* @throws CardException
*/
private boolean readAllAccessRules() throws AccessControlException, CardException {
try {
byte[] data = mApplet.readAllAccessRules();
// no data returned, but no exception
// -> no rule.
if( data == null ) {
return false;
}
BerTlv tlv = Response_DO_Factory.createDO( data );
if( tlv == null ) {
throw new AccessControlException("No valid data object found" );
} if( tlv instanceof Response_ALL_AR_DO ){
ArrayList<REF_AR_DO> array = ((Response_ALL_AR_DO)tlv).getRefArDos();
if( array == null || array.size() == 0 ){
return false; // no rules
} else {
Iterator<REF_AR_DO> iter = array.iterator();
while( iter.hasNext() ){
REF_AR_DO ref_ar_do = iter.next();
this.mAccessRuleCache.putWithMerge(ref_ar_do.getRefDo(), ref_ar_do.getArDo());
}
}
} else {
throw new AccessControlException( "Applet returned invalid or wrong data object!");
}
} catch (ParserException e) {
throw new AccessControlException("Parsing Data Object Exception: " + e.getMessage());
}
return true;
}
private IChannel openChannel(ITerminal terminal, byte[] aid, ISmartcardServiceCallback callback) throws Exception
{
IChannel channel = terminal.openLogicalChannel(null, aid, callback);
// set access conditions to access ARA-M.
ChannelAccess araChannelAccess = new ChannelAccess();
araChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, ACCESS_CONTROL_ENFORCER_TAG);
araChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
channel.setChannelAccess(araChannelAccess);
return channel;
}
private void closeChannel(IChannel channel) {
try {
if (channel != null && channel.getChannelNumber() != 0) {
channel.close();
}
} catch (CardException e) {
}
}
}