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 e47ba5360..e3c6d12da 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/NfcTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/NfcTransport.java @@ -19,13 +19,23 @@ public class NfcTransport implements Transport { this.mTag = tag; } + /** + * Transmit and receive data + * @param data data to transmit + * @return received data + * @throws IOException + */ @Override - public byte[] sendAndReceive(final byte[] data) throws TransportIoException, IOException { + public byte[] transceive(final byte[] data) throws IOException { return mIsoCard.transceive(data); } + /** + * Disconnect and release connection + */ @Override public void release() { + // Not supported } @Override @@ -33,13 +43,18 @@ public class NfcTransport implements Transport { return mIsoCard != null && mIsoCard.isConnected(); } + /** + * Check if Transport supports persistent connections e.g connections which can + * handle multiple operations in one session + * @return true if transport supports persistent connections + */ @Override - public boolean allowPersistentConnection() { + public boolean isPersistentConnectionAllowed() { return false; } /** - * Handle NFC communication and return a result. + * Connect to NFC device. *
* On general communication, see also * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx 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 286a38d1f..7bc53a10a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java @@ -39,7 +39,6 @@ public class SmartcardDevice { return LazyHolder.mSmartcardDevice; } - // METHOD UPDATED [OK] private String getHolderName(String name) { try { String slength; @@ -80,6 +79,7 @@ public class SmartcardDevice { this.mAdminPin = adminPin; } + // NEW MY METHOD public void changeKey(CanonicalizedSecretKey secretKey, Passphrase passphrase) throws IOException { long keyGenerationTimestamp = secretKey.getCreationTime().getTime() / 1000; byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array(); @@ -102,7 +102,7 @@ public class SmartcardDevice { putData(keyType.getTimestampObjectId(), timestampBytes); } - public boolean containsKey(KeyType keyType) throws IOException { + private boolean containsKey(KeyType keyType) throws IOException { return !keyMatchesFingerPrint(keyType, BLANK_FINGERPRINT); } @@ -110,10 +110,17 @@ public class SmartcardDevice { return java.util.Arrays.equals(getMasterKeyFingerprint(keyType.getIdx()), fingerprint); } - // METHOD UPDATED OK + /** + * Connect to device and select pgp applet + * + * @throws IOException + */ public void connectToDevice() throws IOException { + // Connect on transport layer mTransport.connect(); + // Connect on smartcard layer + // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time. // See specification, page 51 String accepted = "9000"; @@ -164,7 +171,6 @@ public class SmartcardDevice { * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83. * @param newPin The new PW1 or PW3. */ - // METHOD UPDATED[OK] public void modifyPin(int pw, byte[] newPin) throws IOException { final int MAX_PW1_LENGTH_INDEX = 1; final int MAX_PW3_LENGTH_INDEX = 3; @@ -210,7 +216,6 @@ public class SmartcardDevice { * @param encryptedSessionKey the encoded session key * @return the decoded session key */ - // METHOD UPDATED [OK] public byte[] decryptSessionKey(byte[] encryptedSessionKey) throws IOException { if (!mPw1ValidatedForDecrypt) { verifyPin(0x82); // (Verify PW1 with mode 82 for decryption) @@ -280,7 +285,6 @@ public class SmartcardDevice { * @param dataObject The data object to be stored. * @param data The data to store in the object */ - // METHOD UPDATED [OK] public void putData(int dataObject, byte[] data) throws IOException { if (data.length > 254) { throw new IOException("Cannot PUT DATA with length > 254"); @@ -315,7 +319,6 @@ public class SmartcardDevice { * 0xB8: Decipherment Key * 0xA4: Authentication Key */ - // METHOD UPDATED [OK] public void putKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase) throws IOException { if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) { @@ -426,10 +429,9 @@ public class SmartcardDevice { * * @return The fingerprints of all subkeys in a contiguous byte array. */ - // METHOD UPDATED [OK] public byte[] getFingerprints() throws IOException { String data = "00CA006E00"; - byte[] buf = mTransport.sendAndReceive(Hex.decode(data)); + byte[] buf = mTransport.transceive(Hex.decode(data)); Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); Log.d(Constants.TAG, "nfcGetFingerprints() Iso7816TLV tlv data:\n" + tlv.prettyPrint()); @@ -446,19 +448,16 @@ public class SmartcardDevice { * * @return Seven bytes in fixed format, plus 0x9000 status word at the end. */ - // METHOD UPDATED [OK] private byte[] getPwStatusBytes() throws IOException { String data = "00CA00C400"; - return mTransport.sendAndReceive(Hex.decode(data)); + return mTransport.transceive(Hex.decode(data)); } - // METHOD UPDATED [OK] public byte[] getAid() throws IOException { String info = "00CA004F00"; - return mTransport.sendAndReceive(Hex.decode(info)); + return mTransport.transceive(Hex.decode(info)); } - // METHOD UPDATED [OK] public String getUserId() throws IOException { String info = "00CA006500"; return getHolderName(communicate(info)); @@ -470,7 +469,6 @@ public class SmartcardDevice { * @param hash the hash for signing * @return a big integer representing the MPI for the given hash */ - // METHOD UPDATED [OK] public byte[] calculateSignature(byte[] hash, int hashAlgo) throws IOException { if (!mPw1ValidatedForSignature) { verifyPin(0x81); // (Verify PW1 with mode 81 for signing) @@ -568,29 +566,23 @@ public class SmartcardDevice { return Hex.decode(signature); } - public boolean isConnected() { - return mTransport != null && mTransport.isConnected(); - } - /** * Transceive data via NFC encoded as Hex */ - // METHOD UPDATED [OK] - private String communicate(String apdu) throws IOException, TransportIoException { - return getHex(mTransport.sendAndReceive(Hex.decode(apdu))); + private String communicate(String apdu) throws IOException { + return getHex(mTransport.transceive(Hex.decode(apdu))); } public Transport getTransport() { return mTransport; } - // NEW METHOD [OK] public boolean isFidesmoToken() { if (isConnected()) { // Check if we can still talk to the card try { // By trying to select any apps that have the Fidesmo AID prefix we can // see if it is a Fidesmo device or not - byte[] mSelectResponse = mTransport.sendAndReceive(Apdu.select(FIDESMO_APPS_AID_PREFIX)); + byte[] mSelectResponse = mTransport.transceive(Apdu.select(FIDESMO_APPS_AID_PREFIX)); // Compare the status returned by our select with the OK status code return Apdu.hasStatus(mSelectResponse, Apdu.OK_APDU); } catch (IOException e) { @@ -614,7 +606,6 @@ public class SmartcardDevice { * @return the public key data objects, in TLV format. For RSA this will be the public modulus * (0x81) and exponent (0x82). These may come out of order; proper TLV parsing is required. */ - // NEW METHOD [OK] public byte[] generateKey(int slot) throws IOException { if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) { throw new IOException("Invalid key slot"); @@ -641,7 +632,6 @@ public class SmartcardDevice { return Hex.decode(publicKeyData); } - // NEW METHOD [OK][OK] private String getDataField(String output) { return output.substring(0, output.length() - 4); } @@ -665,7 +655,6 @@ public class SmartcardDevice { * This works by entering a wrong PIN and then Admin PIN 4 times respectively. * Afterwards, the token is reactivated. */ - // NEW METHOD [OK] public void resetAndWipeToken() throws IOException { String accepted = "9000"; @@ -725,7 +714,11 @@ public class SmartcardDevice { } public boolean isPersistentConnectionAllowed() { - return mTransport != null && mTransport.allowPersistentConnection(); + return mTransport != null && mTransport.isPersistentConnectionAllowed(); + } + + public boolean isConnected() { + return mTransport != null && mTransport.isConnected(); } private static class LazyHolder { 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 5252a95d0..fa8b0695a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/Transport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/Transport.java @@ -2,14 +2,40 @@ package org.sufficientlysecure.keychain.smartcard; import java.io.IOException; +/** + * Abstraction for transmitting APDU commands + */ public interface Transport { - byte[] sendAndReceive(byte[] data) throws IOException; + /** + * Transmit and receive data + * @param data data to transmit + * @return received data + * @throws IOException + */ + byte[] transceive(byte[] data) throws IOException; + /** + * Disconnect and release connection + */ void release(); + /** + * Check if device is was connected to and still is connected + * @return connection status + */ boolean isConnected(); - boolean allowPersistentConnection(); + /** + * Check if Transport supports persistent connections e.g connections which can + * handle multiple operations in one session + * @return true if transport supports persistent connections + */ + boolean isPersistentConnectionAllowed(); + + /** + * Connect to device + * @throws IOException + */ void connect() throws IOException; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/TransportIoException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/TransportIoException.java deleted file mode 100644 index 544dd4045..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/TransportIoException.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.sufficientlysecure.keychain.smartcard; - -import java.io.IOException; - -public class TransportIoException extends IOException { - public TransportIoException() { - } - - public TransportIoException(final String detailMessage) { - super(detailMessage); - } - - public TransportIoException(final String message, final Throwable cause) { - super(message, cause); - } - - public TransportIoException(final Throwable cause) { - super(cause); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionDispatcher.java similarity index 92% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionDispatcher.java index 8a6971fe6..c3068916e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionDispatcher.java @@ -12,7 +12,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.ui.UsbEventReceiverActivity; import org.sufficientlysecure.keychain.util.Log; -public class UsbConnectionManager { +public class UsbConnectionDispatcher { private Activity mActivity; private OnDiscoveredUsbDeviceListener mListener; @@ -36,7 +36,7 @@ public class UsbConnectionManager { } }; - public UsbConnectionManager(final Activity activity, final OnDiscoveredUsbDeviceListener listener) { + public UsbConnectionDispatcher(final Activity activity, final OnDiscoveredUsbDeviceListener listener) { this.mActivity = activity; this.mListener = listener; } 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 2a21c10dd..86f4a687d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbTransport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbTransport.java @@ -19,9 +19,14 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +/** + * Based on USB CCID Specification rev. 1.1 + * http://www.usb.org/developers/docs/devclass_docs/DWG_Smart-Card_CCID_Rev110.pdf + * Implements small subset of these features + */ public class UsbTransport implements Transport { - private static final int CLASS_SMARTCARD = 11; - private static final int TIMEOUT = 20 * 1000; // 2 s + private static final int USB_CLASS_SMARTCARD = 11; + private static final int TIMEOUT = 20 * 1000; // 20s private final UsbManager mUsbManager; private final UsbDevice mUsbDevice; @@ -29,29 +34,24 @@ public class UsbTransport implements Transport { private UsbEndpoint mBulkIn; private UsbEndpoint mBulkOut; private UsbDeviceConnection mConnection; - private byte mCounter = 0; + private byte mCounter; - public UsbTransport(final UsbDevice usbDevice, final UsbManager usbManager) { + public UsbTransport(UsbDevice usbDevice, UsbManager usbManager) { mUsbDevice = usbDevice; mUsbManager = usbManager; } - 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 { + /** + * Manage ICC power, Yubikey requires to power on ICC + * Spec: 6.1.1 PC_to_RDR_IccPowerOn; 6.1.2 PC_to_RDR_IccPowerOff + * + * @param on true to turn ICC on, false to turn it off + * @throws UsbTransportException + */ + private void iccPowerSet(boolean on) throws UsbTransportException { final byte[] iccPowerOn = { - 0x62, + (byte) (on ? 0x62 : 0x63), 0x00, 0x00, 0x00, 0x00, 0x00, mCounter++, @@ -63,22 +63,28 @@ public class UsbTransport implements Transport { } /** - * Get first class 11 (Chip/Smartcard) interface for the device + * Get first class 11 (Chip/Smartcard) interface of the device * * @param device {@link UsbDevice} which will be searched * @return {@link UsbInterface} of smartcard or null if it doesn't exist */ @Nullable - private static UsbInterface getSmartCardInterface(final UsbDevice device) { + private static UsbInterface getSmartCardInterface(UsbDevice device) { for (int i = 0; i < device.getInterfaceCount(); i++) { - final UsbInterface anInterface = device.getInterface(i); - if (anInterface.getInterfaceClass() == CLASS_SMARTCARD) { + UsbInterface anInterface = device.getInterface(i); + if (anInterface.getInterfaceClass() == USB_CLASS_SMARTCARD) { return anInterface; } } return null; } + /** + * Get device's bulk-in and bulk-out endpoints + * + * @param usbInterface usb device interface + * @return pair of builk-in and bulk-out endpoints respectively + */ @NonNull private static Pair