diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
3 files changed, 210 insertions, 28 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/OnDiscoveredUsbDeviceListener.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/OnDiscoveredUsbDeviceListener.java new file mode 100644 index 000000000..6104985be --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/OnDiscoveredUsbDeviceListener.java @@ -0,0 +1,7 @@ +package org.sufficientlysecure.keychain.javacard; + +import android.hardware.usb.UsbDevice; + +public interface OnDiscoveredUsbDeviceListener { +    void usbDeviceDiscovered(UsbDevice usbDevice); +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java new file mode 100644 index 000000000..d140c771c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java @@ -0,0 +1,135 @@ +package org.sufficientlysecure.keychain.javacard; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; + +public class UsbConnectionManager { +    private static final String LOG_TAG = UsbConnectionManager.class.getName(); +    private static final String ACTION_USB_PERMISSION = Constants.PACKAGE_NAME + ".USB_PERMITSSION"; + +    private Activity mActivity; +    private OnDiscoveredUsbDeviceListener mListener; +    private final Semaphore mRunning = new Semaphore(1); +    private final Set<UsbDevice> mProcessedDevices = Collections.newSetFromMap(new ConcurrentHashMap<UsbDevice, Boolean>()); + +    /** +     * 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<String, UsbDevice> 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<Void, Void, IOException>() { +            @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();      }  | 
