OTG: Add support for persistent usb connection
No need to reinsert token on each operation
This commit is contained in:
@@ -28,4 +28,9 @@ public class NfcTransport implements Transport {
|
||||
public boolean isConnected() {
|
||||
return mIsoCard.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowPersistentConnection() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,32 @@ public class SmartcardDevice {
|
||||
private boolean mPw3Validated;
|
||||
private boolean mTagHandlingEnabled;
|
||||
|
||||
public SmartcardDevice() {
|
||||
protected SmartcardDevice() {
|
||||
}
|
||||
|
||||
public static SmartcardDevice getInstance() {
|
||||
return LazyHolder.mSmartcardDevice;
|
||||
}
|
||||
|
||||
// METHOD UPDATED [OK]
|
||||
private String getHolderName(String name) {
|
||||
try {
|
||||
String slength;
|
||||
int ilength;
|
||||
name = name.substring(6);
|
||||
slength = name.substring(0, 2);
|
||||
ilength = Integer.parseInt(slength, 16) * 2;
|
||||
name = name.substring(2, ilength + 2);
|
||||
name = (new String(Hex.decode(name))).replace('<', ' ');
|
||||
return name;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// try-catch for https://github.com/FluffyKaon/OpenPGP-Card
|
||||
// Note: This should not happen, but happens with
|
||||
// https://github.com/FluffyKaon/OpenPGP-Card, thus return an empty string for now!
|
||||
|
||||
Log.e(Constants.TAG, "Couldn't get holder name, returning empty string!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHex(byte[] raw) {
|
||||
@@ -541,15 +566,8 @@ public class SmartcardDevice {
|
||||
return Hex.decode(signature);
|
||||
}
|
||||
|
||||
private String getHolderName(String name) {
|
||||
String slength;
|
||||
int ilength;
|
||||
name = name.substring(6);
|
||||
slength = name.substring(0, 2);
|
||||
ilength = Integer.parseInt(slength, 16) * 2;
|
||||
name = name.substring(2, ilength + 2);
|
||||
name = (new String(Hex.decode(name))).replace('<', ' ');
|
||||
return (name);
|
||||
public boolean isConnected() {
|
||||
return mTransport != null && mTransport.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -560,8 +578,8 @@ public class SmartcardDevice {
|
||||
return getHex(mTransport.sendAndReceive(Hex.decode(apdu)));
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return mTransport.isConnected();
|
||||
public Transport getTransport() {
|
||||
return mTransport;
|
||||
}
|
||||
|
||||
// NEW METHOD [OK]
|
||||
@@ -702,6 +720,13 @@ public class SmartcardDevice {
|
||||
|
||||
public void setTransport(Transport mTransport) {
|
||||
this.mTransport = mTransport;
|
||||
}
|
||||
|
||||
public boolean allowPersistentConnection() {
|
||||
return mTransport != null && mTransport.allowPersistentConnection();
|
||||
}
|
||||
|
||||
private static class LazyHolder {
|
||||
private static final SmartcardDevice mSmartcardDevice = new SmartcardDevice();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,6 @@ public interface Transport {
|
||||
void release();
|
||||
|
||||
boolean isConnected();
|
||||
|
||||
boolean allowPersistentConnection();
|
||||
}
|
||||
|
||||
@@ -51,4 +51,12 @@ public class UsbConnectionManager {
|
||||
public void onStop() {
|
||||
mActivity.unregisterReceiver(mUsbReceiver);
|
||||
}
|
||||
|
||||
public void rescanDevices() {
|
||||
final SmartcardDevice smartcardDevice = SmartcardDevice.getInstance();
|
||||
if (smartcardDevice.isConnected()
|
||||
&& (smartcardDevice.getTransport() instanceof UsbTransport)) {
|
||||
mListener.usbDeviceDiscovered(((UsbTransport) smartcardDevice.getTransport()).getUsbDevice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import android.util.Pair;
|
||||
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -45,33 +47,7 @@ public class UsbTransport implements Transport {
|
||||
// check result
|
||||
|
||||
powerOn();
|
||||
|
||||
setTimings();
|
||||
}
|
||||
|
||||
private void setTimings() throws TransportIoException {
|
||||
byte[] data = {
|
||||
0x6C,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
mCounter++,
|
||||
0x00, 0x00, 0x00
|
||||
};
|
||||
sendRaw(data);
|
||||
data = receive();
|
||||
|
||||
data[0] = 0x61;
|
||||
data[1] = 0x04;
|
||||
data[2] = data[3] = data[4] = 0x00;
|
||||
data[5] = 0x00;
|
||||
data[6] = mCounter++;
|
||||
data[7] = 0x00;
|
||||
data[8] = data[9] = 0x00;
|
||||
|
||||
data[13] = 1;
|
||||
|
||||
sendRaw(data);
|
||||
receive();
|
||||
Log.d(Constants.TAG, "Usb transport connected");
|
||||
}
|
||||
|
||||
private void powerOff() throws TransportIoException {
|
||||
@@ -147,6 +123,11 @@ public class UsbTransport implements Transport {
|
||||
return mUsbManager.getDeviceList().containsValue(mUsbDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowPersistentConnection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sendAndReceive(byte[] data) throws TransportIoException {
|
||||
send(data);
|
||||
@@ -223,4 +204,8 @@ public class UsbTransport implements Transport {
|
||||
private boolean isXfrBlockNotReady(byte[] bytes) {
|
||||
return getStatus(bytes) == 2;
|
||||
}
|
||||
|
||||
public UsbDevice getUsbDevice() {
|
||||
return mUsbDevice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,9 +249,9 @@ public class CreateSecurityTokenImportResetFragment
|
||||
@Override
|
||||
public void doNfcInBackground() throws IOException {
|
||||
|
||||
mTokenFingerprints = mCreateKeyActivity.mSmartcardDevice.getFingerprints();
|
||||
mTokenAid = mCreateKeyActivity.mSmartcardDevice.getAid();
|
||||
mTokenUserId = mCreateKeyActivity.mSmartcardDevice.getUserId();
|
||||
mTokenFingerprints = mCreateKeyActivity.getSmartcardDevice().getFingerprints();
|
||||
mTokenAid = mCreateKeyActivity.getSmartcardDevice().getAid();
|
||||
mTokenUserId = mCreateKeyActivity.getSmartcardDevice().getUserId();
|
||||
|
||||
byte[] fp = new byte[20];
|
||||
ByteBuffer.wrap(fp).put(mTokenFingerprints, 0, 20);
|
||||
|
||||
@@ -140,6 +140,32 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
|
||||
if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD
|
||||
&& mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_RESET_CARD) {
|
||||
obtainSecurityTokenPin(mRequiredInput);
|
||||
checkPinAvailability();
|
||||
} else {
|
||||
// No need for pin, rescan USB devices
|
||||
mUsbDispatcher.rescanDevices();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (REQUEST_CODE_PIN == requestCode) {
|
||||
checkPinAvailability();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPinAvailability() {
|
||||
try {
|
||||
Passphrase passphrase = PassphraseCacheService.getCachedPassphrase(this,
|
||||
mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
|
||||
if (passphrase != null) {
|
||||
// Rescan USB devices
|
||||
mUsbDispatcher.rescanDevices();
|
||||
}
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
throw new AssertionError(
|
||||
"tried to find passphrase for non-existing key. this is a programming error!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,28 +301,33 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenNfcActivity
|
||||
|
||||
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
// check all 200ms if Security Token has been taken away
|
||||
while (true) {
|
||||
if (isNfcConnected()) {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException ignored) {
|
||||
if (mSmartcardDevice.allowPersistentConnection()) {
|
||||
// Just close
|
||||
finish();
|
||||
} else {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
// check all 200ms if Security Token has been taken away
|
||||
while (true) {
|
||||
if (isNfcConnected()) {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
super.onPostExecute(result);
|
||||
finish();
|
||||
}
|
||||
}.execute();
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
super.onPostExecute(result);
|
||||
finish();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.nfc.TagLostException;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
@@ -41,9 +40,9 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.smartcard.SmartcardDevice;
|
||||
import org.sufficientlysecure.keychain.smartcard.NfcTransport;
|
||||
import org.sufficientlysecure.keychain.smartcard.OnDiscoveredUsbDeviceListener;
|
||||
import org.sufficientlysecure.keychain.smartcard.SmartcardDevice;
|
||||
import org.sufficientlysecure.keychain.smartcard.UsbConnectionManager;
|
||||
import org.sufficientlysecure.keychain.smartcard.UsbTransport;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
|
||||
@@ -72,7 +71,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
||||
|
||||
private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android";
|
||||
|
||||
public SmartcardDevice mSmartcardDevice = new SmartcardDevice();
|
||||
protected SmartcardDevice mSmartcardDevice = SmartcardDevice.getInstance();
|
||||
protected TagDispatcher mTagDispatcher;
|
||||
protected UsbConnectionManager mUsbDispatcher;
|
||||
private boolean mTagHandlingEnabled;
|
||||
@@ -175,7 +174,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
||||
|
||||
|
||||
public void usbDeviceDiscovered(final UsbDevice device) {
|
||||
// Actual NFC operations are executed in doInBackground to not block the UI thread
|
||||
// Actual USB operations are executed in doInBackground to not block the UI thread
|
||||
if (!mTagHandlingEnabled)
|
||||
return;
|
||||
new AsyncTask<Void, Void, IOException>() {
|
||||
@@ -372,7 +371,6 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
||||
Log.d(Constants.TAG, "BaseNfcActivity.onPause");
|
||||
|
||||
mTagDispatcher.disableExclusiveNfc();
|
||||
// mUsbDispatcher.stopListeningForDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -453,10 +451,15 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
||||
}
|
||||
|
||||
protected void handleUsbDevice(UsbDevice device) throws IOException {
|
||||
UsbManager usbManager = (UsbManager) getSystemService(USB_SERVICE);
|
||||
mSmartcardDevice.setTransport(new UsbTransport(device, usbManager));
|
||||
mSmartcardDevice.connectToDevice();
|
||||
// Don't reconnect if device was already connected
|
||||
if (!mSmartcardDevice.isConnected()
|
||||
|| !(mSmartcardDevice.getTransport() instanceof UsbTransport)
|
||||
|| !((UsbTransport) mSmartcardDevice.getTransport()).getUsbDevice().equals(device)) {
|
||||
UsbManager usbManager = (UsbManager) getSystemService(USB_SERVICE);
|
||||
|
||||
mSmartcardDevice.setTransport(new UsbTransport(device, usbManager));
|
||||
mSmartcardDevice.connectToDevice();
|
||||
}
|
||||
doNfcInBackground();
|
||||
}
|
||||
|
||||
@@ -464,48 +467,6 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
||||
return mSmartcardDevice.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses out the status word from a JavaCard response string.
|
||||
*
|
||||
* @param response A hex string with the response from the token
|
||||
* @return A short indicating the SW1/SW2, or 0 if a status could not be determined.
|
||||
*/
|
||||
short parseCardStatus(String response) {
|
||||
if (response.length() < 4) {
|
||||
return 0; // invalid input
|
||||
}
|
||||
|
||||
try {
|
||||
return Short.parseShort(response.substring(response.length() - 4), 16);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String getHolderName(String name) {
|
||||
try {
|
||||
String slength;
|
||||
int ilength;
|
||||
name = name.substring(6);
|
||||
slength = name.substring(0, 2);
|
||||
ilength = Integer.parseInt(slength, 16) * 2;
|
||||
name = name.substring(2, ilength + 2);
|
||||
name = (new String(Hex.decode(name))).replace('<', ' ');
|
||||
return name;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// try-catch for https://github.com/FluffyKaon/OpenPGP-Card
|
||||
// Note: This should not happen, but happens with
|
||||
// https://github.com/FluffyKaon/OpenPGP-Card, thus return an empty string for now!
|
||||
|
||||
Log.e(Constants.TAG, "Couldn't get holder name, returning empty string!", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHex(byte[] raw) {
|
||||
return new String(Hex.encode(raw));
|
||||
}
|
||||
|
||||
public class IsoDepNotSupportedException extends IOException {
|
||||
|
||||
public IsoDepNotSupportedException(String detailMessage) {
|
||||
@@ -575,4 +536,8 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
||||
super.onStart();
|
||||
mUsbDispatcher.onStart();
|
||||
}
|
||||
|
||||
public SmartcardDevice getSmartcardDevice() {
|
||||
return mSmartcardDevice;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user