From 3798249570e97861793f5d0ebc695d94e8d5ddcd Mon Sep 17 00:00:00 2001 From: Nikita Mikhailov Date: Thu, 7 Apr 2016 01:22:24 +0600 Subject: OTG: Implement hidden activity usb detection technique --- OpenKeychain/src/main/AndroidManifest.xml | 18 + .../keychain/javacard/BaseJavacardDevice.java | 712 --------------------- .../keychain/javacard/JavacardDevice.java | 105 --- .../keychain/smartcard/SmartcardDevice.java | 2 +- .../keychain/smartcard/UsbConnectionManager.java | 118 +--- .../keychain/ui/UsbEventReceiverActivity.java | 42 ++ .../ui/base/BaseSecurityTokenNfcActivity.java | 19 +- .../src/main/res/xml/usb_device_filter.xml | 25 +- 8 files changed, 108 insertions(+), 933 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/BaseJavacardDevice.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/JavacardDevice.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index f485e964c..c6795d9f7 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -877,6 +877,24 @@ android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_import_keys" /> + + + + + + + + + pwStatusBytes[MAX_PW1_LENGTH_INDEX]) { - throw new IOException("Invalid PIN length"); - } - } else if (pw == 0x83) { - if (newPin.length < 8 || newPin.length > pwStatusBytes[MAX_PW3_LENGTH_INDEX]) { - throw new IOException("Invalid PIN length"); - } - } else { - throw new IOException("Invalid PW index for modify PIN operation"); - } - - byte[] pin; - if (pw == 0x83) { - pin = mAdminPin.toStringUnsafe().getBytes(); - } else { - pin = mPin.toStringUnsafe().getBytes(); - } - - // Command APDU for CHANGE REFERENCE DATA command (page 32) - String changeReferenceDataApdu = "00" // CLA - + "24" // INS - + "00" // P1 - + String.format("%02x", pw) // P2 - + String.format("%02x", pin.length + newPin.length) // Lc - + getHex(pin) - + getHex(newPin); - String response = nfcCommunicate(changeReferenceDataApdu); // change PIN - if (!response.equals("9000")) { - throw new CardException("Failed to change PIN", parseCardStatus(response)); - } - } - - /** - * Call DECIPHER command - * - * @param encryptedSessionKey the encoded session key - * @return the decoded session key - */ - // METHOD UPDATED [OK] - public byte[] decryptSessionKey(byte[] encryptedSessionKey) throws IOException { - if (!mPw1ValidatedForDecrypt) { - nfcVerifyPin(0x82); // (Verify PW1 with mode 82 for decryption) - } - - String firstApdu = "102a8086fe"; - String secondApdu = "002a808603"; - String le = "00"; - - byte[] one = new byte[254]; - // leave out first byte: - System.arraycopy(encryptedSessionKey, 1, one, 0, one.length); - - byte[] two = new byte[encryptedSessionKey.length - 1 - one.length]; - for (int i = 0; i < two.length; i++) { - two[i] = encryptedSessionKey[i + one.length + 1]; - } - - nfcCommunicate(firstApdu + getHex(one)); - String second = nfcCommunicate(secondApdu + getHex(two) + le); - - String decryptedSessionKey = getDataField(second); - - return Hex.decode(decryptedSessionKey); - } - - /** - * Verifies the user's PW1 or PW3 with the appropriate mode. - * - * @param mode For PW1, this is 0x81 for signing, 0x82 for everything else. - * For PW3 (Admin PIN), mode is 0x83. - */ - // METHOD UPDATED [OK] - public void nfcVerifyPin(int mode) throws IOException { - if (mPin != null || mode == 0x83) { - - byte[] pin; - if (mode == 0x83) { - pin = mAdminPin.toStringUnsafe().getBytes(); - } else { - pin = mPin.toStringUnsafe().getBytes(); - } - - // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time. - // See specification, page 51 - String accepted = "9000"; - String response = nfcTryPin(mode, pin); // login - if (!response.equals(accepted)) { - throw new CardException("Bad PIN!", parseCardStatus(response)); - } - - if (mode == 0x81) { - mPw1ValidatedForSignature = true; - } else if (mode == 0x82) { - mPw1ValidatedForDecrypt = true; - } else if (mode == 0x83) { - mPw3Validated = true; - } - } - } - - /** - * Stores a data object on the token. Automatically validates the proper PIN for the operation. - * Supported for all data objects < 255 bytes in length. Only the cardholder certificate - * (0x7F21) can exceed this length. - * - * @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"); - } - if (dataObject == 0x0101 || dataObject == 0x0103) { - if (!mPw1ValidatedForDecrypt) { - nfcVerifyPin(0x82); // (Verify PW1 for non-signing operations) - } - } else if (!mPw3Validated) { - nfcVerifyPin(0x83); // (Verify PW3) - } - - String putDataApdu = "00" // CLA - + "DA" // INS - + String.format("%02x", (dataObject & 0xFF00) >> 8) // P1 - + String.format("%02x", dataObject & 0xFF) // P2 - + String.format("%02x", data.length) // Lc - + getHex(data); - - String response = nfcCommunicate(putDataApdu); // put data - if (!response.equals("9000")) { - throw new CardException("Failed to put data.", parseCardStatus(response)); - } - } - - - /** - * Puts a key on the token in the given slot. - * - * @param slot The slot on the token where the key should be stored: - * 0xB6: Signature Key - * 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) { - throw new IOException("Invalid key slot"); - } - - RSAPrivateCrtKey crtSecretKey; - try { - secretKey.unlock(passphrase); - crtSecretKey = secretKey.getCrtSecretKey(); - } catch (PgpGeneralException e) { - throw new IOException(e.getMessage()); - } - - // Shouldn't happen; the UI should block the user from getting an incompatible key this far. - if (crtSecretKey.getModulus().bitLength() > 2048) { - throw new IOException("Key too large to export to Security Token."); - } - - // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537. - if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) { - throw new IOException("Invalid public exponent for smart Security Token."); - } - - if (!mPw3Validated) { - nfcVerifyPin(0x83); // (Verify PW3 with mode 83) - } - - byte[] header = Hex.decode( - "4D82" + "03A2" // Extended header list 4D82, length of 930 bytes. (page 23) - + String.format("%02x", slot) + "00" // CRT to indicate targeted key, no length - + "7F48" + "15" // Private key template 0x7F48, length 21 (decimal, 0x15 hex) - + "9103" // Public modulus, length 3 - + "928180" // Prime P, length 128 - + "938180" // Prime Q, length 128 - + "948180" // Coefficient (1/q mod p), length 128 - + "958180" // Prime exponent P (d mod (p - 1)), length 128 - + "968180" // Prime exponent Q (d mod (1 - 1)), length 128 - + "97820100" // Modulus, length 256, last item in private key template - + "5F48" + "820383");// DO 5F48; 899 bytes of concatenated key data will follow - byte[] dataToSend = new byte[934]; - byte[] currentKeyObject; - int offset = 0; - - System.arraycopy(header, 0, dataToSend, offset, header.length); - offset += header.length; - currentKeyObject = crtSecretKey.getPublicExponent().toByteArray(); - System.arraycopy(currentKeyObject, 0, dataToSend, offset, 3); - offset += 3; - // NOTE: For a 2048-bit key, these lengths are fixed. However, bigint includes a leading 0 - // in the array to represent sign, so we take care to set the offset to 1 if necessary. - currentKeyObject = crtSecretKey.getPrimeP().toByteArray(); - System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128); - Arrays.fill(currentKeyObject, (byte) 0); - offset += 128; - currentKeyObject = crtSecretKey.getPrimeQ().toByteArray(); - System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128); - Arrays.fill(currentKeyObject, (byte) 0); - offset += 128; - currentKeyObject = crtSecretKey.getCrtCoefficient().toByteArray(); - System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128); - Arrays.fill(currentKeyObject, (byte) 0); - offset += 128; - currentKeyObject = crtSecretKey.getPrimeExponentP().toByteArray(); - System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128); - Arrays.fill(currentKeyObject, (byte) 0); - offset += 128; - currentKeyObject = crtSecretKey.getPrimeExponentQ().toByteArray(); - System.arraycopy(currentKeyObject, currentKeyObject.length - 128, dataToSend, offset, 128); - Arrays.fill(currentKeyObject, (byte) 0); - offset += 128; - currentKeyObject = crtSecretKey.getModulus().toByteArray(); - System.arraycopy(currentKeyObject, currentKeyObject.length - 256, dataToSend, offset, 256); - - String putKeyCommand = "10DB3FFF"; - String lastPutKeyCommand = "00DB3FFF"; - - // Now we're ready to communicate with the token. - offset = 0; - String response; - while (offset < dataToSend.length) { - int dataRemaining = dataToSend.length - offset; - if (dataRemaining > 254) { - response = nfcCommunicate( - putKeyCommand + "FE" + Hex.toHexString(dataToSend, offset, 254) - ); - offset += 254; - } else { - int length = dataToSend.length - offset; - response = nfcCommunicate( - lastPutKeyCommand + String.format("%02x", length) - + Hex.toHexString(dataToSend, offset, length)); - offset += length; - } - - if (!response.endsWith("9000")) { - throw new CardException("Key export to Security Token failed", parseCardStatus(response)); - } - } - - // Clear array with secret data before we return. - Arrays.fill(dataToSend, (byte) 0); - } - - /** - * Return fingerprints of all keys from application specific data stored - * on tag, or null if data not available. - * - * @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)); - - Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); - Log.d(Constants.TAG, "nfcGetFingerprints() Iso7816TLV tlv data:\n" + tlv.prettyPrint()); - - Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); - if (fptlv == null) { - return null; - } - return fptlv.mV; - } - - /** - * Return the PW Status Bytes from the token. This is a simple DO; no TLV decoding needed. - * - * @return Seven bytes in fixed format, plus 0x9000 status word at the end. - */ - // METHOD UPDATED [OK] - public byte[] nfcGetPwStatusBytes() throws IOException { - String data = "00CA00C400"; - return mTransport.sendAndReceive(Hex.decode(data)); - } - - // METHOD UPDATED [OK] - public byte[] getAid() throws IOException { - String info = "00CA004F00"; - return mTransport.sendAndReceive(Hex.decode(info)); - } - - // METHOD UPDATED [OK] - public String getUserId() throws IOException { - String info = "00CA006500"; - return nfcGetHolderName(nfcCommunicate(info)); - } - - /** - * Call COMPUTE DIGITAL SIGNATURE command and returns the MPI value - * - * @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) { - nfcVerifyPin(0x81); // (Verify PW1 with mode 81 for signing) - } - - // dsi, including Lc - String dsi; - - Log.i(Constants.TAG, "Hash: " + hashAlgo); - switch (hashAlgo) { - case HashAlgorithmTags.SHA1: - if (hash.length != 20) { - throw new IOException("Bad hash length (" + hash.length + ", expected 10!"); - } - dsi = "23" // Lc - + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes - + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes - + "0605" + "2B0E03021A" // OID of SHA1 - + "0500" // TLV coding of ZERO - + "0414" + getHex(hash); // 0x14 are 20 hash bytes - break; - case HashAlgorithmTags.RIPEMD160: - if (hash.length != 20) { - throw new IOException("Bad hash length (" + hash.length + ", expected 20!"); - } - dsi = "233021300906052B2403020105000414" + getHex(hash); - break; - case HashAlgorithmTags.SHA224: - if (hash.length != 28) { - throw new IOException("Bad hash length (" + hash.length + ", expected 28!"); - } - dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash); - break; - case HashAlgorithmTags.SHA256: - if (hash.length != 32) { - throw new IOException("Bad hash length (" + hash.length + ", expected 32!"); - } - dsi = "333031300D060960864801650304020105000420" + getHex(hash); - break; - case HashAlgorithmTags.SHA384: - if (hash.length != 48) { - throw new IOException("Bad hash length (" + hash.length + ", expected 48!"); - } - dsi = "433041300D060960864801650304020205000430" + getHex(hash); - break; - case HashAlgorithmTags.SHA512: - if (hash.length != 64) { - throw new IOException("Bad hash length (" + hash.length + ", expected 64!"); - } - dsi = "533051300D060960864801650304020305000440" + getHex(hash); - break; - default: - throw new IOException("Not supported hash algo!"); - } - - // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37) - String apdu = - "002A9E9A" // CLA, INS, P1, P2 - + dsi // digital signature input - + "00"; // Le - - String response = nfcCommunicate(apdu); - - if (response.length() < 4) { - throw new CardException("Bad response", (short) 0); - } - // split up response into signature and status - String status = response.substring(response.length() - 4); - String signature = response.substring(0, response.length() - 4); - - // while we are getting 0x61 status codes, retrieve more data - while (status.substring(0, 2).equals("61")) { - Log.d(Constants.TAG, "requesting more data, status " + status); - // Send GET RESPONSE command - response = nfcCommunicate("00C00000" + status.substring(2)); - status = response.substring(response.length() - 4); - signature += response.substring(0, response.length() - 4); - } - - Log.d(Constants.TAG, "final response:" + status); - - if (!mPw1ValidForMultipleSignatures) { - mPw1ValidatedForSignature = false; - } - - if (!"9000".equals(status)) { - throw new CardException("Bad NFC response code: " + status, parseCardStatus(response)); - } - - // Make sure the signature we received is actually the expected number of bytes long! - if (signature.length() != 256 && signature.length() != 512) { - throw new IOException("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); - } - - return Hex.decode(signature); - } - - public String nfcGetHolderName(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); - } - - private String nfcGetDataField(String output) { - return output.substring(0, output.length() - 4); - } - - /** - * Transceive data via NFC encoded as Hex - */ - // METHOD UPDATED [OK] - public String nfcCommunicate(String apdu) throws IOException, TransportIoException { - return getHex(mTransport.sendAndReceive(Hex.decode(apdu))); - } - - public boolean isConnected() { - return mTransport.isConnected(); - } - - // 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)); - // Compare the status returned by our select with the OK status code - return Apdu.hasStatus(mSelectResponse, Apdu.OK_APDU); - } catch (IOException e) { - Log.e(Constants.TAG, "Card communication failed!", e); - } - } - return false; - } - - /** - * Generates a key on the card in the given slot. If the slot is 0xB6 (the signature key), - * this command also has the effect of resetting the digital signature counter. - * NOTE: This does not set the key fingerprint data object! After calling this command, you - * must construct a public key packet using the returned public key data objects, compute the - * key fingerprint, and store it on the card using: putData(0xC8, key.getFingerprint()) - * - * @param slot The slot on the card where the key should be generated: - * 0xB6: Signature Key - * 0xB8: Decipherment Key - * 0xA4: Authentication Key - * @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[] nfcGenerateKey(int slot) throws IOException { - if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) { - throw new IOException("Invalid key slot"); - } - - if (!mPw3Validated) { - nfcVerifyPin(0x83); // (Verify PW3 with mode 83) - } - - String generateKeyApdu = "0047800002" + String.format("%02x", slot) + "0000"; - String getResponseApdu = "00C00000"; - - String first = nfcCommunicate(generateKeyApdu); - String second = nfcCommunicate(getResponseApdu); - - if (!second.endsWith("9000")) { - throw new IOException("On-card key generation failed"); - } - - String publicKeyData = getDataField(first) + getDataField(second); - - Log.d(Constants.TAG, "Public Key Data Objects: " + publicKeyData); - - return Hex.decode(publicKeyData); - } - - // NEW METHOD [OK][OK] - private String getDataField(String output) { - return output.substring(0, output.length() - 4); - } - - // NEW METHOD [OK] - private String nfcTryPin(int mode, byte[] pin) throws IOException { - // Command APDU for VERIFY command (page 32) - String login = - "00" // CLA - + "20" // INS - + "00" // P1 - + String.format("%02x", mode) // P2 - + String.format("%02x", pin.length) // Lc - + Hex.toHexString(pin); - - return nfcCommunicate(login); - } - - /** - * Resets security token, which deletes all keys and data objects. - * 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"; - - // try wrong PIN 4 times until counter goes to C0 - byte[] pin = "XXXXXX".getBytes(); - for (int i = 0; i <= 4; i++) { - String response = nfcTryPin(0x81, pin); - if (response.equals(accepted)) { // Should NOT accept! - throw new CardException("Should never happen, XXXXXX has been accepted!", parseCardStatus(response)); - } - } - - // try wrong Admin PIN 4 times until counter goes to C0 - byte[] adminPin = "XXXXXXXX".getBytes(); - for (int i = 0; i <= 4; i++) { - String response = nfcTryPin(0x83, adminPin); - if (response.equals(accepted)) { // Should NOT accept! - throw new CardException("Should never happen, XXXXXXXX has been accepted", parseCardStatus(response)); - } - } - - // reactivate token! - String reactivate1 = "00" + "e6" + "00" + "00"; - String reactivate2 = "00" + "44" + "00" + "00"; - String response1 = nfcCommunicate(reactivate1); - String response2 = nfcCommunicate(reactivate2); - if (!response1.equals(accepted) || !response2.equals(accepted)) { - throw new CardException("Reactivating failed!", parseCardStatus(response1)); - } - - } - - /** - * Return the fingerprint 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 fingerprint of the requested key, or null if not found. - */ - public byte[] getMasterKeyFingerprint(int idx) throws IOException { - byte[] data = getFingerprints(); - if (data == null) { - return null; - } - - // return the master key fingerprint - ByteBuffer fpbuf = ByteBuffer.wrap(data); - byte[] fp = new byte[20]; - fpbuf.position(idx * 20); - fpbuf.get(fp, 0, 20); - - return fp; - } - - @Override - public void setTransport(Transport mTransport) { - this.mTransport = mTransport; - - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/JavacardDevice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/JavacardDevice.java deleted file mode 100644 index 63d4d2ad7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/javacard/JavacardDevice.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.sufficientlysecure.keychain.javacard; - -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; -import org.sufficientlysecure.keychain.util.Passphrase; - -import java.io.IOException; - -public interface JavacardDevice { - - Passphrase getPin(); - - void setPin(final Passphrase pin); - - Passphrase getAdminPin(); - - void setAdminPin(final Passphrase adminPin); - - void changeKey(CanonicalizedSecretKey secretKey, Passphrase passphrase) throws IOException; - - boolean containsKey(KeyType keyType) throws IOException; - - boolean keyMatchesFingerPrint(KeyType keyType, byte[] fingerprint) throws IOException; - - void connectToDevice() throws IOException; - - /** - * Modifies the user's PW1 or PW3. Before sending, the new PIN will be validated for - * conformance to the card's requirements for key length. - * - * @param pinType For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83. - * @param newPin The new PW1 or PW3. - */ - void modifyPin(int pinType, byte[] newPin) throws IOException; - - /** - * Calls to calculate the signature and returns the MPI value - * - * @param encryptedSessionKey the encoded session key - * @return the decoded session key - */ - byte[] decryptSessionKey(byte[] encryptedSessionKey) throws IOException; - - /** - * Return fingerprints of all keys from application specific data stored - * on tag, or null if data not available. - * - * @return The fingerprints of all subkeys in a contiguous byte array. - */ - byte[] getFingerprints() throws IOException; - - - byte[] getAid() throws IOException; - - String getUserId() throws IOException; - - boolean isConnected(); - - /** - * Calls to calculate the signature and returns the MPI value - * - * @param hash the hash for signing - * @return a big integer representing the MPI for the given hash - */ - byte[] calculateSignature(byte[] hash, int hashAlgo) throws IOException; - - boolean isFidesmoToken(); - - /** - * Return the fingerprint 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 fingerprint of the requested key, or null if not found. - */ - byte[] getMasterKeyFingerprint(int idx) throws IOException; - - /** - * Resets security token, which deletes all keys and data objects. - * This works by entering a wrong PIN and then Admin PIN 4 times respectively. - * Afterwards, the token is reactivated. - */ - void resetAndWipeToken() throws IOException; - - /** - * Puts a key on the token in the given slot. - * - * @param slot The slot on the token where the key should be stored: - * 0xB6: Signature Key - * 0xB8: Decipherment Key - * 0xA4: Authentication Key - */ - void putKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase) throws IOException; - - /** - * Stores a data object on the token. Automatically validates the proper PIN for the operation. - * Supported for all data objects < 255 bytes in length. Only the cardholder certificate - * (0x7F21) can exceed this length. - * - * @param dataObject The data object to be stored. - * @param data The data to store in the object - */ - void putData(int dataObject, byte[] data) throws IOException; - - void setTransport(Transport mTransport); -} 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 cffc49555..b86c3cf4c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/SmartcardDevice.java @@ -78,7 +78,7 @@ public class SmartcardDevice { } public boolean containsKey(KeyType keyType) throws IOException { - return keyMatchesFingerPrint(keyType, BLANK_FINGERPRINT); + return !keyMatchesFingerPrint(keyType, BLANK_FINGERPRINT); } public boolean keyMatchesFingerPrint(KeyType keyType, byte[] fingerprint) throws IOException { 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 c98d5d43f..8a6971fe6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/smartcard/UsbConnectionManager.java @@ -1,7 +1,6 @@ package org.sufficientlysecure.keychain.smartcard; import android.app.Activity; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -10,79 +9,29 @@ import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.ui.UsbEventReceiverActivity; 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; -import java.util.concurrent.atomic.AtomicBoolean; - 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 final Semaphore mRunning = new Semaphore(1); - private final Set mProcessedDevices = Collections.newSetFromMap(new ConcurrentHashMap()); - private final AtomicBoolean mStopped = new AtomicBoolean(false); private Activity mActivity; - private final Thread mWatchThread = new Thread() { - @Override - public void run() { - final UsbManager usbManager = (UsbManager) mActivity.getSystemService(Context.USB_SERVICE); - - while (!mStopped.get()) { - try { - mRunning.acquire(); - } catch (InterruptedException e) { - } - mRunning.release(); - if (mStopped.get()) return; - - // - 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); - Log.d(LOG_TAG, "Requesting permission for " + device.getDeviceName()); - usbManager.requestPermission(device, PendingIntent.getBroadcast(mActivity, 0, intent, 0)); - } - - try { - sleep(1000); - } catch (InterruptedException ignored) { - } - } - } - }; private OnDiscoveredUsbDeviceListener mListener; /** - * Receives broadcast when a supported USB device is attached, detached or - * when a permission to communicate to the device has been granted. + * Receives broadcast when a supported USB device get permission. */ 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)) { + if (UsbEventReceiverActivity.ACTION_USB_PERMISSION.equals(action)) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); - Log.d(LOG_TAG, "ACTION_USB_PERMISSION: " + permission + " Device: " + deviceName); - if (permission) { - interceptIntent(intent); + Log.d(Constants.TAG, "Got permission for " + usbDevice.getDeviceName()); + mListener.usbDeviceDiscovered(usbDevice); } - - context.unregisterReceiver(mUsbReceiver); } } }; @@ -90,59 +39,16 @@ public class UsbConnectionManager { public UsbConnectionManager(final Activity activity, final OnDiscoveredUsbDeviceListener listener) { this.mActivity = activity; this.mListener = listener; - mRunning.acquireUninterruptibly(); - mWatchThread.start(); - } - - private static UsbDevice getDevice(UsbManager manager) { - HashMap deviceList = manager.getDeviceList(); - for (UsbDevice device : deviceList.values()) { - if (device.getVendorId() == 0x1050 && (device.getProductId() == 0x0112 || device.getProductId() == 0x0115)) { - return device; - } - } - return null; } - public void startListeningForDevices() { - mRunning.release(); - } - - public void stopListeningForDevices() { - mRunning.acquireUninterruptibly(); - } + public void onStart() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(UsbEventReceiverActivity.ACTION_USB_PERMISSION); - 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; - } + mActivity.registerReceiver(mUsbReceiver, intentFilter); } - public void onDestroy() { - mStopped.set(true); - mRunning.release(); - try { - mActivity.unregisterReceiver(mUsbReceiver); - } catch (IllegalArgumentException ignore) { - } - mActivity = null; + public void onStop() { + mActivity.unregisterReceiver(mUsbReceiver); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java new file mode 100644 index 000000000..9df5800b5 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UsbEventReceiverActivity.java @@ -0,0 +1,42 @@ +package org.sufficientlysecure.keychain.ui; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.Bundle; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + +public class UsbEventReceiverActivity extends Activity { + public static final String ACTION_USB_PERMISSION = + "org.sufficientlysecure.keychain.ui.USB_PERMISSION"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onResume() { + super.onResume(); + final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); + + Intent intent = getIntent(); + if (intent != null) { + if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + + Log.d(Constants.TAG, "Requesting permission for " + usbDevice.getDeviceName()); + usbManager.requestPermission(usbDevice, + PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0)); + } + } + + // Close the activity + finish(); + } +} \ No newline at end of file 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 8dde54a1f..a6f8c0b0f 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 @@ -254,9 +254,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity */ @Override public void onNewIntent(final Intent intent) { - if (!mTagDispatcher.interceptIntent(intent)) { - mUsbDispatcher.interceptIntent(intent); - } + mTagDispatcher.interceptIntent(intent); } private void handleNfcError(IOException e) { @@ -374,7 +372,7 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity Log.d(Constants.TAG, "BaseNfcActivity.onPause"); mTagDispatcher.disableExclusiveNfc(); - mUsbDispatcher.stopListeningForDevices(); +// mUsbDispatcher.stopListeningForDevices(); } /** @@ -385,7 +383,6 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity super.onResume(); Log.d(Constants.TAG, "BaseNfcActivity.onResume"); mTagDispatcher.enableExclusiveNfc(); - mUsbDispatcher.startListeningForDevices(); } protected void obtainSecurityTokenPin(RequiredInputParcel requiredInput) { @@ -568,8 +565,14 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity } @Override - protected void onDestroy() { - super.onDestroy(); - mUsbDispatcher.onDestroy(); + protected void onStop() { + super.onStop(); + mUsbDispatcher.onStop(); + } + + @Override + protected void onStart() { + super.onStart(); + mUsbDispatcher.onStart(); } } diff --git a/OpenKeychain/src/main/res/xml/usb_device_filter.xml b/OpenKeychain/src/main/res/xml/usb_device_filter.xml index cc0599009..789e4ffb7 100644 --- a/OpenKeychain/src/main/res/xml/usb_device_filter.xml +++ b/OpenKeychain/src/main/res/xml/usb_device_filter.xml @@ -1,4 +1,27 @@ + + - + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3