From 79a0918072e2b4a01f328cb8d8d2a0a8761394f6 Mon Sep 17 00:00:00 2001 From: Nikita Mikhailov Date: Wed, 6 Apr 2016 01:33:01 +0600 Subject: OTG: Fix usb transport --- .../keychain/javacard/BaseJavacardDevice.java | 20 ---- .../keychain/javacard/UsbConnectionManager.java | 9 +- .../keychain/javacard/UsbTransport.java | 122 +++++++++++++++++---- 3 files changed, 108 insertions(+), 43 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/BaseJavacardDevice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/BaseJavacardDevice.java index f81d234b3..796b4e1f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/BaseJavacardDevice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/BaseJavacardDevice.java @@ -393,26 +393,6 @@ public class BaseJavacardDevice implements JavacardDevice { Arrays.fill(dataToSend, (byte) 0); } - - /** - * Return the key id from application specific data stored on tag, or null - * if it doesn't exist. - * - * @param idx Index of the key to return the fingerprint from. - * @return The long key id of the requested key, or null if not found. - */ - public Long nfcGetKeyId(int idx) throws IOException { - byte[] fp = getMasterKeyFingerprint(idx); - if (fp == null) { - return null; - } - ByteBuffer buf = ByteBuffer.wrap(fp); - // skip first 12 bytes of the fingerprint - buf.position(12); - // the last eight bytes are the key id (big endian, which is default order in ByteBuffer) - return buf.getLong(); - } - /** * Return fingerprints of all keys from application specific data stored * on tag, or null if data not available. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java index 9dd3bc028..6b049159a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbConnectionManager.java @@ -80,13 +80,14 @@ public class UsbConnectionManager { false); Log.d(LOG_TAG, "ACTION_USB_PERMISSION: " + permission + " Device: " + deviceName); - interceptIntent(intent); + if (permission) { + interceptIntent(intent); + } context.unregisterReceiver(mUsbReceiver); } } }; - private Handler handler = new Handler(Looper.getMainLooper()); public UsbConnectionManager(final Activity activity, final OnDiscoveredUsbDeviceListener listener) { this.mActivity = activity; @@ -98,7 +99,7 @@ public class UsbConnectionManager { private static UsbDevice getDevice(UsbManager manager) { HashMap deviceList = manager.getDeviceList(); for (UsbDevice device : deviceList.values()) { - if (device.getVendorId() == 0x1050 && device.getProductId() == 0x0112) { + if (device.getVendorId() == 0x1050 && (device.getProductId() == 0x0112 || device.getProductId() == 0x0115)) { return device; } } @@ -139,9 +140,11 @@ public class UsbConnectionManager { public void onDestroy() { mStopped.set(true); + mRunning.release(); try { mActivity.unregisterReceiver(mUsbReceiver); } catch (IllegalArgumentException ignore) { } + mActivity = null; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbTransport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbTransport.java index f2af34c39..07697f11e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/UsbTransport.java @@ -11,10 +11,14 @@ import android.support.annotation.Nullable; import android.util.Pair; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; public class UsbTransport implements Transport { private static final int CLASS_SMARTCARD = 11; - private static final int TIMEOUT = 1000; // 1 s + private static final int TIMEOUT = 20 * 1000; // 2 s private final UsbManager mUsbManager; private final UsbDevice mUsbDevice; @@ -22,7 +26,7 @@ public class UsbTransport implements Transport { private final UsbEndpoint mBulkIn; private final UsbEndpoint mBulkOut; private final UsbDeviceConnection mConnection; - private byte counter = 0; + private byte mCounter = 0; public UsbTransport(final UsbDevice usbDevice, final UsbManager usbManager) throws TransportIoException { mUsbDevice = usbDevice; @@ -40,17 +44,60 @@ public class UsbTransport implements Transport { mConnection.claimInterface(mUsbInterface, true); // 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(); + } + + private void powerOff() throws TransportIoException { + final byte[] iccPowerOff = { + 0x63, + 0x00, 0x00, 0x00, 0x00, + 0x00, + mCounter++, + 0x00, + 0x00, 0x00 + }; + sendRaw(iccPowerOff); + receive(); + } + + void powerOn() throws TransportIoException { final byte[] iccPowerOn = { 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, - counter++, - 0x03, + mCounter++, + 0x00, 0x00, 0x00 }; sendRaw(iccPowerOn); - receiveRaw(); - // Check result + receive(); } /** @@ -101,27 +148,58 @@ public class UsbTransport implements Transport { } @Override - public byte[] sendAndReceive(final byte[] data) throws TransportIoException { + public byte[] sendAndReceive(byte[] data) throws TransportIoException { send(data); - return receive(); + byte[] bytes; + do { + bytes = receive(); + } while (isXfrBlockNotReady(bytes)); + + checkXfrBlockResult(bytes); + return Arrays.copyOfRange(bytes, 10, bytes.length); } - public void send(final byte[] d) throws TransportIoException { + public void send(byte[] d) throws TransportIoException { int l = d.length; byte[] data = Arrays.concatenate(new byte[]{ 0x6f, (byte) l, (byte) (l >> 8), (byte) (l >> 16), (byte) (l >> 24), 0x00, - counter++, - 0x01, + mCounter++, + 0x00, 0x00, 0x00}, d); - sendRaw(data); + + int send = 0; + while (send < data.length) { + final int len = Math.min(mBulkIn.getMaxPacketSize(), data.length - send); + sendRaw(Arrays.copyOfRange(data, send, send + len)); + send += len; + } } public byte[] receive() throws TransportIoException { - final byte[] bytes = receiveRaw(); - return Arrays.copyOfRange(bytes, 10, bytes.length); + byte[] buffer = new byte[mBulkIn.getMaxPacketSize()]; + byte[] result = null; + int readBytes = 0, totalBytes = 0; + + do { + int res = mConnection.bulkTransfer(mBulkIn, buffer, buffer.length, TIMEOUT); + if (res < 0) { + throw new TransportIoException("USB error, failed to receive response " + res); + } + if (result == null) { + if (res < 10) { + throw new TransportIoException("USB error, failed to receive ccid header"); + } + totalBytes = ByteBuffer.wrap(buffer, 1, 4).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get() + 10; + result = new byte[totalBytes]; + } + System.arraycopy(buffer, 0, result, readBytes, res); + readBytes += res; + } while (readBytes < totalBytes); + + return result; } private void sendRaw(final byte[] data) throws TransportIoException { @@ -131,14 +209,18 @@ public class UsbTransport implements Transport { } } - private byte[] receiveRaw() throws TransportIoException { - byte[] buffer = new byte[1024]; + private byte getStatus(byte[] bytes) { + return (byte) ((bytes[7] >> 6) & 0x03); + } - int res = mConnection.bulkTransfer(mBulkIn, buffer, buffer.length, TIMEOUT); - if (res < 0) { - throw new TransportIoException("USB error, failed to receive response " + res); + private void checkXfrBlockResult(byte[] bytes) throws TransportIoException { + final byte status = getStatus(bytes); + if (status != 0) { + throw new TransportIoException("CCID error, status " + status + " error code: " + Hex.toHexString(bytes, 8, 1)); } + } - return Arrays.copyOfRange(buffer, 0, res); + private boolean isXfrBlockNotReady(byte[] bytes) { + return getStatus(bytes) == 2; } } -- cgit v1.2.3