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() {
|
public boolean isConnected() {
|
||||||
return mIsoCard.isConnected();
|
return mIsoCard.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean allowPersistentConnection() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,32 @@ public class SmartcardDevice {
|
|||||||
private boolean mPw3Validated;
|
private boolean mPw3Validated;
|
||||||
private boolean mTagHandlingEnabled;
|
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) {
|
private static String getHex(byte[] raw) {
|
||||||
@@ -541,15 +566,8 @@ public class SmartcardDevice {
|
|||||||
return Hex.decode(signature);
|
return Hex.decode(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getHolderName(String name) {
|
public boolean isConnected() {
|
||||||
String slength;
|
return mTransport != null && mTransport.isConnected();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -560,8 +578,8 @@ public class SmartcardDevice {
|
|||||||
return getHex(mTransport.sendAndReceive(Hex.decode(apdu)));
|
return getHex(mTransport.sendAndReceive(Hex.decode(apdu)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public Transport getTransport() {
|
||||||
return mTransport.isConnected();
|
return mTransport;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW METHOD [OK]
|
// NEW METHOD [OK]
|
||||||
@@ -702,6 +720,13 @@ public class SmartcardDevice {
|
|||||||
|
|
||||||
public void setTransport(Transport mTransport) {
|
public void setTransport(Transport mTransport) {
|
||||||
this.mTransport = 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();
|
void release();
|
||||||
|
|
||||||
boolean isConnected();
|
boolean isConnected();
|
||||||
|
|
||||||
|
boolean allowPersistentConnection();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,12 @@ public class UsbConnectionManager {
|
|||||||
public void onStop() {
|
public void onStop() {
|
||||||
mActivity.unregisterReceiver(mUsbReceiver);
|
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.Arrays;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
@@ -45,33 +47,7 @@ public class UsbTransport implements Transport {
|
|||||||
// check result
|
// check result
|
||||||
|
|
||||||
powerOn();
|
powerOn();
|
||||||
|
Log.d(Constants.TAG, "Usb transport connected");
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void powerOff() throws TransportIoException {
|
private void powerOff() throws TransportIoException {
|
||||||
@@ -147,6 +123,11 @@ public class UsbTransport implements Transport {
|
|||||||
return mUsbManager.getDeviceList().containsValue(mUsbDevice);
|
return mUsbManager.getDeviceList().containsValue(mUsbDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean allowPersistentConnection() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] sendAndReceive(byte[] data) throws TransportIoException {
|
public byte[] sendAndReceive(byte[] data) throws TransportIoException {
|
||||||
send(data);
|
send(data);
|
||||||
@@ -223,4 +204,8 @@ public class UsbTransport implements Transport {
|
|||||||
private boolean isXfrBlockNotReady(byte[] bytes) {
|
private boolean isXfrBlockNotReady(byte[] bytes) {
|
||||||
return getStatus(bytes) == 2;
|
return getStatus(bytes) == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UsbDevice getUsbDevice() {
|
||||||
|
return mUsbDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,9 +249,9 @@ public class CreateSecurityTokenImportResetFragment
|
|||||||
@Override
|
@Override
|
||||||
public void doNfcInBackground() throws IOException {
|
public void doNfcInBackground() throws IOException {
|
||||||
|
|
||||||
mTokenFingerprints = mCreateKeyActivity.mSmartcardDevice.getFingerprints();
|
mTokenFingerprints = mCreateKeyActivity.getSmartcardDevice().getFingerprints();
|
||||||
mTokenAid = mCreateKeyActivity.mSmartcardDevice.getAid();
|
mTokenAid = mCreateKeyActivity.getSmartcardDevice().getAid();
|
||||||
mTokenUserId = mCreateKeyActivity.mSmartcardDevice.getUserId();
|
mTokenUserId = mCreateKeyActivity.getSmartcardDevice().getUserId();
|
||||||
|
|
||||||
byte[] fp = new byte[20];
|
byte[] fp = new byte[20];
|
||||||
ByteBuffer.wrap(fp).put(mTokenFingerprints, 0, 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
|
if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD
|
||||||
&& mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_RESET_CARD) {
|
&& mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_RESET_CARD) {
|
||||||
obtainSecurityTokenPin(mRequiredInput);
|
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);
|
nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
if (mSmartcardDevice.allowPersistentConnection()) {
|
||||||
@Override
|
// Just close
|
||||||
protected Void doInBackground(Void... params) {
|
finish();
|
||||||
// check all 200ms if Security Token has been taken away
|
} else {
|
||||||
while (true) {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
if (isNfcConnected()) {
|
@Override
|
||||||
try {
|
protected Void doInBackground(Void... params) {
|
||||||
Thread.sleep(200);
|
// check all 200ms if Security Token has been taken away
|
||||||
} catch (InterruptedException ignored) {
|
while (true) {
|
||||||
|
if (isNfcConnected()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void result) {
|
protected void onPostExecute(Void result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import android.nfc.TagLostException;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
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.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
import org.sufficientlysecure.keychain.smartcard.SmartcardDevice;
|
|
||||||
import org.sufficientlysecure.keychain.smartcard.NfcTransport;
|
import org.sufficientlysecure.keychain.smartcard.NfcTransport;
|
||||||
import org.sufficientlysecure.keychain.smartcard.OnDiscoveredUsbDeviceListener;
|
import org.sufficientlysecure.keychain.smartcard.OnDiscoveredUsbDeviceListener;
|
||||||
|
import org.sufficientlysecure.keychain.smartcard.SmartcardDevice;
|
||||||
import org.sufficientlysecure.keychain.smartcard.UsbConnectionManager;
|
import org.sufficientlysecure.keychain.smartcard.UsbConnectionManager;
|
||||||
import org.sufficientlysecure.keychain.smartcard.UsbTransport;
|
import org.sufficientlysecure.keychain.smartcard.UsbTransport;
|
||||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
|
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";
|
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 TagDispatcher mTagDispatcher;
|
||||||
protected UsbConnectionManager mUsbDispatcher;
|
protected UsbConnectionManager mUsbDispatcher;
|
||||||
private boolean mTagHandlingEnabled;
|
private boolean mTagHandlingEnabled;
|
||||||
@@ -175,7 +174,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
|||||||
|
|
||||||
|
|
||||||
public void usbDeviceDiscovered(final UsbDevice device) {
|
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)
|
if (!mTagHandlingEnabled)
|
||||||
return;
|
return;
|
||||||
new AsyncTask<Void, Void, IOException>() {
|
new AsyncTask<Void, Void, IOException>() {
|
||||||
@@ -372,7 +371,6 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
|||||||
Log.d(Constants.TAG, "BaseNfcActivity.onPause");
|
Log.d(Constants.TAG, "BaseNfcActivity.onPause");
|
||||||
|
|
||||||
mTagDispatcher.disableExclusiveNfc();
|
mTagDispatcher.disableExclusiveNfc();
|
||||||
// mUsbDispatcher.stopListeningForDevices();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -453,10 +451,15 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void handleUsbDevice(UsbDevice device) throws IOException {
|
protected void handleUsbDevice(UsbDevice device) throws IOException {
|
||||||
UsbManager usbManager = (UsbManager) getSystemService(USB_SERVICE);
|
// Don't reconnect if device was already connected
|
||||||
mSmartcardDevice.setTransport(new UsbTransport(device, usbManager));
|
if (!mSmartcardDevice.isConnected()
|
||||||
mSmartcardDevice.connectToDevice();
|
|| !(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();
|
doNfcInBackground();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,48 +467,6 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
|||||||
return mSmartcardDevice.isConnected();
|
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 class IsoDepNotSupportedException extends IOException {
|
||||||
|
|
||||||
public IsoDepNotSupportedException(String detailMessage) {
|
public IsoDepNotSupportedException(String detailMessage) {
|
||||||
@@ -575,4 +536,8 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
mUsbDispatcher.onStart();
|
mUsbDispatcher.onStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SmartcardDevice getSmartcardDevice() {
|
||||||
|
return mSmartcardDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user