From b5eb6468fecfc16ea041eb0f4bf48c37ec2e81f2 Mon Sep 17 00:00:00 2001 From: Nikita Mikhailov Date: Fri, 8 Apr 2016 00:41:53 +0600 Subject: OTG: Add support for persistent usb connection No need to reinsert token on each operation --- .../keychain/smartcard/NfcTransport.java | 5 ++ .../keychain/smartcard/SmartcardDevice.java | 49 ++++++++++++---- .../keychain/smartcard/Transport.java | 2 + .../keychain/smartcard/UsbConnectionManager.java | 8 +++ .../keychain/smartcard/UsbTransport.java | 39 ++++--------- .../ui/CreateSecurityTokenImportResetFragment.java | 6 +- .../ui/SecurityTokenOperationActivity.java | 67 ++++++++++++++++------ .../ui/base/BaseSecurityTokenNfcActivity.java | 67 ++++++---------------- 8 files changed, 132 insertions(+), 111 deletions(-) (limited to 'OpenKeychain/src/main/java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/NfcTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/NfcTransport.java index 557c6f37d..d56f5b5bf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/NfcTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/NfcTransport.java @@ -28,4 +28,9 @@ public class NfcTransport implements Transport { public boolean isConnected() { return mIsoCard.isConnected(); } + + @Override + public boolean allowPersistentConnection() { + return false; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java index b86c3cf4c..4420c0c88 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java @@ -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(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/Transport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/Transport.java index e01d7da16..9b0ad2998 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/Transport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/Transport.java @@ -8,4 +8,6 @@ public interface Transport { void release(); boolean isConnected(); + + boolean allowPersistentConnection(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java index 8a6971fe6..e6fb5b04d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java @@ -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()); + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbTransport.java index 08f296c25..2d435ccbe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbTransport.java @@ -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; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java index a0e93ed85..aba06ac47 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java index c68936577..5f0093678 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java @@ -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() { - @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() { + @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(); + } } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java index a6f8c0b0f..ecec98aaf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java @@ -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() { @@ -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; + } } -- cgit v1.2.3