blob: 5cc7ec989f2b167bc3422a76d15bada2d99dbef3 [file] [log] [blame]
/*
* Copyright 2019-2021 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 java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import com.nxp.sems.SemsTLV;
import com.nxp.sems.SemsUtil;
import com.nxp.sems.channel.ISemsApduChannel;
import com.nxp.sems.ISemsCallback;
import com.nxp.sems.SemsStatus;
import com.nxp.sems.SemsGetLastExecStatus;
import android.content.Context;
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;
private static ISemsApduChannel sChannel;
private final byte basicChannel = 0x00;
private String callerPackageName;
private static String sRespOutlog;
private byte[] AID_MEM;
private String encryptedScriptDirectory = "";
private String outDirectory = "";
private String inputScript;
private String outputScript;
private ISemsCallback mSemsCallback;
private static Context mContext;
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;
/**
* 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;
mContext = context;
sRespOutlog = "";
if (sSemsExecutor == null) {
sSemsExecutor = new SemsExecutor();
}
return sSemsExecutor;
}
private SemsExecutor() {
this.AID_MEM = SEMS_APP_AID;
}
/**
* Logging the response APDU received during SEMS execution
* <br/>
* The Input type of response,
* The Input status bytes
* Agent to provide the SEMS Application with an identifier
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private void putIntoLog(byte[] what, byte type) {
byte[] data;
/*Skip responses ending with SW '6310'*/
if ((what[what.length - 2] == 0x63) && (what[what.length - 1] == 0x10)) {
return;
}
switch (type) {
case ErrorResponse:
return;
default:
return;
case SemsCertResponse:
data = new byte[] {0x7F, 0x21};
break;
case SemsAuthResponse:
data = new byte[] {0x60};
break;
case SemsResponse:
data = new byte[] {0x40};
break;
}
data = SemsTLV.make(0x61, SemsUtil.append(SemsTLV.make(0x43, data),
SemsTLV.make(0x44, what)));
sRespOutlog = sRespOutlog + SemsUtil.toHexString(data) + "\r\n";
}
/**
* Set the current application directory & caller information
* <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 SemsStatus setDirectories() {
SemsStatus status = SemsStatus.SEMS_STATUS_FAILED;
PackageInfo pInfo;
PackageManager pm = mContext.getPackageManager();
String str = mContext.getPackageName();
try {
pInfo = pm.getPackageInfo(str, 0);
str = pInfo.applicationInfo.dataDir;
this.callerPackageName = pInfo.packageName;
this.encryptedScriptDirectory = str;
this.outDirectory = str;
status = SemsStatus.SEMS_STATUS_SUCCESS;
} catch (Exception e) {
e.printStackTrace();
}
return status;
}
private static Path getPath(String dir, String file) {
return (dir != null) ? FileSystems.getDefault().getPath(dir, file)
: FileSystems.getDefault().getPath(file);
}
/**
* Write the content of String buffer to out file
* <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[] writeScriptOutFile(String scriptOut) {
Path p = getPath(outDirectory, scriptOut);
try {
Files.write(p, sRespOutlog.getBytes());
} catch (IOException e) {
Log.e(TAG, "IOException during writeScriptOutfile: ");
}
return sRespOutlog.getBytes();
}
/**
* Write the content of String buffer to backup file
* <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[] writeScriptInputFile(String filename, String scriptBuffer) {
Path p = getPath(outDirectory, filename);
try {
Files.write(p, scriptBuffer.getBytes());
} catch (IOException e) {
Log.e(TAG, "IOException during writeScriptInputfile: ");
}
return scriptBuffer.getBytes();
}
/**
* Read the file content to String format
* <br/>
* The Input path of the SEMS encrypted script stored,
* Agent to provide the SEMS Application with an identifier
* @param void
*
* @return {@code true} if the SW returned is 9000, {@code false} otherwise.
*/
private String readScriptFile(String scriptIn) throws Exception {
Path p = getPath(encryptedScriptDirectory, scriptIn);
String script = "";
Iterator<String> i;
try {
List<String> lines = Files.readAllLines(p, Charset.defaultCharset());
i = lines.iterator();
while (i.hasNext()) {
String s = i.next();
if (!s.startsWith("%%%")) {
script += s;
}
}
} catch (IOException e) {
Log.e(TAG, "IOException during reading script: ");
throw new Exception();
}
return script;
}
/**
* 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: ");
return sChannel.transmit(SemsUtil.append(header, SHA1ofCallerPackage));
}
/**
* 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 {
if (APCert.length < 255) {
// 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);
putIntoLog(rapdu, SemsCertResponse);
return rapdu;
} else {
// Two commands
byte[] rapdu;
/* static length because of Brainpool curve*/
int signAndPubKeyLen = SEMS_CERTIFICATE_SIGNATURE_5F37_LEN +
SEMS_CERTIFICATE_SIGNATURE_7F49_86_LEN + 6;
byte[] firstCommandData =
Arrays.copyOfRange(APCert, 0, APCert.length - signAndPubKeyLen);
byte[] secondCommandData = Arrays.copyOfRange(
APCert, APCert.length - signAndPubKeyLen, APCert.length);
byte[] firstHeader = {(byte)0x80, (byte)0xA0, (byte)0x01, (byte)0x00};
byte[] secondHeader = {(byte)0x80, (byte)0xA0, (byte)0x00, (byte)0x00};
byte[] firstLen = new byte[1];
byte[] secondLen = new byte[1];
firstLen[0] = (byte)firstCommandData.length;
secondLen[0] = (byte)secondCommandData.length;
byte[] firstCommand = SemsUtil.append(
SemsUtil.append(firstHeader, firstLen), firstCommandData);
byte[] secondCommand = SemsUtil.append(
SemsUtil.append(secondHeader, secondLen), secondCommandData);
Log.d(TAG, "******* Processing LS Certificate 1/2 command");
rapdu = sChannel.transmit(firstCommand);
if (SemsUtil.getSW(rapdu) != (short)0x9000) {
putIntoLog(rapdu, SemsCertResponse);
return Arrays.copyOfRange(rapdu, rapdu.length - 2, rapdu.length);
}
Log.d(TAG, "******* Processing LS Certificate 2/2 command");
rapdu = sChannel.transmit(secondCommand);
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();
}
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 = setDirectories();
return 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;
this.inputScript = scriptIn;
this.outputScript = scriptOut;
this.mSemsCallback = callback;
status = setDirectories();
if (status == SemsStatus.SEMS_STATUS_SUCCESS) {
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 = this.inputScript;
String scriptOut = this.outputScript;
byte[] swReturned = sw9000;
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) {
putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(sw6987);
Log.e(TAG, ">>>>>>>>>> parseHexString returned NULL <<<<<<<<<<");
return;
}
List<SemsTLV> scriptTlvs = SemsTLV.parse(data);
byte[] rapdu;
if ((scriptTlvs == null) || scriptTlvs.size() == 0) {
putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(sw6987);
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) {
closeLogicalChannel(channelNumber);
updateSemsStatus(sw6987);
return;
}
}
// fall-through
case SEMS_STATE_STORE_DATA: {
/*
* STEP 3 of executeScript - Sending SHA1 of Caller package
*/
rapdu = sendSHA1OfCallerPackage(channelNumber,
callerPackageName.getBytes());
if (rapdu == null) {
Log.e(TAG, "sendSHA1OfCallerPackage received incorrect rapdu");
closeLogicalChannel(channelNumber);
updateSemsStatus(rapdu);
return;
}
if (SemsUtil.getSW(rapdu) != (short)0x9000) {
closeLogicalChannel(channelNumber);
updateSemsStatus(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();
putIntoLog(sw6F00, ErrorResponse);
closeLogicalChannel(channelNumber);
updateSemsStatus(sw6F00);
return;
}
putIntoLog(response, SWResponse);
closeLogicalChannel(channelNumber);
updateSemsStatus(response);
return;
}catch(Exception e) {
putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(sw6987);
Log.e(TAG, ">>>>>>>>>> Error : Invalid Script <<<<<<<<<<");
return;
} finally {
/*Always write log file*/
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();
rapdu = sendProcessScript(channelNumber, secCmd);
if (rapdu == null) {
Log.e(TAG, "sendProcessScript received incorrect rapdu");
putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
sw = SemsUtil.getSW(rapdu);
if (sw == (short)0x6310) {
/*
* STEP 8 of executeScript - Process SE Response
*/
putIntoLog(rapdu, SemsResponse);
if ((rapdu = sendToSE(rapdu)) == null) {
putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
putIntoLog(rapdu, SEResponse);
{
rapdu = sendProcessSEResponse(channelNumber, rapdu);
putIntoLog(rapdu, SemsResponse);
while (SemsUtil.getSW(rapdu) == (short)0x6310) {
if ((rapdu = sendToSE(rapdu)) == null) {
putIntoLog(sw6987, ErrorResponse);
rapdu = sw6987;
break;
}
putIntoLog(rapdu, SEResponse);
rapdu = sendProcessSEResponse(channelNumber, rapdu);
putIntoLog(rapdu, SemsResponse);
}
}
if ((SemsUtil.getSW(rapdu) != (short)0x9000) &&
(SemsUtil.getSW(rapdu) != (short)0x6300)) {
putIntoLog(rapdu, ErrorResponse);
break;
}
} else if (sw == (short)0x6320) {
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)) {
putIntoLog(rapdu, SemsResponse);
break;
} else {
putIntoLog(rapdu, SemsResponse);
}
} else {
Log.e(TAG, "Invalid length for secure command");
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");
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) {
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);
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");
closeLogicalChannel(channelNumber);
updateSemsStatus(rapdu);
return SemsStatus.SEMS_STATUS_FAILED;
}
if (SemsUtil.getSW(rapdu) != (short)0x9000) {
Log.e(TAG, "certificate frame command failed");
putIntoLog(rapdu, ErrorResponse);
closeLogicalChannel(channelNumber);
updateSemsStatus(rapdu);
stat = SemsStatus.SEMS_STATUS_FAILED;
}
} else {
Log.e(TAG, "No valid certificate frame found");
closeLogicalChannel(channelNumber);
putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(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");
closeLogicalChannel(channelNumber);
putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(sw6987);
return stat;
}
authFrame = SemsTLV.parse(authFrame.getValue()).get(0);
rapdu = sendAuthenticationFrame(channelNumber, authFrame.getValue());
if (rapdu == null) {
Log.e(TAG, "sendAuthenticationFrame received incorrect rapdu");
closeLogicalChannel(channelNumber);
putIntoLog(sw6987, ErrorResponse);
updateSemsStatus(sw6987);
return stat;
}
putIntoLog(rapdu, SemsAuthResponse);
if (SemsUtil.getSW(rapdu) == (short)0x6310) { // begin_perso cleanup
int i = 0;
if ((rapdu = sendToSE(rapdu)) == null) {
putIntoLog(sw6987, ErrorResponse);
closeLogicalChannel(channelNumber);
updateSemsStatus(sw6987);
return stat;
}
putIntoLog(rapdu, SEResponse);
rapdu = sendProcessSEResponse(channelNumber, rapdu);
putIntoLog(rapdu, SemsResponse);
while (SemsUtil.getSW(rapdu) == (short)0x6310) {
if ((rapdu = sendToSE(rapdu)) == null) {
putIntoLog(sw6987, ErrorResponse);
closeLogicalChannel(channelNumber);
updateSemsStatus(sw6987);
return stat;
}
putIntoLog(rapdu, SEResponse);
rapdu = sendProcessSEResponse(channelNumber, rapdu);
putIntoLog(rapdu, SemsResponse);
i++;
}
} else if (SemsUtil.getSW(rapdu) != (short)0x9000) {
Log.e(TAG, "Processing Authentication frame failed");
putIntoLog(rapdu, SemsResponse);
closeLogicalChannel(channelNumber);
updateSemsStatus(rapdu);
return stat;
} else if (SemsUtil.getSW(rapdu) == (short)0x9000) {
stat = SemsStatus.SEMS_STATUS_SUCCESS;
}
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, sRespOutlog);
}
}
/**
* 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;
}
}