blob: fe1ab11fbca650d2a5d0db1a6fb8003071fac6e5 [file] [log] [blame]
/*
* Copyright 2019-2022 NXP
*
* 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.nxp.sems;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import com.nxp.sems.ISemsCallback;
import com.nxp.sems.SemsFileOperation;
import com.nxp.sems.SemsGetLastExecStatus;
import com.nxp.sems.SemsStatus;
import com.nxp.sems.SemsTLV;
import com.nxp.sems.SemsUtil;
import com.nxp.sems.channel.ISemsApduChannel;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class SemsExecutor {
private static final byte[] clientVersion = {(byte)0x01, (byte)0x00};
public static final String TAG = "SEMS-SemsExecutor";
public static final byte SEMS_STATE_SELECT = 0;
public static final byte SEMS_STATE_STORE_DATA = 1;
public static final byte SEMS_STATE_CHECK_CERTIFICATE = 2;
public static final byte SEMS_STATE_VERIFY_SIGNATURE = 3;
public static final byte SEMS_STATE_SECURE_COMMAND_PROCESSING = 4;
public static final byte SEMS_STATE_PROCESSING_COMPLETED = 5;
public static final byte SEMS_CERTIFICATE_SIGNATURE_5F37_LEN = 64;
public static final byte SEMS_CERTIFICATE_SIGNATURE_7F49_86_LEN = 67;
public static final short MAX_FRAME_SIZE = 255;
public static final int TAG73 = 0x73;
public static final int TAG5F37 = 0x5F37;
private static ISemsApduChannel sChannel;
private final byte basicChannel = 0x00;
private String shatype;
private byte[] AID_MEM;
private ISemsCallback mSemsCallback;
private static Context sContext;
private SemsFileOperation mSemsFileOp;
private String inputScript;
private String outputScript;
private int certIndex = -1;
private byte[] rapduSelect;
private byte[] response;
private SemsTLV tlvRE42;
private SemsTLV tlvRE45;
private int linePointer;
private boolean lsCommandSeen = false;
private byte mState;
private static SemsExecutor sSemsExecutor;
public String SEMS_HASH_TYPE_SHA1 = "SHA1";
public String SEMS_HASH_TYPE_SHA256 = "SHA256";
/**
* AID of the SEMS Application Instance.
*/
private static final byte[] SEMS_APP_AID =
SemsUtil.parseHexString("A00000015153454D5300000001");
private static final byte[] SEMS_UPD_APP_AID =
SemsUtil.parseHexString("A00000015153454D53FFFFFF01");
private static final byte[] sw9000 = {(byte)0x90, (byte)0x00};
private static final byte[] sw6F00 = {(byte)0x6F, (byte)0x00};
private static final byte[] sw6985 = {(byte)0x69, (byte)0x85};
private static final byte[] sw6987 = {(byte)0x69, (byte)0x87};
private static final byte[] sw6A82 = {(byte)0x6A, (byte)0x82};
private static final byte SemsResponse = 0x01;
private static final byte SEResponse = 0x02;
private static final byte ErrorResponse = 0x03;
private static final byte SWResponse = 0x04;
private static final byte SemsCertResponse = 0x05;
private static final byte SemsAuthResponse = 0x06;
/**
* Returns SemsExecutor singleton object
* <br/>
* The Input caller application context,
* The Input APDU channel information
* @param void
*
* @return {@code SemsExecutor}.
*/
public static SemsExecutor getInstance(ISemsApduChannel semsChannel,
Context context) {
sChannel = semsChannel;
sContext = context;
if (sSemsExecutor == null) {
sSemsExecutor = new SemsExecutor();
}
return sSemsExecutor;
}
private SemsExecutor() {
this.AID_MEM = SEMS_APP_AID;
this.shatype = SEMS_HASH_TYPE_SHA1;
mSemsFileOp = new SemsFileOperation();
}
/**
* Set Hash type of the algorithm execution.
* <br/>
* @param Hash algorithm type used by SEMS
* to set
* @return {@code status} 0 in SUCCESS, otherwise -1 in failure
*/
public synchronized int setHashAlgorithm(String shaType) {
this.shatype = shaType;
return 0;
}
/**
* Get Hash type of the algorithm execution.
* <br/>
* Returns response SHAType to Application
* From SEMS
*
* @return SHAType use for Hash
* script.
*/
public synchronized String getHashAlgorithm() {
return shatype;
}
/**
* Check Hash type of the algorithm execution.
* <br/>
* Returns response True if shatype is SHA256
*
* @return boolean TRUE for SHA256 ShaHash type
*/
private synchronized boolean isSemsHashAlgoSHA256() {
return (shatype == SEMS_HASH_TYPE_SHA256);
}
/**
* Close the session with Application selected
* <br/>
* The Input Logical channel,
* Agent to provide the SEMS Application with an identifier
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
protected void closeLogicalChannel(byte channel) { sChannel.close(); }
/**
* Select SEMS Application
* <br/>
* The Input the AID to be selected
* Agent to provide the SEMS Application with an identifier
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] selectSEMSApplet() {
try {
Log.d(TAG, "Select SEMS applet: ");
return sChannel.open(AID_MEM);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Select the Application with mentioned AID
* <br/>
* The Input Logical channel,
* The Input the AID to be selected
* Agent to provide the SEMS Application with an identifier
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] selectApplication(byte channel, byte[] AID) throws Exception{
Log.d(TAG, "Select applet: ");
return sChannel.open(AID);
}
/**
* Performs store data operation of caller information.
* <br/>
* The Input SHA-1 digest of caller package name,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] sendSHA1OfCallerPackage(byte channel, byte[] callerPackage) throws Exception{
byte[] SHA1ofCallerPackage = SemsUtil.SHA1(callerPackage);
final byte[] header = {(byte)0x80, (byte)0xE2, (byte)0x00, 0x00,
(byte)0x16, (byte)0x4F, (byte)0x14};
Log.d(TAG, "Register Caller for SHA1: "+ SHA1ofCallerPackage.length);
return sChannel.transmit(SemsUtil.append(header, SHA1ofCallerPackage));
}
/**
* Performs store data operation of caller information.
* <br/>
* The Input SHA-256 digest of caller package name,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] sendSHA256OfCallerPackage(byte channel, byte[] callerPackage) throws Exception{
byte[] SHA256ofCallerPackage = SemsUtil.SHA256(callerPackage);
final byte[] header = {(byte)0x80, (byte)0xE2, (byte)0x00, 0x00,
(byte)0x22, (byte)0x4F, (byte)0x20};
Log.d(TAG, "Register Caller for SHA256: "+ SHA256ofCallerPackage.length);
return sChannel.transmit(SemsUtil.append(header, SHA256ofCallerPackage));
}
/**
* Performs store data operation of caller information.
* <br/>
* The Input SHA-1 digest of caller package name,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] sendAPCertificate(byte channel, byte[] APCert) {
try {
SemsAppletIdentifier.delayNthCommand();
if (APCert.length < MAX_FRAME_SIZE) {
// One command
byte[] header = {(byte)0x80, (byte)0xA0, (byte)0x01, (byte)0x00};
byte[] len = new byte[1];
len[0] = (byte)APCert.length;
byte[] command = SemsUtil.append(SemsUtil.append(header, len), APCert);
Log.d(TAG, "******* Processing LS Certificate 1/1 command");
byte[] rapdu = sChannel.transmit(command);
mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
return rapdu;
} else { // Two/Three commands based on tag length
byte[] rapdu;
byte[] commandHeader = {(byte) 0x80, (byte) 0xA0, (byte) 0x01, (byte) 0x00};
byte[] cmdLen = new byte[1];
int tag73Offset = 0, tag5F37Offset = 0;
int signAndPubKeyLen;
/*If TAG73 does not exists*/
if (SemsAppletIdentifier.getTag73Len() == 0) {
/* static length because of Brainpool curve*/
signAndPubKeyLen =
(SEMS_CERTIFICATE_SIGNATURE_5F37_LEN + SEMS_CERTIFICATE_SIGNATURE_7F49_86_LEN + 6);
} else {
tag73Offset = SemsTLV.getTagOffset(APCert, TAG73);
tag5F37Offset = SemsTLV.getTagOffset(APCert, TAG5F37);
/* Length of remaining frame from TAG73 onwards*/
signAndPubKeyLen = (APCert.length - tag73Offset);
}
/*First frame till either start of (TAG73 or TAG5F37)*/
byte[] commandData = Arrays.copyOfRange(APCert, 0, APCert.length - signAndPubKeyLen);
cmdLen[0] = (byte) commandData.length;
byte[] firstCommand = SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
Log.d(TAG,
"******* Processing LS Certificate 1st Frame APCert " + APCert.length
+ " signAndPubKeyLen " + signAndPubKeyLen + " tag73Offset " + tag73Offset
+ " tag5F37Offset " + tag5F37Offset);
rapdu = sChannel.transmit(firstCommand);
mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
if (SemsUtil.getSW(rapdu) != (short) 0x9000) {
return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
}
/*If the second frame is greater than 255*/
if (signAndPubKeyLen > MAX_FRAME_SIZE) {
/*2nd Frame shall be only TAG73*/
commandData = Arrays.copyOfRange(APCert, (tag73Offset), (tag5F37Offset));
cmdLen[0] = (byte) (commandData.length);
commandHeader[2] = 0x00;
byte[] secondCommand =
SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
Log.d(TAG, "******* Processing LS Certificate 2nd/3 Frame cmdLen " + commandData.length);
rapdu = sChannel.transmit(secondCommand);
mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
if (SemsUtil.getSW(rapdu) != (short) 0x9000) {
return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
}
/*3rd Frame shall be remaining from TAG5F37 onwards till end*/
commandData = Arrays.copyOfRange(APCert, tag5F37Offset, APCert.length);
cmdLen[0] = (byte) (commandData.length);
byte[] thirdCommand =
SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
Log.d(TAG, "******* Processing LS Certificate 3rd/3 Frame");
rapdu = sChannel.transmit(thirdCommand);
mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
} else {
/*2nd Frame shall be remaining (TAG73 or TAG5F37) onwards*/
commandData = Arrays.copyOfRange(APCert, APCert.length - signAndPubKeyLen, APCert.length);
cmdLen[0] = (byte) commandData.length;
commandHeader[2] = 0x00;
byte[] secondCommand =
SemsUtil.append(SemsUtil.append(commandHeader, cmdLen), commandData);
Log.d(TAG, "******* Processing LS Certificate 2nd Frame");
rapdu = sChannel.transmit(secondCommand);
mSemsFileOp.putIntoLog(rapdu, SemsCertResponse);
}
return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
}
} catch (IOException ie) {
ie.printStackTrace();
}
return null;
}
/**
* Performs store data operation of caller information.
* <br/>
* The Input SHA-1 digest of caller package name,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] sendAuthenticationFrame(byte channel, byte[] authFrame) {
byte[] authFrameHeader = {(byte)0x80, (byte)0xA0, (byte)0x00, (byte)0x00};
byte[] authFrameLen = new byte[1];
authFrameLen[0] = (byte)authFrame.length;
byte[] authFrameCommand = SemsUtil.append(
SemsUtil.append(authFrameHeader, authFrameLen), authFrame);
Log.d(TAG, "******* Processing Authentication Frame command");
try {
return sChannel.transmit(authFrameCommand);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Performs store data operation of caller information.
* <br/>
* The Input SHA-1 digest of caller package name,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] sendProcessScript(byte channel, byte[] data) {
try {
return sChannel.transmit(data);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Forward response of SEMS command to eSE
* <br/>
* The Input reponse received from SEMS applet,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code success} if the SW returned is 9000, {@code false}
* otherwise.
*/
private byte[] sendToSE(byte[] rapdu) {
try {
rapdu = sChannel.transmit(Arrays.copyOf(rapdu, rapdu.length - 2));
} catch (IOException e) {
e.printStackTrace();
}
mSemsFileOp.putIntoLog(rapdu, SEResponse);
return rapdu;
}
/**
* Performs store data operation of caller information.
* <br/>
* The Input SHA-1 digest of caller package name,
* The STORE DATA APDU command is used by the SEMS Device
* Agent to provide the SEMS Application with an identifier
* of the caller (SP Device Application), e.g.
* the digest value of the SP Device Application package name
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private byte[] sendProcessSEResponse(byte channel, byte[] rapdu) {
byte[] processSEResponseHeader = {(byte)0x80, (byte)0xA2, (byte)0x80,
(byte)0x00};
byte[] processSEResponseLen = new byte[1];
processSEResponseLen[0] = (byte)(rapdu.length);
try {
return sChannel.transmit(SemsUtil.append(
SemsUtil.append(processSEResponseHeader, processSEResponseLen),
rapdu));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Get output APDU response of script execution.
* <br/>
* Returns response APDU stored in application
* context during previous SemsExecuteScript
* @param fileName : Read response from fileName
* mentioned
*
* @return Stored APDU Responses of previous execute
* script.
*/
public String getSemsOutputResponse(String fileName) throws Exception {
SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
Log.d(TAG, "******* Read response output APDU data");
status = mSemsFileOp.setDirectories(SemsExecutor.sContext);
return mSemsFileOp.readScriptFile(fileName);
}
/**
* Performs secure SEMS script execution.
* <br/>
* The Input script path shall be provided,
* output file name shall be provided,
* IsemsCallback Interface shall be registered .
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
public SemsStatus executeScript(String scriptIn, String scriptOut,
ISemsCallback callback) {
SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
inputScript = scriptIn;
outputScript = scriptOut;
this.mSemsCallback = callback;
status = mSemsFileOp.setDirectories(SemsExecutor.sContext);
if (status == SemsStatus.SEMS_STATUS_SUCCESS) {
mSemsFileOp.writeScriptInputFile("encrypted_script.txt", scriptIn);
new SemsAsyncExecutor().start();
} else {
Log.e(TAG, "Setting SEMS script path failed, package not found");
}
return status;
}
/**
* Performs secure SEMS script execution.
* <br/>
* The Input script path shall be provided,
* output file name shall be provided,
* IsemsCallback Interface shall be registered
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private void executeScript() {
String scriptIn = inputScript;
String scriptOut = outputScript;
byte[] swReturned = sw9000;
response = sw6987;
byte channelNumber = 0;
SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
mState = SEMS_STATE_SELECT;
try {
/*To handle If input is given as path
String script = readScriptFile(scriptIn);*/
String script = scriptIn;
byte[] data = SemsUtil.parseHexString(script);
if (data == null) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
Log.e(TAG, ">>>>>>>>>> parseHexString returned NULL <<<<<<<<<<");
return;
}
List<SemsTLV> scriptTlvs = SemsTLV.parse(data);
byte[] rapdu;
if ((scriptTlvs == null) || scriptTlvs.size() == 0) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
Log.e(TAG, ">>>>>>>>>> Error : Script size 0 <<<<<<<<<<");
return;
}
Log.d(TAG, "");
Log.d(TAG, ">>>>>>>>>> Starting SEMS Execute Script <<<<<<<<<<");
Log.d(TAG, "");
try {
/*start_execute_script:*/
while (mState < SEMS_STATE_PROCESSING_COMPLETED) {
/*
* STEP 2A of executeScript - Select SEMS applet
*/
switch (mState) {
case SEMS_STATE_SELECT: {
status = SelectSems();
if (status != SemsStatus.SEMS_STATUS_SUCCESS) {
response = sw6987;
return;
}
}
// fall-through
case SEMS_STATE_STORE_DATA: {
/*
* STEP 3 of executeScript - Sending SHA1 of Caller package
*/
synchronized (SemsExecutor.this) {
if (shatype == "SHA256") {
synchronized (SemsFileOperation.class) {
rapdu = sendSHA256OfCallerPackage(
channelNumber, mSemsFileOp.mCallerPackageName.getBytes());
}
} else {
synchronized (SemsFileOperation.class) {
rapdu = sendSHA1OfCallerPackage(
channelNumber, mSemsFileOp.mCallerPackageName.getBytes());
}
}
}
if (rapdu == null) {
Log.e(TAG, "sendSHA1OfCallerPackage received incorrect rapdu");
response = rapdu;
return;
}
if (SemsUtil.getSW(rapdu) != (short) 0x9000) {
response = rapdu;
return;
}
}
// fall-through
case SEMS_STATE_CHECK_CERTIFICATE: {
/*
* STEP 4 of executeScript - Searching for Certificate in Script
* and Sending of Certificate
*/
if (ProcessCertificateFrame(scriptTlvs) !=
SemsStatus.SEMS_STATUS_SUCCESS) {
return;
}
}
// fall-through
case SEMS_STATE_VERIFY_SIGNATURE: {
/*
* STEP 5 of executeScript - Authentication frame command
*/
if (verifySignature(scriptTlvs) != SemsStatus.SEMS_STATUS_SUCCESS) {
return;
}
}
// fall-through
case SEMS_STATE_SECURE_COMMAND_PROCESSING: {
/* STEP 6 of executeScript -Secure script commands*/
status = SemsSecureCommandProcess(scriptTlvs);
break;
}
}
} /* End of restart_execute_script*/
/*
* STEP 9 of executeScript
*/
} catch (Exception e) {
e.printStackTrace();
mSemsFileOp.putIntoLog(sw6F00, ErrorResponse);
response = sw6F00;
return;
}
mSemsFileOp.putIntoLog(response, SWResponse);
return;
}catch(Exception e) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
response = sw6987;
Log.e(TAG, ">>>>>>>>>> Error : Invalid Script <<<<<<<<<< ");
e.printStackTrace();
return;
} finally {
closeLogicalChannel(channelNumber);
updateSemsStatus(response);
/*Always write log file*/
mSemsFileOp.writeScriptOutFile(scriptOut);
}
}
/**
* Performs secure SEMS script execution.
* <br/>
* The SEMS secure command packets are forwarded to SEMS applet
* based on the return SW next action is taken up.
*
* @param List<SemsTLV> parsed input TLV list
*
* @return {@code SEMS_STATE_SUCCESS} if processing is success, {@code false}
* otherwise.
*/
private SemsStatus SemsSecureCommandProcess(List<SemsTLV> scriptTlvs) throws Exception {
SemsStatus stat = SemsStatus.SEMS_STATUS_FAILED;
byte rapdu[] = sw6987;
SemsTLV secureCommand;
byte channelNumber = 0;
short sw;
lsCommandSeen = false;
mState = SEMS_STATE_SECURE_COMMAND_PROCESSING;
while (linePointer < scriptTlvs.size()) {
/*
* STEP 6 of executeScript - Searching for Secure Script Command
*/
secureCommand = scriptTlvs.get(linePointer);
linePointer++;
if (secureCommand.getTLV()[0] == (byte)0x40) {
/*
* STEP 7 of executeScript - Secure Script Command Found
*/
lsCommandSeen = true;
if (secureCommand.getLength() > 4 /* DDD: was 32 */) {
byte[] secCmd = secureCommand.getValue();
SemsAppletIdentifier.delayNthCommand();
rapdu = sendProcessScript(channelNumber, secCmd);
if (rapdu == null) {
Log.e(TAG, "sendProcessScript received incorrect rapdu");
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
sw = SemsUtil.getSW(rapdu);
if (sw == (short)0x6310) {
/*
* STEP 8 of executeScript - Process SE Response
*/
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
if ((rapdu = sendToSE(rapdu)) == null) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
mSemsFileOp.putIntoLog(rapdu, SEResponse);
{
rapdu = sendProcessSEResponse(channelNumber, rapdu);
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
while (SemsUtil.getSW(rapdu) == (short)0x6310) {
if ((rapdu = sendToSE(rapdu)) == null) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
mSemsFileOp.putIntoLog(rapdu, SEResponse);
rapdu = sendProcessSEResponse(channelNumber, rapdu);
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
}
}
if ((SemsUtil.getSW(rapdu) != (short)0x9000) &&
(SemsUtil.getSW(rapdu) != (short)0x6300)) {
mSemsFileOp.putIntoLog(rapdu, ErrorResponse);
break;
}
} else if (sw == (short)0x6320) {
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
closeLogicalChannel(channelNumber);
AID_MEM = SemsUtil.getRDATA(rapdu);
Log.d(TAG, "Received new AID need to switch");
/*Strip already processed LS commands*/
scriptTlvs.subList(0, linePointer).clear();
linePointer = 0;
Log.d(TAG, "Switch to new AID");
if (scriptTlvs.size() > 0) {
mState = SEMS_STATE_SELECT;
Log.d(TAG, "Continue buffered script");
} else {
mState = SEMS_STATE_SECURE_COMMAND_PROCESSING;
Log.d(TAG, "Finished buffered script");
SemsStatus mstatus = SelectSems();
if (mstatus == SemsStatus.SEMS_STATUS_SUCCESS) {
Log.d(TAG, "Successful selection of SEMS update");
} else if (mstatus == SemsStatus.SEMS_STATUS_FAILED) {
Log.d(TAG, "Selection failed for SEMS update");
}
rapdu = rapduSelect;
}
/*continue restart_execute_script;*/
break;
} else if ((sw != (short)0x9000) && (sw != (short)0x6300)) {
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
break;
} else {
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
}
} else {
Log.e(TAG, "Invalid length for secure command");
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
} else if (secureCommand.getTLV()[0] != (byte)0x60 &&
!(secureCommand.getTLV()[0] == (byte)0x7F &&
secureCommand.getTLV()[1] == (byte)0x21)) {
Log.e(TAG, "Invalid tag found secure script");
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
} else if (lsCommandSeen) {
closeLogicalChannel(channelNumber);
/* Consume already processed LS commands*/
scriptTlvs.subList(0, linePointer - 1).clear();
linePointer = 0;
Log.d(TAG, "");
Log.d(TAG, ">>>>>>> Executing new script...");
Log.d(TAG, "");
/* Re-select the LS Application. (FIXME: may be removed because
* there may be an issue in the LS Applet)*/
rapdu = selectApplication(channelNumber, AID_MEM);
if(rapdu == null) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
mState = SEMS_STATE_STORE_DATA;
break;
}
}
sw = SemsUtil.getSW(rapdu);
response = rapdu;
if (sw == (short)0x9000 && mState == SEMS_STATE_SECURE_COMMAND_PROCESSING) {
stat = SemsStatus.SEMS_STATUS_SUCCESS;
mState = SEMS_STATE_PROCESSING_COMPLETED;
Log.d(TAG, "Reached end of script processing");
} else if (mState != SEMS_STATE_SECURE_COMMAND_PROCESSING) {
Log.d(TAG, "Script processing shall continue");
} else {
mState = SEMS_STATE_PROCESSING_COMPLETED;
Log.d(TAG, "Script execution failed");
}
return stat;
}
/**
* Script certificate verification is handled
* <br/>
* The SEMS Device Agent looks for the first CERT.SP.AUT
* certificate of which tags '42' and '45' match the
* buffered tags '42' and '45' (see Figure B-2) returned
* by the SELECT command.
* @param List<SemsTLV> parsed input TLV list
*
* @return {@code SEMS_STATE_SUCCESS} if processing is success, {@code false}
* otherwise.
*/
private SemsStatus ProcessCertificateFrame(List<SemsTLV> scriptTlvs) {
List<SemsTLV> tlvs;
byte channelNumber = 0;
SemsTLV tlvSC42;
SemsTLV tlvSC45;
SemsTLV tlvCertInScript = null;
byte[] APCert = null;
SemsStatus stat = SemsStatus.SEMS_STATUS_FAILED;
byte rapdu[];
Log.d(TAG, "Check Mandatory fields in Certificate ");
for (int i = 0; i < scriptTlvs.size(); i++) {
if (scriptTlvs.get(i).getTag() == 0x7F21) {
certIndex = i;
tlvCertInScript = scriptTlvs.get(i);
try {
SemsAppletIdentifier.validateTag73Support(tlvCertInScript);
} catch (Exception e) {
Log.e(TAG, "Exception in parsing TAG73 in CERT frame");
}
byte[] cert = tlvCertInScript.getTLV();
tlvs = SemsTLV.parse(tlvCertInScript.getValue());
tlvSC42 = SemsTLV.find(tlvs, 0x42);
tlvSC45 = SemsTLV.find(tlvs, 0x45);
/* Check that all mandatory tags are present. If not so, return
6987.*/
boolean isCertOK = true;
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x93) != null;
}
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x42) != null;
}
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x5F20) != null;
}
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x95) != null;
}
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x45) != null;
}
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x53) != null;
}
if (isCertOK) {
isCertOK = SemsTLV.find(tlvs, 0x5F37) != null;
}
if (isCertOK) {
SemsTLV tlvSC7F49 = SemsTLV.find(tlvs, 0x7F49);
isCertOK = tlvSC7F49 != null && tlvSC7F49.isConstructed() &&
SemsTLV.find(tlvSC7F49.getNodes(), 0x86) != null;
}
if (!isCertOK) {
break;
}
if (tlvSC42 == null || tlvSC45 == null) {
continue;
}
if (Arrays.equals(tlvRE42.getValue(), tlvSC42.getValue()) &&
Arrays.equals(tlvRE45.getValue(), tlvSC45.getValue())) {
/* Correct certificate found*/
APCert = cert;
stat = SemsStatus.SEMS_STATUS_SUCCESS;
break;
}
} else if (scriptTlvs.get(i).getTag() == 0x60) {
;
} else if (scriptTlvs.get(i).getTag() == 0x40) {
Log.e(TAG, "Unexpected TAG 40 command found");
break;
} else {
break;
}
}
if (stat == SemsStatus.SEMS_STATUS_SUCCESS && APCert != null) {
rapdu = sendAPCertificate(channelNumber, APCert);
if (rapdu == null) {
Log.e(TAG, "sendAPCertificate received incorrect rapdu");
response = rapdu;
return SemsStatus.SEMS_STATUS_FAILED;
}
if (SemsUtil.getSW(rapdu) != (short)0x9000) {
Log.e(TAG, "certificate frame command failed");
mSemsFileOp.putIntoLog(rapdu, ErrorResponse);
response = rapdu;
stat = SemsStatus.SEMS_STATUS_FAILED;
}
} else {
Log.e(TAG, "No valid certificate frame found");
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
response = sw6987;
}
Log.d(TAG, "Exit Certificate frame validation");
return stat;
}
/**
* Select SEMS application/SEMS updater on eSE
* <br/>
* The SEMS Device Agent selects either the SEMS
* Application or the SEMS Updater. The SEMS Updater
* is selected if the SEMS Application is not present
* @param List<SemsTLV> parsed input TLV list
*
* @return {@code SEMS_STATE_SUCCESS} if processing is success, {@code false}
* otherwise.
*/
private SemsStatus SelectSems() throws Exception {
byte channelNumber = 0;
SemsTLV tlvRootEntityKeyID;
List<SemsTLV> tlvs;
SemsStatus stat = SemsStatus.SEMS_STATUS_FAILED;
Log.d(TAG, "Select SEMS Application");
rapduSelect = selectApplication(channelNumber, AID_MEM);
if (rapduSelect == null) {
Log.e(TAG, "SEMS-select failed");
return stat;
}
if (SemsUtil.getSW(rapduSelect) != (short)0x9000) {
if ((SemsUtil.getSW(rapduSelect) == (short)0x6999) ||
(SemsUtil.getSW(rapduSelect) == (short)0x6A82)) {
rapduSelect = selectApplication(channelNumber, SEMS_APP_AID);
if (rapduSelect == null) {
Log.e(TAG, "SEMS-select failed");
return stat;
}
if ((SemsUtil.getSW(rapduSelect) == (short)0x6999) ||
(SemsUtil.getSW(rapduSelect) == (short)0x6A82)) {
rapduSelect = selectApplication(channelNumber, SEMS_UPD_APP_AID);
if (rapduSelect == null) {
Log.e(TAG, "SEMS-select failed");
return stat;
}
if (SemsUtil.getSW(rapduSelect) == (short)0x9000) {
AID_MEM = SEMS_UPD_APP_AID;
stat = SemsStatus.SEMS_STATUS_SUCCESS;
} else {
Log.e(TAG, "SEMS/SEMS-updater not found");
return stat;
}
} else {
if (SemsUtil.getSW(rapduSelect) == (short)0x9000) {
AID_MEM = SEMS_APP_AID;
stat = SemsStatus.SEMS_STATUS_SUCCESS;
} else {
Log.e(TAG, "SEMS/SEMS-updater not found");
return stat;
}
}
} else {
Log.e(TAG, "SEMS/SEMS-updater select failed");
return stat;
}
} else {
stat = SemsStatus.SEMS_STATUS_SUCCESS;
}
if (stat == SemsStatus.SEMS_STATUS_SUCCESS) {
/*
* STEP 2B of executeScript - retrieve TAG42 and TAG45
*/
tlvs = SemsTLV.parse(SemsTLV.parse(rapduSelect).get(0).getValue());
tlvRootEntityKeyID = SemsTLV.find(tlvs, 0x65);
if (tlvRootEntityKeyID == null) {
return SemsStatus.SEMS_STATUS_FAILED;
}
tlvs = SemsTLV.parse(tlvRootEntityKeyID.getValue());
tlvRE42 = SemsTLV.find(tlvs, 0x42);
tlvRE45 = SemsTLV.find(tlvs, 0x45);
}
Log.d(TAG, "Exit Select SEMS Application");
return stat;
}
/**
* SEMS Script Authentication frame
* <br/>
* SEMS Device Agent sends an Authentication
* Frame in a PROCESS SCRIPT COMMAND APDU command
* @param List<SemsTLV> parsed input TLV list
*
* @return {@code SEMS_STATE_SUCCESS} if processing is success, {@code false}
* otherwise.
*/
private SemsStatus verifySignature(List<SemsTLV> scriptTlvs) {
byte channelNumber = 0;
byte[] rapdu;
SemsTLV authFrame;
SemsStatus stat = SemsStatus.SEMS_STATUS_FAILED;
linePointer = certIndex + 1;
authFrame = scriptTlvs.get(linePointer);
linePointer++;
if (authFrame.getTag() != 0x60) {
Log.e(TAG, "Authentication frame not found");
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
response = sw6987;
return stat;
}
authFrame = SemsTLV.parse(authFrame.getValue()).get(0);
SemsAppletIdentifier.delayNthCommand();
rapdu = sendAuthenticationFrame(channelNumber, authFrame.getValue());
if (rapdu == null) {
Log.e(TAG, "sendAuthenticationFrame received incorrect rapdu");
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(sw6987);
return stat;
}
mSemsFileOp.putIntoLog(rapdu, SemsAuthResponse);
if (SemsUtil.getSW(rapdu) == (short)0x6310) { // begin_perso cleanup
int i = 0;
if ((rapdu = sendToSE(rapdu)) == null) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
response = sw6987;
return stat;
}
mSemsFileOp.putIntoLog(rapdu, SEResponse);
rapdu = sendProcessSEResponse(channelNumber, rapdu);
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
while (SemsUtil.getSW(rapdu) == (short)0x6310) {
if ((rapdu = sendToSE(rapdu)) == null) {
mSemsFileOp.putIntoLog(sw6987, ErrorResponse);
response = sw6987;
return stat;
}
mSemsFileOp.putIntoLog(rapdu, SEResponse);
rapdu = sendProcessSEResponse(channelNumber, rapdu);
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
i++;
}
} else if (SemsUtil.getSW(rapdu) != (short)0x9000) {
Log.e(TAG, "Processing Authentication frame failed");
mSemsFileOp.putIntoLog(rapdu, SemsResponse);
} else if (SemsUtil.getSW(rapdu) == (short)0x9000) {
stat = SemsStatus.SEMS_STATUS_SUCCESS;
}
response = rapdu;
return stat;
}
class SemsAsyncExecutor extends Thread {
public void run() { executeScript(); }
}
/**
* Update the SEMS execution status to application
* <br/>
* Inform the registered SP application about the
* SEMS execution status
* @param List<SemsTLV> parsed input TLV list
*
* @return {@code SEMS_STATE_SUCCESS} if processing is success, {@code false}
* otherwise.
*/
void updateSemsStatus(byte[] status) {
int respLen = 0;
int updateStatus = 0x0F;
if (status != null) {
respLen = status.length;
if (respLen >= 2) {
if (status[respLen - 2] == (byte)0x90 && status[respLen - 1] == 0) {
updateStatus = 0;
Log.d(TAG, "Exit SEMS script execution success");
} else if (status[respLen - 2] == (byte)0x69 &&
status[respLen - 1] == (byte)0x87) {
updateStatus = 1;
Log.d(
TAG,
"Exit SEMS script execution failed due to script processing error");
} else if (status[respLen - 2] == (byte)0x6F &&
status[respLen - 1] == (byte)0x00) {
updateStatus = 2;
Log.d(TAG, "Exit SEMS script execution failed due to IO exception");
} else {
updateStatus = 3;
Log.d(TAG, "Exit SEMS script execution failed due to unknown");
}
}
}
if (this.mSemsCallback != null) {
this.mSemsCallback.onSemsComplete(updateStatus);
this.mSemsCallback.onSemsComplete(updateStatus, mSemsFileOp.getRespOutLog());
}
}
/**
* Retrieve the last SEMS execution status by sending GET DATA command
* to SEMS applet
* <br/>
* @return {@code SemsGetLastExecStatus object}
* outScriptSignature : SEMS lib will provide the Authentication frame
* signature of the last executed script. Application can use this
* info to match with local SEMS script, useful in multiple application
* context.
* status:
* 0x00 - Success, The input script has been completely executed
* 0x01 - Failed, The input script execution was interrupted
* because of teardown
*/
SemsGetLastExecStatus getLastSemsExecuteStatus() throws Exception {
final byte GET_SEMS_STATUS = 0x46;
final byte GET_AUTH_SIGNATURE = 0x47;
byte channelNumber = 0;
List<SemsTLV> tlvs;
SemsTLV tlvSC46 , tlvSC47;
byte[] rapdu;
SemsGetLastExecStatus lastSemsExec = new SemsGetLastExecStatus();
lastSemsExec.status = SemsAgent.SEMS_STATUS_FAILED;
lastSemsExec.outScriptSignature = null;
/* Frame packet to get status and authentication */
byte[] getDataFrame = {(byte)0x80, (byte)0xCA, (byte)0x00, (byte)0x00, (byte)0x00};
try {
if (SelectSems() != SemsStatus.SEMS_STATUS_SUCCESS) {
return lastSemsExec;
}
/******** Processing Authentication command ***********/
getDataFrame[3] = GET_AUTH_SIGNATURE;
rapdu = sChannel.transmit(getDataFrame);
if((rapdu.length != 0) && SemsUtil.getSW(rapdu) == (short)0x9000) {
tlvs = SemsTLV.parse(rapdu);
if(tlvs.size() != 0) {
tlvSC47 = SemsTLV.find(tlvs, GET_AUTH_SIGNATURE);
if(tlvSC47 != null)
lastSemsExec.outScriptSignature = Arrays.toString(tlvSC47.getValue());
}
}
/******** Processing Status command ***********/
getDataFrame[3] = GET_SEMS_STATUS;
rapdu = sChannel.transmit(getDataFrame);
if((rapdu.length != 0) && SemsUtil.getSW(rapdu) == (short)0x9000) {
tlvs = SemsTLV.parse(rapdu);
if(tlvs.size() != 0) {
int statusByte = 0;
tlvSC46 = SemsTLV.find(tlvs, GET_SEMS_STATUS);
if((tlvSC46 != null) && (tlvSC46.getValue()[statusByte] == SemsAgent.SEMS_STATUS_SUCCESS)) {
lastSemsExec.status = SemsAgent.SEMS_STATUS_SUCCESS;
}
}
}
Log.d(TAG, "******* Sems authentication signature : " + lastSemsExec.outScriptSignature);
Log.d(TAG, "******* Sems status : " + lastSemsExec.status);
/*Close the logical chanel*/
closeLogicalChannel(channelNumber);
} catch (IOException e) {
closeLogicalChannel(channelNumber);
e.printStackTrace();
}
return lastSemsExec;
}
}