package org.nick.passman;
import static org.nick.passman.Hex.fromHex;
import static org.nick.passman.Hex.toHex;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.simalliance.openmobileapi.Channel;
import org.simalliance.openmobileapi.Session;
import android.util.Base64;
import android.util.Log;
public class PmAppletClient {
private static final String TAG = PmAppletClient.class.getSimpleName();
private static final boolean DEBUG = true;
private static final short SW_SUCCESS = (short) 0x9000;
private static final String APPLET_AID = "73 69 6d 70 61 73 73 6d 61 6e 01";
private static final byte CLA = (byte) 0x80;
private static final byte INS_GET_STATUS = (byte) 0x1;
private static final byte INS_GEN_RANDOM = (byte) 0x2;
private static final byte INS_GEN_KEY = (byte) 0x03;
private static final byte INS_ENCRYPT = (byte) 0x4;
private static final byte INS_DECRYPT = (byte) 0x5;
private static final byte INS_CLEAR = (byte) 0x6;
private static final String GET_RESPONSE_CMD = "00 C0 00 00 ";
private static final String GET_STATUS_CMD = "80 01 00 00 00 01";
private static final String GENERATE_KEYS_CMD = "80 03 00 00 00 00";
private static final String ENCRYPT_CMD = "80 04 00 00 ";
private static final String DECRYPT_CMD = "80 05 00 00 ";
private static final String CLEAR_CMD = "80 06 00 00 00 00";
private Session session;
private Channel channel;
public PmAppletClient(Session session) {
this.session = session;
}
public void connect() throws IOException {
channel = session.openLogicalChannel(fromHex(APPLET_AID));
}
public void disconnect() {
if (channel != null) {
channel.close();
}
}
private void init() throws IOException {
if (channel == null || channel.isClosed()) {
connect();
}
}
public boolean isInitialized() throws IOException {
init();
ResponseApdu rapdu = transmit(fromHex(GET_STATUS_CMD));
checkSw(rapdu);
return rapdu.getData()[0] == 1;
}
public void generateKeys() throws IOException {
init();
ResponseApdu rapdu = transmit(fromHex(GENERATE_KEYS_CMD));
checkSw(rapdu);
}
private void checkSw(ResponseApdu rapdu) {
if (rapdu.getSW() != SW_SUCCESS) {
throw new AppletException(rapdu.getSW());
}
}
public byte[] encrypt(byte[] data) throws IOException {
init();
String cmdStr = ENCRYPT_CMD + String.format("%02x", data.length)
+ toHex(data) + "00";
ResponseApdu rapdu = transmit(fromHex(cmdStr));
checkSw(rapdu);
return rapdu.getData();
}
public byte[] encrypt(String str) throws IOException {
try {
return encrypt(str.trim().getBytes("ASCII"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public String encryptStr(String str) throws IOException {
try {
return Base64.encodeToString(encrypt(str.trim().getBytes("ASCII")),
Base64.NO_WRAP);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public byte[] decrypt(byte[] data) throws IOException {
init();
String cmdStr = DECRYPT_CMD
+ String.format("%02x", (byte) (data.length & 0xff))
+ toHex(data) + "00";
ResponseApdu rapdu = transmit(fromHex(cmdStr));
checkSw(rapdu);
return rapdu.getData();
}
public byte[] decrypt(String base64) throws IOException {
return decrypt(Base64.decode(base64.trim(), Base64.NO_WRAP));
}
public String decryptStr(byte[] encrypted) throws IOException {
return new String(decrypt(encrypted), "ASCII");
}
public String decryptStr(String base64) throws IOException {
try {
return new String(decrypt(Base64.decode(base64.trim(),
Base64.NO_WRAP)), "ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public void clear() throws IOException {
init();
ResponseApdu rapdu = transmit(fromHex(CLEAR_CMD));
checkSw(rapdu);
}
private ResponseApdu transmit(byte[] cmd) throws IOException {
log(cmd);
ResponseApdu response = new ResponseApdu(channel.transmit(cmd));
log(response);
while (response.isDataRemaining()) {
cmd = fromHex(GET_RESPONSE_CMD
+ String.format("%02x",
(byte) (response.getRemainingDataLength() & 0xff)));
log(cmd);
response = new ResponseApdu(channel.transmit(cmd));
log(response);
}
return response;
}
private static void log(byte[] cmd) {
if (DEBUG) {
Log.d(TAG, String.format("--> %s", Hex.toHex(cmd)));
}
}
private static void log(ResponseApdu response) {
if (DEBUG) {
Log.d(TAG, String.format("<-- %s", response.toString()));
}
}
}