From bd2906a887f22c415f9b194481607660db2216f6 Mon Sep 17 00:00:00 2001 From: Nikita Mikhailov Date: Mon, 4 Apr 2016 00:33:59 +0600 Subject: OTG: Add usb device manager prototype --- OpenKeychain/src/main/AndroidManifest.xml | 5 + .../javacard/OnDiscoveredUsbDeviceListener.java | 7 ++ .../keychain/javacard/UsbConnectionManager.java | 135 +++++++++++++++++++++ .../ui/base/BaseSecurityTokenNfcActivity.java | 96 ++++++++++----- .../src/main/res/xml/usb_device_filter.xml | 4 + 5 files changed, 219 insertions(+), 28 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/OnDiscoveredUsbDeviceListener.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java create mode 100644 OpenKeychain/src/main/res/xml/usb_device_filter.xml diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 74bf936b4..f485e964c 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -43,6 +43,11 @@ android:name="android.hardware.screen.portrait" android:required="false" /> + + + mProcessedDevices = Collections.newSetFromMap(new ConcurrentHashMap()); + + /** + * Receives broadcast when a supported USB device is attached, detached or + * when a permission to communicate to the device has been granted. + */ + private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + UsbDevice usbDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + String deviceName = usbDevice.getDeviceName(); + + if (ACTION_USB_PERMISSION.equals(action)) { + boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, + false); + Log.d(LOG_TAG, "ACTION_USB_PERMISSION: " + permission + " Device: " + deviceName); + + interceptIntent(intent); + + context.unregisterReceiver(mUsbReceiver); + } + } + }; + + private final Thread mWatchThread = new Thread() { + @Override + public void run() { + final UsbManager usbManager = (UsbManager) mActivity.getSystemService(Context.USB_SERVICE); + + while (true) { + mRunning.acquireUninterruptibly(); + mRunning.release(); + + // + + final UsbDevice device = getDevice(usbManager); + if (device != null && !mProcessedDevices.contains(device)) { + mProcessedDevices.add(device); + + final Intent intent = new Intent(ACTION_USB_PERMISSION); + + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_USB_PERMISSION); + mActivity.registerReceiver(mUsbReceiver, filter); + + usbManager.requestPermission(device, PendingIntent.getBroadcast(mActivity, 0, intent, 0)); + } + + try { + sleep(1000); + } catch (InterruptedException ignored) { + } + } + } + }; + + public UsbConnectionManager(final Activity activity, final OnDiscoveredUsbDeviceListener listener) { + this.mActivity = activity; + this.mListener = listener; + mRunning.acquireUninterruptibly(); + mWatchThread.start(); + } + + public void startListeningForDevices() { + mRunning.release(); + } + + public void stopListeningForDevices() { + mRunning.acquireUninterruptibly(); + } + + public void interceptIntent(final Intent intent) { + if (intent == null || intent.getAction() == null) return; + switch (intent.getAction()) { + case UsbManager.ACTION_USB_DEVICE_ATTACHED: { + final UsbManager usbManager = (UsbManager) mActivity.getSystemService(Context.USB_SERVICE); + final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + Intent usbI = new Intent(mActivity, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + usbI.setAction(ACTION_USB_PERMISSION); + usbI.putExtra(UsbManager.EXTRA_DEVICE, device); + PendingIntent pi = PendingIntent.getActivity(mActivity, 0, usbI, PendingIntent.FLAG_CANCEL_CURRENT); + usbManager.requestPermission(device, pi); + break; + } + case ACTION_USB_PERMISSION: { + UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (device != null) + mListener.usbDeviceDiscovered(device); + break; + } + default: + break; + } + } + + private static UsbDevice getDevice(UsbManager manager) { + HashMap deviceList = manager.getDeviceList(); + for (UsbDevice device : deviceList.values()) { + Log.d(LOG_TAG, device.getDeviceName() + " " + device.getDeviceId()); + if (device.getVendorId() == 0x1050 && device.getProductId() == 0x0112) { + Log.d(LOG_TAG, device.getDeviceName() + " OK"); + return device; + } + } + return null; + } +} 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 94d640768..573123daf 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 @@ -20,42 +20,32 @@ package org.sufficientlysecure.keychain.ui.base; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.security.interfaces.RSAPrivateCrtKey; - import android.app.Activity; +import android.app.PendingIntent; import android.content.Intent; import android.content.pm.PackageManager; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.TagLostException; import android.os.AsyncTask; import android.os.Bundle; -import nordpol.Apdu; -import nordpol.IsoCard; -import nordpol.android.TagDispatcher; -import nordpol.android.AndroidCard; -import nordpol.android.OnDiscoveredTagListener; - -import org.bouncycastle.bcpg.HashAlgorithmTags; -import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.javacard.BaseJavacardDevice; import org.sufficientlysecure.keychain.javacard.JavacardDevice; import org.sufficientlysecure.keychain.javacard.NfcTransport; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.javacard.OnDiscoveredUsbDeviceListener; +import org.sufficientlysecure.keychain.javacard.UsbConnectionManager; +import org.sufficientlysecure.keychain.javacard.UsbTransport; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.service.PassphraseCacheService.KeyNotFoundException; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.CreateKeyActivity; @@ -66,27 +56,27 @@ import org.sufficientlysecure.keychain.ui.dialog.FidesmoPgpInstallDialog; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; -import org.sufficientlysecure.keychain.util.Iso7816TLV; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; -public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implements OnDiscoveredTagListener { +import java.io.IOException; + +import nordpol.IsoCard; +import nordpol.android.AndroidCard; +import nordpol.android.OnDiscoveredTagListener; +import nordpol.android.TagDispatcher; + +public abstract class BaseSecurityTokenNfcActivity extends BaseActivity + implements OnDiscoveredTagListener, OnDiscoveredUsbDeviceListener { public static final int REQUEST_CODE_PIN = 1; public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled"; private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android"; - //protected Passphrase mPin; - //protected Passphrase mAdminPin; - //protected boolean mPw1ValidForMultipleSignatures; - //protected boolean mPw1ValidatedForSignature; - //protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming? - //protected boolean mPw3Validated; - public JavacardDevice mJavacardDevice; protected TagDispatcher mTagDispatcher; -// private IsoCard mIsoCard; + protected UsbConnectionManager mUsbDispatcher; private boolean mTagHandlingEnabled; private byte[] mNfcFingerprints; @@ -185,6 +175,43 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen }.execute(); } + + public void usbDeviceDiscovered(final UsbDevice device) { + // Actual NFC operations are executed in doInBackground to not block the UI thread + if(!mTagHandlingEnabled) + return; + new AsyncTask() { + @Override + protected void onPreExecute() { + super.onPreExecute(); + onNfcPreExecute(); + } + + @Override + protected IOException doInBackground(Void... params) { + try { + handleUsbDevice(device); + } catch (IOException e) { + return e; + } + + return null; + } + + @Override + protected void onPostExecute(IOException exception) { + super.onPostExecute(exception); + + if (exception != null) { + handleNfcError(exception); + return; + } + + onNfcPostExecute(); + } + }.execute(); + } + protected void pauseTagHandling() { mTagHandlingEnabled = false; } @@ -198,6 +225,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen super.onCreate(savedInstanceState); mTagDispatcher = TagDispatcher.get(this, this, false, false, true, false); + mUsbDispatcher = new UsbConnectionManager(this, this); // Check whether we're recreating a previously destroyed instance if (savedInstanceState != null) { @@ -228,7 +256,9 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen */ @Override public void onNewIntent(final Intent intent) { - mTagDispatcher.interceptIntent(intent); + if (!mTagDispatcher.interceptIntent(intent)) { + mUsbDispatcher.interceptIntent(intent); + } } private void handleNfcError(IOException e) { @@ -346,6 +376,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen Log.d(Constants.TAG, "BaseNfcActivity.onPause"); mTagDispatcher.disableExclusiveNfc(); + mUsbDispatcher.stopListeningForDevices(); } /** @@ -356,6 +387,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen super.onResume(); Log.d(Constants.TAG, "BaseNfcActivity.onResume"); mTagDispatcher.enableExclusiveNfc(); + mUsbDispatcher.startListeningForDevices(); } protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) { @@ -372,7 +404,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, RequiredInputParcel.createRequiredPassphrase(requiredInput)); startActivityForResult(intent, REQUEST_CODE_PIN); - } catch (KeyNotFoundException e) { + } catch (PassphraseCacheService.KeyNotFoundException e) { throw new AssertionError( "tried to find passphrase for non-existing key. this is a programming error!"); } @@ -425,6 +457,14 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen doNfcInBackground(); } + protected void handleUsbDevice(UsbDevice device) throws IOException { + UsbManager usbManager = (UsbManager) getSystemService(USB_SERVICE); + mJavacardDevice = new BaseJavacardDevice(new UsbTransport(device, usbManager)); + mJavacardDevice.connectToDevice(); + + doNfcInBackground(); + } + public boolean isNfcConnected() { return mJavacardDevice.isConnected(); } diff --git a/OpenKeychain/src/main/res/xml/usb_device_filter.xml b/OpenKeychain/src/main/res/xml/usb_device_filter.xml new file mode 100644 index 000000000..cc0599009 --- /dev/null +++ b/OpenKeychain/src/main/res/xml/usb_device_filter.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file -- cgit v1.2.3